diff --git a/distrib/sets/lists/minix/mi b/distrib/sets/lists/minix/mi index 05771c69c..63d78d958 100644 --- a/distrib/sets/lists/minix/mi +++ b/distrib/sets/lists/minix/mi @@ -181,6 +181,9 @@ ./sbin/mkfs.mfs minix-sys ./sbin/mknod minix-sys ./sbin/newfs_ext2fs minix-sys +./sbin/newfs_msdos minix-sys +./sbin/newfs_udf minix-sys +./sbin/newfs_v7fs minix-sys ./sbin/nologin minix-sys ./sbin/ping minix-sys ./sbin/poweroff minix-sys @@ -906,6 +909,12 @@ ./usr/include/fs minix-sys ./usr/include/fs/puffs minix-sys ./usr/include/fs/puffs/puffs_msgif.h minix-sys +./usr/include/fs/udf minix-sys +./usr/include/fs/udf/ecma167-udf.h minix-sys +./usr/include/fs/udf/udf_mount.h minix-sys +./usr/include/fs/v7fs minix-sys +./usr/include/fs/v7fs/v7fs_args.h minix-sys +./usr/include/fs/v7fs/v7fs.h minix-sys ./usr/include/fstab.h minix-sys ./usr/include/fts.h minix-sys ./usr/include/ftw.h minix-sys @@ -5251,11 +5260,15 @@ ./usr/man/man8/link.8 minix-sys ./usr/man/man8/loadramdisk.8 minix-sys ./usr/man/man8/MAKEDEV.8 minix-sys +./usr/man/man8/makefs.8 minix-sys ./usr/man/man8/makewhatis.8 minix-sys ./usr/man/man8/mknod.8 minix-sys ./usr/man/man8/mtree.8 minix-sys ./usr/man/man8/netconf.8 minix-sys ./usr/man/man8/newfs_ext2fs.8 minix-sys +./usr/man/man8/newfs_msdos.8 minix-sys +./usr/man/man8/newfs_udf.8 minix-sys +./usr/man/man8/newfs_v7fs.8 minix-sys ./usr/man/man8/newroot.8 minix-sys obsolete ./usr/man/man8/nologin.8 minix-sys ./usr/man/man8/nonamed.8 minix-sys @@ -5340,6 +5353,7 @@ ./usr/sbin/installboot_nbsd minix-sys ./usr/sbin/kernel minix-sys ./usr/sbin/link minix-sys +./usr/sbin/makefs minix-sys ./usr/sbin/mkfs.mfsv3 minix-sys ./usr/sbin/mkproto minix-sys ./usr/sbin/mtree minix-sys diff --git a/sbin/Makefile b/sbin/Makefile index ed2ae4306..9af12675d 100644 --- a/sbin/Makefile +++ b/sbin/Makefile @@ -15,7 +15,7 @@ SUBDIR= \ shutdown \ # support for various file systems -SUBDIR+= newfs_ext2fs fsck_ext2fs +SUBDIR+= newfs_ext2fs fsck_ext2fs newfs_msdos newfs_udf newfs_v7fs .if !defined(__MINIX) SUBDIR+= newfs fsck_ffs fsdb dump restore clri tunefs resize_ffs SUBDIR+= newfs_lfs fsck_lfs dump_lfs resize_lfs diff --git a/sbin/mount/fattr.c b/sbin/mount/fattr.c new file mode 100644 index 000000000..aabb5605e --- /dev/null +++ b/sbin/mount/fattr.c @@ -0,0 +1,89 @@ +/* $NetBSD: fattr.c,v 1.10 2009/06/19 12:55:45 stacktic Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#ifndef lint +__RCSID("$NetBSD: fattr.c,v 1.10 2009/06/19 12:55:45 stacktic Exp $"); +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mountprog.h" + +int +a_num(const char *s, const char *id_type) +{ + int id; + char *ep; + + id = strtol(s, &ep, 0); + if (*ep || s == ep || id < 0) + errx(1, "unknown %s id: %s", id_type, s); + return id; +} + +gid_t +a_gid(const char *s) +{ + struct group *gr; + + if ((gr = getgrnam(s)) != NULL) + return gr->gr_gid; + return a_num(s, "group"); +} + +uid_t +a_uid(const char *s) +{ + struct passwd *pw; + + if ((pw = getpwnam(s)) != NULL) + return pw->pw_uid; + return a_num(s, "user"); +} + +mode_t +a_mask(const char *s) +{ + int rv; + char *ep; + + rv = strtol(s, &ep, 8); + if (s == ep || *ep || rv < 0) + errx(1, "invalid file mode: %s", s); + return rv; +} diff --git a/sbin/mount/mountprog.h b/sbin/mount/mountprog.h new file mode 100644 index 000000000..2c7327430 --- /dev/null +++ b/sbin/mount/mountprog.h @@ -0,0 +1,37 @@ +/* $NetBSD: mountprog.h,v 1.1 2008/08/05 20:57:45 pooka Exp $ */ + +/*- + * Copyright (c) 2000, 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +int a_num(const char *, const char *); +gid_t a_gid(const char *); +uid_t a_uid(const char *); +mode_t a_mask(const char *); + +int checkvfsname(const char *, const char **); +const char ** makevfslist(const char *); + +void pathadj(const char *, char *); diff --git a/sbin/newfs_msdos/Makefile b/sbin/newfs_msdos/Makefile new file mode 100644 index 000000000..1a6dfbff7 --- /dev/null +++ b/sbin/newfs_msdos/Makefile @@ -0,0 +1,25 @@ +# $NetBSD: Makefile,v 1.6 2013/01/21 20:28:38 christos Exp $ +# From: $FreeBSD: src/sbin/newfs_msdos/Makefile,v 1.5 2001/03/26 14:33:18 ru Exp $ + +.include + +PROG= newfs_msdos +MAN= newfs_msdos.8 +SRCS= newfs_msdos.c partutil.c mkfs_msdos.c + +LDADD+= -lutil +DPADD+= ${LIBUTIL} + +LDADD+=-lprop +DPADD+=${LIBPROP} + +FSCK=${NETBSDSRCDIR}/sbin/fsck +CPPFLAGS+=-I${.CURDIR} -I${FSCK} +.PATH: ${FSCK} + + +.if ${MACHINE} == "pc98" +CFLAGS+= -DPC98 +.endif + +.include diff --git a/sbin/newfs_msdos/mkfs_msdos.c b/sbin/newfs_msdos/mkfs_msdos.c new file mode 100644 index 000000000..dc7dde316 --- /dev/null +++ b/sbin/newfs_msdos/mkfs_msdos.c @@ -0,0 +1,976 @@ +/* $NetBSD: mkfs_msdos.c,v 1.8 2013/10/19 01:09:59 christos Exp $ */ + +/* + * Copyright (c) 1998 Robert Nordier + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#ifndef lint +#if 0 +static const char rcsid[] = + "$FreeBSD: src/sbin/newfs_msdos/newfs_msdos.c,v 1.15 2000/10/10 01:49:37 wollman Exp $"; +#else +__RCSID("$NetBSD: mkfs_msdos.c,v 1.8 2013/10/19 01:09:59 christos Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#ifndef MAKEFS +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifndef MAKEFS +#include "partutil.h" +#endif +#include "mkfs_msdos.h" + +#define MAXU16 0xffff /* maximum unsigned 16-bit quantity */ +#define BPN 4 /* bits per nibble */ +#define NPB 2 /* nibbles per byte */ + +#define DOSMAGIC 0xaa55 /* DOS magic number */ +#define MINBPS 512 /* minimum bytes per sector */ +#define MAXSPC 128 /* maximum sectors per cluster */ +#define MAXNFT 16 /* maximum number of FATs */ +#define DEFBLK 4096 /* default block size */ +#define DEFBLK16 2048 /* default block size FAT16 */ +#define DEFRDE 512 /* default root directory entries */ +#define RESFTE 2 /* reserved FAT entries */ +#define MINCLS12 1 /* minimum FAT12 clusters */ +#define MINCLS16 0xff5 /* minimum FAT16 clusters */ +#define MINCLS32 0xfff5 /* minimum FAT32 clusters */ +#define MAXCLS12 0xff4 /* maximum FAT12 clusters */ +#define MAXCLS16 0xfff4 /* maximum FAT16 clusters */ +#define MAXCLS32 0xffffff4 /* maximum FAT32 clusters */ + +#define mincls(fat_type) ((fat_type) == 12 ? MINCLS12 : \ + (fat_type) == 16 ? MINCLS16 : \ + MINCLS32) + +#define maxcls(fat_type) ((fat_type) == 12 ? MAXCLS12 : \ + (fat_type) == 16 ? MAXCLS16 : \ + MAXCLS32) + +#define mk1(p, x) \ + (p) = (u_int8_t)(x) + +#define mk2(p, x) \ + (p)[0] = (u_int8_t)(x), \ + (p)[1] = (u_int8_t)((x) >> 010) + +#define mk4(p, x) \ + (p)[0] = (u_int8_t)(x), \ + (p)[1] = (u_int8_t)((x) >> 010), \ + (p)[2] = (u_int8_t)((x) >> 020), \ + (p)[3] = (u_int8_t)((x) >> 030) + +struct bs { + u_int8_t jmp[3]; /* bootstrap entry point */ + u_int8_t oem[8]; /* OEM name and version */ +}; + +struct bsbpb { + u_int8_t bps[2]; /* bytes per sector */ + u_int8_t spc; /* sectors per cluster */ + u_int8_t res[2]; /* reserved sectors */ + u_int8_t nft; /* number of FATs */ + u_int8_t rde[2]; /* root directory entries */ + u_int8_t sec[2]; /* total sectors */ + u_int8_t mid; /* media descriptor */ + u_int8_t spf[2]; /* sectors per FAT */ + u_int8_t spt[2]; /* sectors per track */ + u_int8_t hds[2]; /* drive heads */ + u_int8_t hid[4]; /* hidden sectors */ + u_int8_t bsec[4]; /* big total sectors */ +}; + +struct bsxbpb { + u_int8_t bspf[4]; /* big sectors per FAT */ + u_int8_t xflg[2]; /* FAT control flags */ + u_int8_t vers[2]; /* file system version */ + u_int8_t rdcl[4]; /* root directory start cluster */ + u_int8_t infs[2]; /* file system info sector */ + u_int8_t bkbs[2]; /* backup boot sector */ + u_int8_t rsvd[12]; /* reserved */ +}; + +struct bsx { + u_int8_t drv; /* drive number */ + u_int8_t rsvd; /* reserved */ + u_int8_t sig; /* extended boot signature */ + u_int8_t volid[4]; /* volume ID number */ + u_int8_t label[11]; /* volume label */ + u_int8_t type[8]; /* file system type */ +}; + +struct de { + u_int8_t namext[11]; /* name and extension */ + u_int8_t attr; /* attributes */ + u_int8_t rsvd[10]; /* reserved */ + u_int8_t time[2]; /* creation time */ + u_int8_t date[2]; /* creation date */ + u_int8_t clus[2]; /* starting cluster */ + u_int8_t size[4]; /* size */ +}; + +struct bpb { + u_int bps; /* bytes per sector */ + u_int spc; /* sectors per cluster */ + u_int res; /* reserved sectors */ + u_int nft; /* number of FATs */ + u_int rde; /* root directory entries */ + u_int sec; /* total sectors */ + u_int mid; /* media descriptor */ + u_int spf; /* sectors per FAT */ + u_int spt; /* sectors per track */ + u_int hds; /* drive heads */ + u_int hid; /* hidden sectors */ + u_int bsec; /* big total sectors */ + u_int bspf; /* big sectors per FAT */ + u_int rdcl; /* root directory start cluster */ + u_int infs; /* file system info sector */ + u_int bkbs; /* backup boot sector */ +}; + +#define INIT(a, b, c, d, e, f, g, h, i, j) \ + { .bps = a, .spc = b, .res = c, .nft = d, .rde = e, \ + .sec = f, .mid = g, .spf = h, .spt = i, .hds = j, } +static struct { + const char *name; + struct bpb bpb; +} stdfmt[] = { + {"160", INIT(512, 1, 1, 2, 64, 320, 0xfe, 1, 8, 1)}, + {"180", INIT(512, 1, 1, 2, 64, 360, 0xfc, 2, 9, 1)}, + {"320", INIT(512, 2, 1, 2, 112, 640, 0xff, 1, 8, 2)}, + {"360", INIT(512, 2, 1, 2, 112, 720, 0xfd, 2, 9, 2)}, + {"640", INIT(512, 2, 1, 2, 112, 1280, 0xfb, 2, 8, 2)}, + {"720", INIT(512, 2, 1, 2, 112, 1440, 0xf9, 3, 9, 2)}, + {"1200", INIT(512, 1, 1, 2, 224, 2400, 0xf9, 7, 15, 2)}, + {"1232", INIT(1024,1, 1, 2, 192, 1232, 0xfe, 2, 8, 2)}, + {"1440", INIT(512, 1, 1, 2, 224, 2880, 0xf0, 9, 18, 2)}, + {"2880", INIT(512, 2, 1, 2, 240, 5760, 0xf0, 9, 36, 2)} +}; + +static u_int8_t bootcode[] = { + 0xfa, /* cli */ + 0x31, 0xc0, /* xor ax,ax */ + 0x8e, 0xd0, /* mov ss,ax */ + 0xbc, 0x00, 0x7c, /* mov sp,7c00h */ + 0xfb, /* sti */ + 0x8e, 0xd8, /* mov ds,ax */ + 0xe8, 0x00, 0x00, /* call $ + 3 */ + 0x5e, /* pop si */ + 0x83, 0xc6, 0x19, /* add si,+19h */ + 0xbb, 0x07, 0x00, /* mov bx,0007h */ + 0xfc, /* cld */ + 0xac, /* lodsb */ + 0x84, 0xc0, /* test al,al */ + 0x74, 0x06, /* jz $ + 8 */ + 0xb4, 0x0e, /* mov ah,0eh */ + 0xcd, 0x10, /* int 10h */ + 0xeb, 0xf5, /* jmp $ - 9 */ + 0x30, 0xe4, /* xor ah,ah */ + 0xcd, 0x16, /* int 16h */ + 0xcd, 0x19, /* int 19h */ + 0x0d, 0x0a, + 'N', 'o', 'n', '-', 's', 'y', 's', 't', + 'e', 'm', ' ', 'd', 'i', 's', 'k', + 0x0d, 0x0a, + 'P', 'r', 'e', 's', 's', ' ', 'a', 'n', + 'y', ' ', 'k', 'e', 'y', ' ', 't', 'o', + ' ', 'r', 'e', 'b', 'o', 'o', 't', + 0x0d, 0x0a, + 0 +}; + +static int got_siginfo = 0; /* received a SIGINFO */ + +#ifndef MAKEFS +static int check_mounted(const char *, mode_t); +#endif +static int getstdfmt(const char *, struct bpb *); +static int getbpbinfo(int, const char *, const char *, int, struct bpb *, int); +static void print_bpb(struct bpb *); +static int ckgeom(const char *, u_int, const char *); +static int oklabel(const char *); +static void mklabel(u_int8_t *, const char *); +static void setstr(u_int8_t *, const char *, size_t); +static void infohandler(int sig); + +int +mkfs_msdos(const char *fname, const char *dtype, const struct msdos_options *op) +{ + char buf[MAXPATHLEN]; + struct stat sb; + struct timeval tv; + struct bpb bpb; + struct tm *tm; + struct bs *bs; + struct bsbpb *bsbpb; + struct bsxbpb *bsxbpb; + struct bsx *bsx; + struct de *de; + u_int8_t *img; + const char *bname; + ssize_t n; + time_t now; + u_int bss, rds, cls, dir, lsn, x, x1, x2; + int ch, fd, fd1; + struct msdos_options o = *op; + int oflags = O_RDWR | O_CREAT; + + if (o.block_size && o.sectors_per_cluster) { + warnx("Cannot specify both block size and sectors per cluster"); + return -1; + } + if (o.OEM_string && strlen(o.OEM_string) > 8) { + warnx("%s: bad OEM string", o.OEM_string); + return -1; + } + if (o.create_size) { + if (o.no_create) { + warnx("create (-C) is incompatible with -N"); + return -1; + } + if (o.offset == 0) + oflags |= O_TRUNC; + fd = open(fname, oflags, 0644); + if (fd == -1) { + warnx("failed to create %s", fname); + return -1; + } + (void)lseek(fd, o.create_size - 1, SEEK_SET); + if (write(fd, "\0", 1) != 1) { + warn("failed to set file size"); + return -1; + } + (void)lseek(fd, 0, SEEK_SET); + } else if ((fd = open(fname, o.no_create ? O_RDONLY : O_RDWR)) == -1 || + fstat(fd, &sb)) { + warn("%s", fname); + return -1; + } +#ifndef MAKEFS + if (!o.no_create) + if (check_mounted(fname, sb.st_mode) == -1) + return -1; +#endif + if (!S_ISCHR(sb.st_mode) && !o.create_size) { + warnx("warning, %s is not a character device", fname); + return -1; + } + if (o.offset && o.offset != lseek(fd, o.offset, SEEK_SET)) { + warnx("cannot seek to %jd", (intmax_t)o.offset); + return -1; + } + memset(&bpb, 0, sizeof(bpb)); + if (o.floppy) { + if (getstdfmt(o.floppy, &bpb) == -1) + return -1; + bpb.bsec = bpb.sec; + bpb.sec = 0; + bpb.bspf = bpb.spf; + bpb.spf = 0; + } + if (o.drive_heads) + bpb.hds = o.drive_heads; + if (o.sectors_per_track) + bpb.spt = o.sectors_per_track; + if (o.bytes_per_sector) + bpb.bps = o.bytes_per_sector; + if (o.size) + bpb.bsec = o.size; + if (o.hidden_sectors_set) + bpb.hid = o.hidden_sectors; + if (!(o.floppy || (o.drive_heads && o.sectors_per_track && + o.bytes_per_sector && o.size && o.hidden_sectors_set))) { + if (getbpbinfo(fd, fname, dtype, o.hidden_sectors_set, &bpb, + o.create_size != 0) == -1) + return -1; + bpb.bsec -= (o.offset / bpb.bps); + if (bpb.spc == 0) { /* set defaults */ + if (bpb.bsec <= 6000) /* about 3MB -> 512 bytes */ + bpb.spc = 1; + else if (bpb.bsec <= (1<<17)) /* 64M -> 4k */ + bpb.spc = 8; + else if (bpb.bsec <= (1<<19)) /* 256M -> 8k */ + bpb.spc = 16; + else if (bpb.bsec <= (1<<21)) /* 1G -> 16k */ + bpb.spc = 32; + else + bpb.spc = 64; /* otherwise 32k */ + } + } + + if (o.volume_label && !oklabel(o.volume_label)) { + warnx("%s: bad volume label", o.volume_label); + return -1; + } + + switch (o.fat_type) { + case 0: + if (o.floppy) + o.fat_type = 12; + else if (!o.directory_entries && (o.info_sector || o.backup_sector)) + o.fat_type = 32; + break; + case 12: + case 16: + if (o.info_sector) { + warnx("Cannot specify info sector with FAT%u", o.fat_type); + return -1; + } + if (o.backup_sector) { + warnx("Cannot specify backup sector with FAT%u", o.fat_type); + return -1; + } + break; + case 32: + if (o.directory_entries) { + warnx("Cannot specify directory entries with FAT32"); + return -1; + } + break; + default: + warnx("%d: bad FAT type", o.fat_type); + return -1; + } + if (!powerof2(bpb.bps)) { + warnx("bytes/sector (%u) is not a power of 2", bpb.bps); + return -1; + } + if (bpb.bps < MINBPS) { + warnx("bytes/sector (%u) is too small; minimum is %u", + bpb.bps, MINBPS); + return -1; + } + + if (o.floppy && o.fat_type == 32) + bpb.rde = 0; + if (o.block_size) { + if (!powerof2(o.block_size)) { + warnx("block size (%u) is not a power of 2", o.block_size); + return -1; + } + if (o.block_size < bpb.bps) { + warnx("block size (%u) is too small; minimum is %u", + o.block_size, bpb.bps); + return -1; + } + if (o.block_size > bpb.bps * MAXSPC) { + warnx("block size (%u) is too large; maximum is %u", + o.block_size, bpb.bps * MAXSPC); + return -1; + } + bpb.spc = o.block_size / bpb.bps; + } + if (o.sectors_per_cluster) { + if (!powerof2(o.sectors_per_cluster)) { + warnx("sectors/cluster (%u) is not a power of 2", + o.sectors_per_cluster); + return -1; + } + bpb.spc = o.sectors_per_cluster; + } + if (o.reserved_sectors) + bpb.res = o.reserved_sectors; + if (o.num_FAT) { + if (o.num_FAT > MAXNFT) { + warnx("number of FATs (%u) is too large; maximum is %u", + o.num_FAT, MAXNFT); + return -1; + } + bpb.nft = o.num_FAT; + } + if (o.directory_entries) + bpb.rde = o.directory_entries; + if (o.media_descriptor_set) { + if (o.media_descriptor < 0xf0) { + warnx("illegal media descriptor (%#x)", o.media_descriptor); + return -1; + } + bpb.mid = o.media_descriptor; + } + if (o.sectors_per_fat) + bpb.bspf = o.sectors_per_fat; + if (o.info_sector) + bpb.infs = o.info_sector; + if (o.backup_sector) + bpb.bkbs = o.backup_sector; + bss = 1; + bname = NULL; + fd1 = -1; + if (o.bootstrap) { + bname = o.bootstrap; + if (!strchr(bname, '/')) { + snprintf(buf, sizeof(buf), "/boot/%s", bname); + if (!(bname = strdup(buf))) { + warn(NULL); + return -1; + } + } + if ((fd1 = open(bname, O_RDONLY)) == -1 || fstat(fd1, &sb)) { + warn("%s", bname); + return -1; + } + if (!S_ISREG(sb.st_mode) || sb.st_size % bpb.bps || + sb.st_size < bpb.bps || sb.st_size > bpb.bps * MAXU16) { + warnx("%s: inappropriate file type or format", bname); + return -1; + } + bss = sb.st_size / bpb.bps; + } + if (!bpb.nft) + bpb.nft = 2; + if (!o.fat_type) { + if (bpb.bsec < (bpb.res ? bpb.res : bss) + + howmany((RESFTE + (bpb.spc ? MINCLS16 : MAXCLS12 + 1)) * + ((bpb.spc ? 16 : 12) / BPN), bpb.bps * NPB) * + bpb.nft + + howmany(bpb.rde ? bpb.rde : DEFRDE, + bpb.bps / sizeof(struct de)) + + (bpb.spc ? MINCLS16 : MAXCLS12 + 1) * + (bpb.spc ? bpb.spc : howmany(DEFBLK, bpb.bps))) + o.fat_type = 12; + else if (bpb.rde || bpb.bsec < + (bpb.res ? bpb.res : bss) + + howmany((RESFTE + MAXCLS16) * 2, bpb.bps) * bpb.nft + + howmany(DEFRDE, bpb.bps / sizeof(struct de)) + + (MAXCLS16 + 1) * + (bpb.spc ? bpb.spc : howmany(8192, bpb.bps))) + o.fat_type = 16; + else + o.fat_type = 32; + } + x = bss; + if (o.fat_type == 32) { + if (!bpb.infs) { + if (x == MAXU16 || x == bpb.bkbs) { + warnx("no room for info sector"); + return -1; + } + bpb.infs = x; + } + if (bpb.infs != MAXU16 && x <= bpb.infs) + x = bpb.infs + 1; + if (!bpb.bkbs) { + if (x == MAXU16) { + warnx("no room for backup sector"); + return -1; + } + bpb.bkbs = x; + } else if (bpb.bkbs != MAXU16 && bpb.bkbs == bpb.infs) { + warnx("backup sector would overwrite info sector"); + return -1; + } + if (bpb.bkbs != MAXU16 && x <= bpb.bkbs) + x = bpb.bkbs + 1; + } + if (!bpb.res) + bpb.res = o.fat_type == 32 ? MAX(x, MAX(16384 / bpb.bps, 4)) : x; + else if (bpb.res < x) { + warnx("too few reserved sectors (need %d have %d)", x, bpb.res); + return -1; + } + if (o.fat_type != 32 && !bpb.rde) + bpb.rde = DEFRDE; + rds = howmany(bpb.rde, bpb.bps / sizeof(struct de)); + if (!bpb.spc) + for (bpb.spc = howmany(o.fat_type == 16 ? DEFBLK16 : DEFBLK, bpb.bps); + bpb.spc < MAXSPC && + bpb.res + + howmany((RESFTE + maxcls(o.fat_type)) * (o.fat_type / BPN), + bpb.bps * NPB) * bpb.nft + + rds + + (u_int64_t)(maxcls(o.fat_type) + 1) * bpb.spc <= bpb.bsec; + bpb.spc <<= 1); + if (o.fat_type != 32 && bpb.bspf > MAXU16) { + warnx("too many sectors/FAT for FAT12/16"); + return -1; + } + x1 = bpb.res + rds; + x = bpb.bspf ? bpb.bspf : 1; + if (x1 + (u_int64_t)x * bpb.nft > bpb.bsec) { + warnx("meta data exceeds file system size"); + return -1; + } + x1 += x * bpb.nft; + x = (u_int64_t)(bpb.bsec - x1) * bpb.bps * NPB / + (bpb.spc * bpb.bps * NPB + o.fat_type / BPN * bpb.nft); + x2 = howmany((RESFTE + MIN(x, maxcls(o.fat_type))) * (o.fat_type / BPN), + bpb.bps * NPB); + if (!bpb.bspf) { + bpb.bspf = x2; + x1 += (bpb.bspf - 1) * bpb.nft; + } + cls = (bpb.bsec - x1) / bpb.spc; + x = (u_int64_t)bpb.bspf * bpb.bps * NPB / (o.fat_type / BPN) - RESFTE; + if (cls > x) + cls = x; + if (bpb.bspf < x2) { + warnx("warning: sectors/FAT limits file system to %u clusters", + cls); + return -1; + } + if (cls < mincls(o.fat_type)) { + warnx("%u clusters too few clusters for FAT%u, need %u", cls, + o.fat_type, mincls(o.fat_type)); + return -1; + } + if (cls > maxcls(o.fat_type)) { + cls = maxcls(o.fat_type); + bpb.bsec = x1 + (cls + 1) * bpb.spc - 1; + warnx("warning: FAT type limits file system to %u sectors", + bpb.bsec); + return -1; + } + printf("%s: %u sector%s in %u FAT%u cluster%s " + "(%u bytes/cluster)\n", fname, cls * bpb.spc, + cls * bpb.spc == 1 ? "" : "s", cls, o.fat_type, + cls == 1 ? "" : "s", bpb.bps * bpb.spc); + if (!bpb.mid) + bpb.mid = !bpb.hid ? 0xf0 : 0xf8; + if (o.fat_type == 32) + bpb.rdcl = RESFTE; + if (bpb.hid + bpb.bsec <= MAXU16) { + bpb.sec = bpb.bsec; + bpb.bsec = 0; + } + if (o.fat_type != 32) { + bpb.spf = bpb.bspf; + bpb.bspf = 0; + } + ch = 0; + if (o.fat_type == 12) + ch = 1; /* 001 Primary DOS with 12 bit FAT */ + else if (o.fat_type == 16) { + if (bpb.bsec == 0) + ch = 4; /* 004 Primary DOS with 16 bit FAT <32M */ + else + ch = 6; /* 006 Primary 'big' DOS, 16-bit FAT (> 32MB) */ + /* + * XXX: what about: + * 014 DOS (16-bit FAT) - LBA + * ? + */ + } else if (o.fat_type == 32) { + ch = 11; /* 011 Primary DOS with 32 bit FAT */ + /* + * XXX: what about: + * 012 Primary DOS with 32 bit FAT - LBA + * ? + */ + } + if (ch != 0) + printf("MBR type: %d\n", ch); + print_bpb(&bpb); + if (!o.no_create) { + gettimeofday(&tv, NULL); + now = tv.tv_sec; + tm = localtime(&now); + if (!(img = malloc(bpb.bps))) + err(1, NULL); + dir = bpb.res + (bpb.spf ? bpb.spf : bpb.bspf) * bpb.nft; +#ifdef SIGINFO + signal(SIGINFO, infohandler); +#endif + for (lsn = 0; lsn < dir + (o.fat_type == 32 ? bpb.spc : rds); lsn++) { + if (got_siginfo) { + fprintf(stderr,"%s: writing sector %u of %u (%u%%)\n", + fname,lsn,(dir + (o.fat_type == 32 ? bpb.spc : rds)), + (lsn*100)/(dir + (o.fat_type == 32 ? bpb.spc : rds))); + got_siginfo = 0; + } + x = lsn; + if (o.bootstrap && + o.fat_type == 32 && bpb.bkbs != MAXU16 && + bss <= bpb.bkbs && x >= bpb.bkbs) { + x -= bpb.bkbs; + if (!x && lseek(fd1, o.offset, SEEK_SET)) { + warn("%s", bname); + return -1; + } + } + if (o.bootstrap && x < bss) { + if ((n = read(fd1, img, bpb.bps)) == -1) { + warn("%s", bname); + return -1; + } + if ((size_t)n != bpb.bps) { + warnx("%s: can't read sector %u", bname, x); + return -1; + } + } else + memset(img, 0, bpb.bps); + if (!lsn || + (o.fat_type == 32 && bpb.bkbs != MAXU16 && lsn == bpb.bkbs)) { + x1 = sizeof(struct bs); + bsbpb = (struct bsbpb *)(img + x1); + mk2(bsbpb->bps, bpb.bps); + mk1(bsbpb->spc, bpb.spc); + mk2(bsbpb->res, bpb.res); + mk1(bsbpb->nft, bpb.nft); + mk2(bsbpb->rde, bpb.rde); + mk2(bsbpb->sec, bpb.sec); + mk1(bsbpb->mid, bpb.mid); + mk2(bsbpb->spf, bpb.spf); + mk2(bsbpb->spt, bpb.spt); + mk2(bsbpb->hds, bpb.hds); + mk4(bsbpb->hid, bpb.hid); + mk4(bsbpb->bsec, bpb.bsec); + x1 += sizeof(struct bsbpb); + if (o.fat_type == 32) { + bsxbpb = (struct bsxbpb *)(img + x1); + mk4(bsxbpb->bspf, bpb.bspf); + mk2(bsxbpb->xflg, 0); + mk2(bsxbpb->vers, 0); + mk4(bsxbpb->rdcl, bpb.rdcl); + mk2(bsxbpb->infs, bpb.infs); + mk2(bsxbpb->bkbs, bpb.bkbs); + x1 += sizeof(struct bsxbpb); + } + bsx = (struct bsx *)(img + x1); + mk1(bsx->sig, 0x29); + if (o.volume_id_set) + x = o.volume_id; + else + x = (((u_int)(1 + tm->tm_mon) << 8 | + (u_int)tm->tm_mday) + + ((u_int)tm->tm_sec << 8 | + (u_int)(tv.tv_usec / 10))) << 16 | + ((u_int)(1900 + tm->tm_year) + + ((u_int)tm->tm_hour << 8 | + (u_int)tm->tm_min)); + mk4(bsx->volid, x); + mklabel(bsx->label, o.volume_label ? o.volume_label : "NO NAME"); + snprintf(buf, sizeof(buf), "FAT%u", o.fat_type); + setstr(bsx->type, buf, sizeof(bsx->type)); + if (!o.bootstrap) { + x1 += sizeof(struct bsx); + bs = (struct bs *)img; + mk1(bs->jmp[0], 0xeb); + mk1(bs->jmp[1], x1 - 2); + mk1(bs->jmp[2], 0x90); + setstr(bs->oem, o.OEM_string ? o.OEM_string : "NetBSD", + sizeof(bs->oem)); + memcpy(img + x1, bootcode, sizeof(bootcode)); + mk2(img + MINBPS - 2, DOSMAGIC); + } + } else if (o.fat_type == 32 && bpb.infs != MAXU16 && + (lsn == bpb.infs || + (bpb.bkbs != MAXU16 && + lsn == bpb.bkbs + bpb.infs))) { + mk4(img, 0x41615252); + mk4(img + MINBPS - 28, 0x61417272); + mk4(img + MINBPS - 24, 0xffffffff); + mk4(img + MINBPS - 20, bpb.rdcl); + mk2(img + MINBPS - 2, DOSMAGIC); + } else if (lsn >= bpb.res && lsn < dir && + !((lsn - bpb.res) % + (bpb.spf ? bpb.spf : bpb.bspf))) { + mk1(img[0], bpb.mid); + for (x = 1; x < o.fat_type * (o.fat_type == 32 ? 3U : 2U) / 8U; x++) + mk1(img[x], o.fat_type == 32 && x % 4 == 3 ? 0x0f : 0xff); + } else if (lsn == dir && o.volume_label) { + de = (struct de *)img; + mklabel(de->namext, o.volume_label); + mk1(de->attr, 050); + x = (u_int)tm->tm_hour << 11 | + (u_int)tm->tm_min << 5 | + (u_int)tm->tm_sec >> 1; + mk2(de->time, x); + x = (u_int)(tm->tm_year - 80) << 9 | + (u_int)(tm->tm_mon + 1) << 5 | + (u_int)tm->tm_mday; + mk2(de->date, x); + } + if ((n = write(fd, img, bpb.bps)) == -1) { + warn("%s", fname); + return -1; + } + if ((size_t)n != bpb.bps) { + warnx("%s: can't write sector %u", fname, lsn); + return -1; + } + } + } + return 0; +} + +#ifndef MAKEFS +/* + * return -1 with error if file system is mounted. + */ +static int +check_mounted(const char *fname, mode_t mode) +{ + struct statvfs *mp; + const char *s1, *s2; + size_t len; + int n, r; + + if (!(n = getmntinfo(&mp, MNT_NOWAIT))) { + warn("getmntinfo"); + return -1; + } + len = strlen(_PATH_DEV); + s1 = fname; + if (!strncmp(s1, _PATH_DEV, len)) + s1 += len; + r = S_ISCHR(mode) && s1 != fname && *s1 == 'r'; + for (; n--; mp++) { + s2 = mp->f_mntfromname; + if (!strncmp(s2, _PATH_DEV, len)) + s2 += len; + if ((r && s2 != mp->f_mntfromname && !strcmp(s1 + 1, s2)) || + !strcmp(s1, s2)) { + warnx("%s is mounted on %s", fname, mp->f_mntonname); + return -1; + } + } + return 0; +} +#endif + +/* + * Get a standard format. + */ +static int +getstdfmt(const char *fmt, struct bpb *bpb) +{ + u_int x, i; + + x = sizeof(stdfmt) / sizeof(stdfmt[0]); + for (i = 0; i < x && strcmp(fmt, stdfmt[i].name); i++); + if (i == x) { + warnx("%s: unknown standard format", fmt); + return -1; + } + *bpb = stdfmt[i].bpb; + return 0; +} + +/* + * Get disk slice, partition, and geometry information. + */ +static int +getbpbinfo(int fd, const char *fname, const char *dtype, int iflag, + struct bpb *bpb, int create) +{ + const char *s1, *s2; + int part; + + part = -1; + s1 = fname; + if ((s2 = strrchr(s1, '/'))) + s1 = s2 + 1; + for (s2 = s1; *s2 && !isdigit((unsigned char)*s2); s2++); + if (!*s2 || s2 == s1) + s2 = NULL; + else + while (isdigit((unsigned char)*++s2)); + s1 = s2; + +#ifndef MAKEFS + int maxpartitions = getmaxpartitions(); + + // XXX: Does not work with wedges + if (s2 && *s2 >= 'a' && *s2 <= 'a' + maxpartitions - 1) { + part = *s2++ - 'a'; + } +#endif + if (((part != -1) && ((!iflag && part != -1) || !bpb->bsec)) || + !bpb->bps || !bpb->spt || !bpb->hds) { + u_int sector_size; + u_int nsectors; + u_int ntracks; + u_int size; +#ifndef MAKEFS + struct disk_geom geo; + struct dkwedge_info dkw; + + if (!create && getdiskinfo(fname, fd, NULL, &geo, &dkw) != -1) { + sector_size = geo.dg_secsize = 512; + nsectors = geo.dg_nsectors = 63; + ntracks = geo.dg_ntracks = 255; + size = dkw.dkw_size; + } else +#endif + { + struct stat st; + + if (fstat(fd, &st) == -1) { + warnx("Can't get disk size for `%s'", fname); + return -1; + } + /* create a fake geometry for a file image */ + sector_size = 512; + nsectors = 63; + ntracks = 255; + size = st.st_size / sector_size; + } + if (!bpb->bps) { + if (ckgeom(fname, sector_size, "bytes/sector") == -1) + return -1; + bpb->bps = sector_size; + } + + if (nsectors > 63) { + /* + * The kernel doesn't accept BPB with spt > 63. + * (see sys/fs/msdosfs/msdosfs_vfsops.c:msdosfs_mountfs()) + * If values taken from disklabel don't match these + * restrictions, use popular BIOS default values instead. + */ + nsectors = 63; + } + if (!bpb->spt) { + if (ckgeom(fname, nsectors, "sectors/track") == -1) + return -1; + bpb->spt = nsectors; + } + if (!bpb->hds) + if (ckgeom(fname, ntracks, "drive heads") == -1) + return -1; + bpb->hds = ntracks; + if (!bpb->bsec) + bpb->bsec = size; + } + return 0; +} + +/* + * Print out BPB values. + */ +static void +print_bpb(struct bpb *bpb) +{ + printf("bps=%u spc=%u res=%u nft=%u", bpb->bps, bpb->spc, bpb->res, + bpb->nft); + if (bpb->rde) + printf(" rde=%u", bpb->rde); + if (bpb->sec) + printf(" sec=%u", bpb->sec); + printf(" mid=%#x", bpb->mid); + if (bpb->spf) + printf(" spf=%u", bpb->spf); + printf(" spt=%u hds=%u hid=%u", bpb->spt, bpb->hds, bpb->hid); + if (bpb->bsec) + printf(" bsec=%u", bpb->bsec); + if (!bpb->spf) { + printf(" bspf=%u rdcl=%u", bpb->bspf, bpb->rdcl); + printf(" infs="); + printf(bpb->infs == MAXU16 ? "%#x" : "%u", bpb->infs); + printf(" bkbs="); + printf(bpb->bkbs == MAXU16 ? "%#x" : "%u", bpb->bkbs); + } + printf("\n"); +} + +/* + * Check a disk geometry value. + */ +static int +ckgeom(const char *fname, u_int val, const char *msg) +{ + if (!val) { + warnx("%s: no default %s", fname, msg); + return -1; + } + if (val > MAXU16) { + warnx("%s: illegal %s", fname, msg); + return -1; + } + return 0; +} +/* + * Check a volume label. + */ +static int +oklabel(const char *src) +{ + int c, i; + + for (i = 0; i <= 11; i++) { + c = (u_char)*src++; + if (c < ' ' + !i || strchr("\"*+,./:;<=>?[\\]|", c)) + break; + } + return i && !c; +} + +/* + * Make a volume label. + */ +static void +mklabel(u_int8_t *dest, const char *src) +{ + int c, i; + + for (i = 0; i < 11; i++) { + c = *src ? toupper((unsigned char)*src++) : ' '; + *dest++ = !i && c == '\xe5' ? 5 : c; + } +} + +/* + * Copy string, padding with spaces. + */ +static void +setstr(u_int8_t *dest, const char *src, size_t len) +{ + while (len--) + *dest++ = *src ? *src++ : ' '; +} + +static void +infohandler(int sig) +{ + got_siginfo = 1; +} diff --git a/sbin/newfs_msdos/mkfs_msdos.h b/sbin/newfs_msdos/mkfs_msdos.h new file mode 100644 index 000000000..1987636d9 --- /dev/null +++ b/sbin/newfs_msdos/mkfs_msdos.h @@ -0,0 +1,71 @@ +/* $NetBSD: mkfs_msdos.h,v 1.2 2013/01/23 15:29:15 christos Exp $ */ + +/*- + * Copyright (c) 2013 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#define ALLOPTS \ +AOPT('@', off_t, offset, 0, "Offset in device") \ +AOPT('B', char *, bootstrap, -1, "Bootstrap file") \ +AOPT('C', off_t, create_size, 0, "Create file") \ +AOPT('F', uint8_t, fat_type, 12, "FAT type (12, 16, or 32)") \ +AOPT('I', uint32_t, volume_id, 0, "Volume ID") \ +AOPT('L', char *, volume_label, -1, "Volume Label") \ +AOPT('N', bool, no_create, -2, "Don't create filesystem, print params only") \ +AOPT('O', char *, OEM_string, -1, "OEM string") \ +AOPT('S', uint16_t, bytes_per_sector, 1, "Bytes per sector") \ +AOPT('a', uint32_t, sectors_per_fat, 1, "Sectors per FAT") \ +AOPT('b', uint32_t, block_size, 1, "Block size") \ +AOPT('c', uint8_t, sectors_per_cluster, 1, "Sectors per cluster") \ +AOPT('e', uint16_t, directory_entries, 1, "Directory entries") \ +AOPT('f', char *, floppy, -1, "Standard format floppies (160,180,320,360,640,720,1200,1232,1440,2880)") \ +AOPT('h', uint16_t, drive_heads, 1, "Drive heads") \ +AOPT('i', uint16_t, info_sector, 1, "Info sector") \ +AOPT('k', uint16_t, backup_sector, 1, "Backup sector") \ +AOPT('m', uint8_t, media_descriptor, 0, "Media descriptor") \ +AOPT('n', uint8_t, num_FAT, 1, "Number of FATs") \ +AOPT('o', uint32_t, hidden_sectors, 0, "Hidden sectors") \ +AOPT('r', uint16_t, reserved_sectors, 1, "Reserved sectors") \ +AOPT('s', uint32_t, size, 1, "File System size") \ +AOPT('u', uint16_t, sectors_per_track, 1, "Sectors per track") + +struct msdos_options { +#define AOPT(_opt, _type, _name, _min, _desc) _type _name; +ALLOPTS +#undef AOPT + uint32_t volume_id_set:1; + uint32_t media_descriptor_set:1; + uint32_t hidden_sectors_set:1; +}; + +int mkfs_msdos(const char *, const char *, const struct msdos_options *); diff --git a/sbin/newfs_msdos/newfs_msdos.8 b/sbin/newfs_msdos/newfs_msdos.8 new file mode 100644 index 000000000..930874a85 --- /dev/null +++ b/sbin/newfs_msdos/newfs_msdos.8 @@ -0,0 +1,241 @@ +.\" $NetBSD: newfs_msdos.8,v 1.18 2013/07/20 21:39:58 wiz Exp $ +.\" +.\" Copyright (c) 1998 Robert Nordier +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in +.\" the documentation and/or other materials provided with the +.\" distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS +.\" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY +.\" DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +.\" GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +.\" IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +.\" OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +.\" IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" From: $FreeBSD: src/sbin/newfs_msdos/newfs_msdos.8,v 1.13 2001/08/14 10:01:48 ru Exp $ +.\" +.Dd March 26, 2009 +.Dt NEWFS_MSDOS 8 +.Os +.Sh NAME +.Nm newfs_msdos +.Nd construct a new MS-DOS (FAT) file system +.Sh SYNOPSIS +.Nm +.Op Fl N +.Op Fl @ Ar offset +.Op Fl B Ar boot +.Op Fl C Ar create-size +.Op Fl F Ar FAT-type +.Op Fl I Ar volid +.Op Fl L Ar label +.Op Fl O Ar OEM +.Op Fl S Ar sector-size +.Op Fl a Ar FAT-size +.Op Fl b Ar block-size +.Op Fl c Ar cluster-size +.Op Fl e Ar dirents +.Op Fl f Ar format +.Op Fl h Ar heads +.Op Fl i Ar info +.Op Fl k Ar backup +.Op Fl m Ar media +.Op Fl n Ar FATs +.Op Fl o Ar hidden +.Op Fl r Ar reserved +.Op Fl s Ar total +.Op Fl u Ar track-size +.Ar special +.Op Ar disktype +.Sh DESCRIPTION +The +.Nm +utility creates a FAT12, FAT16, or FAT32 file system on device or file named +.Ar special , +using +.Xr disktab 5 +entry +.Ar disktype +to determine geometry, if required. +.Pp +The options are as follow: +.Bl -tag -width indent +.It Fl N +Don't create a file system: just print out parameters. +.It Fl @ Ar offset +Build the filesystem at the specified offset in bytes in the device or file. +A suffix s, k, m, g (lower or upper case) +appended to the offset specifies that the +number is in sectors, kilobytes, megabytes or gigabytes, respectively. +.It Fl B Ar boot +Get bootstrap from file. +.It Fl C Ar create-size +Create the image file with the specified size. +A suffix character appended to the size is interpreted as for the +.Fl @ +option. +The file is created by truncating any existing file with the +same name, seeking just before the required size and writing +a single 0 byte. +As a consequence, the space occupied on disk +may be smaller than the size specified as a parameter. +.It Fl F Ar FAT-type +FAT type (one of 12, 16, or 32). +.It Fl I Ar volid +Volume ID. +.It Fl L Ar label +Volume label (up to 11 characters). +The label should consist of only those characters permitted +in regular DOS (8+3) filenames. +The default is +.Qq Li "NO_NAME" . +.It Fl O Ar OEM +OEM string (up to 8 characters). +The default is +.Qq Li "NetBSD" . +.It Fl S Ar sector-size +Number of bytes per sector. +Acceptable values are powers of 2 in the range 512 through 32768. +.It Fl a Ar FAT-size +Number of sectors per FAT. +.It Fl b Ar block-size +File system block size (bytes per cluster). +This should resolve to an acceptable number of sectors +per cluster (see below). +.It Fl c Ar cluster-size +Sectors per cluster. +Acceptable values are powers of 2 in the range 1 through 128. +If the block or cluster size are not specified, the code +uses a cluster between 512 bytes and 32K depending on +the filesystem size. +.It Fl e Ar dirents +Number of root directory entries (FAT12 and FAT16 only). +.It Fl f Ar format +Specify a standard (floppy disk) format. +The standard formats are (capacities in kilobytes): +160, 180, 320, 360, 640, 720, 1200, 1232, 1440, 2880. +.It Fl h Ar heads +Number of drive heads. +.It Fl i Ar info +Location of the file system info sector (FAT32 only). +A value of 0xffff signifies no info sector. +.It Fl k Ar backup +Location of the backup boot sector (FAT32 only). +A value of 0xffff signifies no backup sector. +.It Fl m Ar media +Media descriptor (acceptable range 0xf0 to 0xff). +.It Fl n Ar FATs +Number of FATs. +Acceptable values are 1 to 16 inclusive. +The default is 2. +.It Fl o Ar hidden +Number of hidden sectors. +.It Fl r Ar reserved +Number of reserved sectors. +.It Fl s Ar total +File system size. +.It Fl u Ar track-size +Number of sectors per track. +.El +.Pp +If +.Nm +receives a +.Dv SIGINFO +signal +(see the +.Sy status +argument for +.Xr stty 1 ) , +a line will be written to the standard error output indicating +the name of the device currently being formatted, the sector +number being written, and the total number of sectors to be written. +.Sh NOTES +If some parameters (e.g. size, number of sectors, etc.) are not specified +through options or disktype, the program tries to generate them +automatically. +In particular, the size is determined as the +device or file size minus the offset specified with the +.Fl @ +option. +When the geometry is not available, it is assumed to be +63 sectors, 255 heads. +The size is then rounded to become +a multiple of the track size and avoid complaints by some filesystem code. +.Pp +FAT file system parameters occupy a "Boot Sector BPB (BIOS Parameter +Block)" in the first of the "reserved" sectors which precede the actual +file system. +For reference purposes, this structure is presented below. +.Bd -literal +struct bsbpb { + u_int16_t bps; /* [-S] bytes per sector */ + u_int8_t spc; /* [-c] sectors per cluster */ + u_int16_t res; /* [-r] reserved sectors */ + u_int8_t nft; /* [-n] number of FATs */ + u_int16_t rde; /* [-e] root directory entries */ + u_int16_t sec; /* [-s] total sectors */ + u_int8_t mid; /* [-m] media descriptor */ + u_int16_t spf; /* [-a] sectors per FAT */ + u_int16_t spt; /* [-u] sectors per track */ + u_int16_t hds; /* [-h] drive heads */ + u_int32_t hid; /* [-o] hidden sectors */ + u_int32_t bsec; /* [-s] big total sectors */ +}; +/* FAT32 extensions */ +struct bsxbpb { + u_int32_t bspf; /* [-a] big sectors per FAT */ + u_int16_t xflg; /* control flags */ + u_int16_t vers; /* file system version */ + u_int32_t rdcl; /* root directory start cluster */ + u_int16_t infs; /* [-i] file system info sector */ + u_int16_t bkbs; /* [-k] backup boot sector */ +}; +.Ed +.Sh EXAMPLES +.Bd -literal -offset indent +newfs_msdos /dev/rwd1a +.Ed +.Pp +Create a file system, using default parameters, on +.Pa /dev/rwd1a . +.Bd -literal -offset indent +newfs_msdos -f 1440 -L foo /dev/rfd0a +.Ed +.Pp +Create a standard 1.44M file system, with volume label +.Ar foo , +on +.Pa /dev/rfd0a . +Create a 30MB image file, with the FAT partition starting +63 sectors within the image file: +.Bd -literal -offset indent +newfs_msdos -C 30M -@63s ./somefile +.Ed +.Sh DIAGNOSTICS +Exit status is 0 on success and 1 on error. +.Sh SEE ALSO +.Xr disktab 5 , +.Xr disklabel 8 , +.Xr fdisk 8 , +.Xr newfs 8 +.Sh HISTORY +The +.Nm +command first appeared in +.Nx 1.3 . +.Sh AUTHORS +.An Robert Nordier Aq Mt rnordier@FreeBSD.org . diff --git a/sbin/newfs_msdos/newfs_msdos.c b/sbin/newfs_msdos/newfs_msdos.c new file mode 100644 index 000000000..42b61d196 --- /dev/null +++ b/sbin/newfs_msdos/newfs_msdos.c @@ -0,0 +1,253 @@ +/* $NetBSD: newfs_msdos.c,v 1.42 2013/01/23 15:29:15 christos Exp $ */ + +/* + * Copyright (c) 1998 Robert Nordier + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static const char rcsid[] = + "$FreeBSD: src/sbin/newfs_msdos/newfs_msdos.c,v 1.15 2000/10/10 01:49:37 wollman Exp $"; +#else +__RCSID("$NetBSD: newfs_msdos.c,v 1.42 2013/01/23 15:29:15 christos Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mkfs_msdos.h" + +#define argto1(arg, lo, msg) argtou(arg, lo, 0xff, msg) +#define argto2(arg, lo, msg) argtou(arg, lo, 0xffff, msg) +#define argto4(arg, lo, msg) argtou(arg, lo, 0xffffffff, msg) +#define argtox(arg, lo, msg) argtou(arg, lo, UINT_MAX, msg) + +__dead static void usage(void); +static u_int argtou(const char *, u_int, u_int, const char *); +static off_t argtooff(const char *, const char *); + +/* + * Construct a FAT12, FAT16, or FAT32 file system. + */ +int +main(int argc, char *argv[]) +{ + static const char opts[] = "@:NB:C:F:I:L:O:S:a:b:c:e:f:h:i:k:m:n:o:r:s:u:"; + struct msdos_options o; + char *fname, *dtype; + char buf[MAXPATHLEN]; + int ch; + + memset(&o, 0, sizeof(o)); + + while ((ch = getopt(argc, argv, opts)) != -1) + switch (ch) { + case '@': + o.offset = argtooff(optarg, "offset"); + break; + case 'N': + o.no_create = 1; + break; + case 'B': + o.bootstrap = optarg; + break; + case 'C': + o.create_size = argtooff(optarg, "create size"); + break; + case 'F': + o.fat_type = atoi(optarg); + break; + case 'I': + o.volume_id = argto4(optarg, 0, "volume ID"); + o.volume_id_set = 1; + break; + case 'L': + o.volume_label = optarg; + break; + case 'O': + o.OEM_string = optarg; + break; + case 'S': + o.bytes_per_sector = argto2(optarg, 1, "bytes/sector"); + break; + case 'a': + o.sectors_per_fat = argto4(optarg, 1, "sectors/FAT"); + break; + case 'b': + o.block_size = argtox(optarg, 1, "block size"); + break; + case 'c': + o.sectors_per_cluster = argto1(optarg, 1, "sectors/cluster"); + break; + case 'e': + o.directory_entries = argto2(optarg, 1, "directory entries"); + break; + case 'f': + o.floppy = optarg; + break; + case 'h': + o.drive_heads = argto2(optarg, 1, "drive heads"); + break; + case 'i': + o.info_sector = argto2(optarg, 1, "info sector"); + break; + case 'k': + o.backup_sector = argto2(optarg, 1, "backup sector"); + break; + case 'm': + o.media_descriptor = argto1(optarg, 0, "media descriptor"); + o.media_descriptor_set = 1; + break; + case 'n': + o.num_FAT = argto1(optarg, 1, "number of FATs"); + break; + case 'o': + o.hidden_sectors = argto4(optarg, 0, "hidden sectors"); + o.hidden_sectors_set = 1; + break; + case 'r': + o.reserved_sectors = argto2(optarg, 1, "reserved sectors"); + break; + case 's': + o.size = argto4(optarg, 1, "file system size"); + break; + case 'u': + o.sectors_per_track = argto2(optarg, 1, "sectors/track"); + break; + default: + usage(); + } + argc -= optind; + argv += optind; + if (argc < 1 || argc > 2) + usage(); + fname = *argv++; + if (!strchr(fname, '/') && !o.create_size) { + snprintf(buf, sizeof(buf), "%sr%s", _PATH_DEV, fname); + if (!(fname = strdup(buf))) + err(1, NULL); + } + dtype = *argv; + return mkfs_msdos(fname, dtype, &o); +} + +/* + * Convert and check a numeric option argument. + */ +static u_int +argtou(const char *arg, u_int lo, u_int hi, const char *msg) +{ + char *s; + u_long x; + + errno = 0; + x = strtoul(arg, &s, 0); + if (errno || !*arg || *s || x < lo || x > hi) + errx(1, "%s: bad %s", arg, msg); + return x; +} + +/* + * Same for off_t, with optional skmgpP suffix + */ +static off_t +argtooff(const char *arg, const char *msg) +{ + char *s; + off_t x; + + errno = 0; + x = strtoll(arg, &s, 0); + /* allow at most one extra char */ + if (errno || x < 0 || (s[0] && s[1]) ) + errx(1, "%s: bad %s", arg, msg); + if (*s) { /* the extra char is the multiplier */ + switch (*s) { + default: + errx(1, "%s: bad %s", arg, msg); + /* notreached */ + + case 's': /* sector */ + case 'S': + x <<= 9; /* times 512 */ + break; + + case 'k': /* kilobyte */ + case 'K': + x <<= 10; /* times 1024 */ + break; + + case 'm': /* megabyte */ + case 'M': + x <<= 20; /* times 1024*1024 */ + break; + + case 'g': /* gigabyte */ + case 'G': + x <<= 30; /* times 1024*1024*1024 */ + break; + + case 'p': /* partition start */ + case 'P': /* partition start */ + case 'l': /* partition length */ + case 'L': /* partition length */ + errx(1, "%s: not supported yet %s", arg, msg); + return -1; + /* notreached */ + } + } + return x; +} + +/* + * Print usage message. + */ +static void +usage(void) +{ + fprintf(stderr, + "usage: %s [ -options ] special [disktype]\n", getprogname()); + fprintf(stderr, "where the options are:\n"); +static struct { + char o; + const char *h; +} opts[] = { +#define AOPT(_opt, _type, _name, _min, _desc) { _opt, _desc }, +ALLOPTS +#undef AOPT +}; + for (size_t i = 0; i < __arraycount(opts); i++) + fprintf(stderr, "\t-%c %s\n", opts[i].o, opts[i].h); + exit(1); +} diff --git a/sbin/newfs_udf/Makefile b/sbin/newfs_udf/Makefile new file mode 100644 index 000000000..008587e8a --- /dev/null +++ b/sbin/newfs_udf/Makefile @@ -0,0 +1,17 @@ +# $NetBSD: Makefile,v 1.4 2013/07/18 12:44:21 reinoud Exp $ + +.include + +PROG= newfs_udf +MAN= newfs_udf.8 +SRCS= newfs_udf.c udf_create.c udf_write.c udf_osta.c fattr.c + +MOUNT= ${NETBSDSRCDIR}/sbin/mount +KUDF= ${NETBSDSRCDIR}/sys/fs/udf +CPPFLAGS+= -I${MOUNT} -I${KUDF} -I${NETBSDSRCDIR}/sys +.PATH: ${MOUNT} ${KUDF} + +DPADD+=${LIBUTIL} +LDADD+=-lutil + +.include diff --git a/sbin/newfs_udf/newfs_udf.8 b/sbin/newfs_udf/newfs_udf.8 new file mode 100644 index 000000000..2660fb4e7 --- /dev/null +++ b/sbin/newfs_udf/newfs_udf.8 @@ -0,0 +1,191 @@ +.\" $NetBSD: newfs_udf.8,v 1.18 2013/08/06 12:15:20 wiz Exp $ +.\" +.\" Copyright (c) 2008 Reinoud Zandijk +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in +.\" the documentation and/or other materials provided with the +.\" distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS +.\" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY +.\" DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +.\" GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +.\" IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +.\" OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +.\" IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" +.Dd August 2, 2013 +.Dt NEWFS_UDF 8 +.Os +.Sh NAME +.Nm newfs_udf +.Nd construct a new UDF file system +.Sh SYNOPSIS +.Nm +.Op Fl cFM +.Op Fl B Ar blockingsize +.Op Fl L Ar loglabel +.Op Fl P Ar discid +.Op Fl p Ar percentage +.Op Fl S Ar sectorsize +.Op Fl s Ar size +.Op Fl t Ar gmtoff +.Op Fl V Ar max_udf +.Op Fl v Ar min_udf +.Ar special +.Sh DESCRIPTION +The +.Nm +utility creates an UDF file system on device +.Ar special +suitable for the media currently inserted. +.Pp +The options are as follow: +.Bl -tag -width indent +.It Fl B Ar blockingsize +When creating image files, specify the blocking size or packetsize of the media +to +.Ar blockingsize . +.It Fl c +Perform a crude surface check first to weed out disc faults on rewritable +media. +.It Fl F +Force file system construction on non-empty recordable media or create an +image file. +.It Fl L Ar loglabel +Set the disc logical label to the specified +.Ar loglabel . +.It Fl M +Disable metadata partition creation when selected UDF version or media dictates +this. +For strict conformance and interchange, don't disable this unless +its causing problems. +.It Fl P Ar discid +Set the physical disc label to the specified +.Ar discid . +.Pp +Prepend +.Ar discid +with volsetname separated with a ':' if wanted. +For strict conformance and interchange, don't set this manually unless it has +a unique hex number in the first 8 character positions. +.It Fl p Ar percentage +Percentage of partition to be initially reserved for metadata on the Metadata +partition. +It defaults to 20 %. +.It Fl S Ar sectorsize +Set the sectorsize for image files. +For strict conformance and interchange, don't set this manually. +.It Fl s Ar size +For image files, set the file size to the humanized size +.Ar size . +.It Fl t Ar gmtoff +Use the specified +.Ar gmtoff +as gmt time offset for recording times on the disc. +.It Fl V Ar max_udf +Select +.Ar max_udf +as the maximum UDF version to be supported. +For UDF version 2.50, use +.Dq 0x250 +or +.Dq 2.50 . +.It Fl v Ar min_udf +Select +.Ar min_udf +as the minimum UDF version to be supported. +For UDF version 2.01, use +.Dq 0x201 +or +.Dq 2.01 . +.El +.Sh NOTES +The UDF file system is defined for the entire optical medium. +It can only function on the entire CD/DVD/BD so the raw partition +has to be specified for read/write actions. +For +.Nm +this means specifying the raw device with the raw partition, i.e. +.Pa /dev/rcd0d +or +.Pa /dev/rcd0c . +.Pp +Some rewritable optical media needs to be formatted first before it can be +used by UDF. +This can be done using +.Xr mmcformat 8 . +.Pp +The default UDF version is version 2.01. +.Sh EXAMPLES +Create a file system, using the specified names on the device +.Pa /dev/rcd0d +with the default UDF version : +.Bd -literal -offset indent +newfs_udf -P "Encyclopedia:copy-nr-1" -L "volume 2" /dev/rcd0d +.Ed +.Pp +Create a 4.8 GiB sparse file and configure it using +.Xr vnconfig 8 +to be a 2048 sector size disc and create a new UDF file system on +.Pa /dev/rvnd0d +: +.Bd -literal -offset indent +dd if=/dev/zero of=bigdisk.2048.udf seek=9999999 count=1 +vnconfig -c vnd0 bigdisk.2048.udf 2048/1/1/1 +newfs_udf -L bigdisk /dev/rvnd0d +.Ed +.Pp +Create a 2 GiB file and create a new UDF file system on it using the default +512 byte sector size : +.Bd -literal -offset indent +newfs_udf -L bigdisk2 -F -s 2G bigdisk2.iso +.Ed +.Pp +Create a 200 MiB file and create a new UDF file system on it using a sector size +of 2048 : +.Bd -literal -offset indent +newfs_udf -L bigdisk2 -F -s 200M -S 2048 bigdisk3.iso +.Ed +.Pp +Create a new UDF file system on the inserted USB stick using its +native sectorsize of 512 : +.Bd -literal -offset indent +newfs_udf -L "My USB stick" /dev/rsd0d +.Ed +.Sh SEE ALSO +.Xr disktab 5 , +.Xr disklabel 8 , +.Xr mmcformat 8 , +.Xr newfs 8 +.Sh HISTORY +The +.Nm +command first appeared in +.Nx 5.0 . +.Sh AUTHORS +.An Reinoud Zandijk Aq Mt reinoud@NetBSD.org +.Sh BUGS +The +.Ar P +and the +.Ar S +arguments have changed meaning. +The meaning of +.Ar S +has been merged into +.Ar P +since +.Nx 6.1 . diff --git a/sbin/newfs_udf/newfs_udf.c b/sbin/newfs_udf/newfs_udf.c new file mode 100644 index 000000000..eaab2b144 --- /dev/null +++ b/sbin/newfs_udf/newfs_udf.c @@ -0,0 +1,896 @@ +/* $NetBSD: newfs_udf.c,v 1.18 2013/08/09 15:11:08 reinoud Exp $ */ + +/* + * Copyright (c) 2006, 2008, 2013 Reinoud Zandijk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* + * TODO + * - implement metadata formatting for BD-R + * - implement support for a read-only companion partition? + */ + +#define _EXPOSE_MMC +#if 0 +# define DEBUG +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "mountprog.h" +#include "udf_create.h" +#include "udf_write.h" +#include "newfs_udf.h" + +/* prototypes */ +int newfs_udf(int argc, char **argv); +static void usage(void) __attribute__((__noreturn__)); + + +/* queue for temporary storage of sectors to be written out */ +struct wrsect { + uint64_t sectornr; + uint8_t *sector_data; + TAILQ_ENTRY(wrsect) next; +}; + +/* write queue and track blocking skew */ +TAILQ_HEAD(wrsect_list, wrsect) write_queue; + + +/* global variables describing disc and format requests */ +int fd; /* device: file descriptor */ +char *dev; /* device: name */ +struct mmc_discinfo mmc_discinfo; /* device: disc info */ + +char *format_str; /* format: string representation */ +int format_flags; /* format: attribute flags */ +int media_accesstype; /* derived from current mmc cap */ +int check_surface; /* for rewritables */ +int imagefile_secsize; /* for files */ +int emul_packetsize; /* for discs and files */ + +int wrtrack_skew; +int meta_perc = UDF_META_PERC; +float meta_fract = (float) UDF_META_PERC / 100.0; + + +/* --------------------------------------------------------------------- */ + +/* + * write queue implementation + */ + +int +udf_write_sector(void *sector, uint64_t location) +{ + struct wrsect *pos, *seekpos; + + + /* search location */ + TAILQ_FOREACH_REVERSE(seekpos, &write_queue, wrsect_list, next) { + if (seekpos->sectornr <= location) + break; + } + if ((seekpos == NULL) || (seekpos->sectornr != location)) { + pos = calloc(1, sizeof(struct wrsect)); + if (pos == NULL) + return ENOMEM; + /* allocate space for copy of sector data */ + pos->sector_data = calloc(1, context.sector_size); + if (pos->sector_data == NULL) + return ENOMEM; + pos->sectornr = location; + + if (seekpos) { + TAILQ_INSERT_AFTER(&write_queue, seekpos, pos, next); + } else { + TAILQ_INSERT_HEAD(&write_queue, pos, next); + } + } else { + pos = seekpos; + } + memcpy(pos->sector_data, sector, context.sector_size); + + return 0; +} + + +/* + * Now all write requests are queued in the TAILQ, write them out to the + * disc/file image. Special care needs to be taken for devices that are only + * strict overwritable i.e. only in packet size chunks + * + * XXX support for growing vnd? + */ + +int +writeout_write_queue(void) +{ + struct wrsect *pos; + uint64_t offset; + uint64_t line_start, new_line_start; + uint32_t line_len, line_offset, relpos; + uint32_t blockingnr; + uint8_t *linebuf, *adr; + + blockingnr = layout.blockingnr; + line_len = blockingnr * context.sector_size; + line_offset = wrtrack_skew * context.sector_size; + + linebuf = malloc(line_len); + if (linebuf == NULL) + return ENOMEM; + + pos = TAILQ_FIRST(&write_queue); + bzero(linebuf, line_len); + + /* + * Always writing out in whole lines now; this is slightly wastefull + * on logical overwrite volumes but it reduces complexity and the loss + * is near zero compared to disc size. + */ + line_start = (pos->sectornr - wrtrack_skew) / blockingnr; + TAILQ_FOREACH(pos, &write_queue, next) { + new_line_start = (pos->sectornr - wrtrack_skew) / blockingnr; + if (new_line_start != line_start) { + /* write out */ + offset = (uint64_t) line_start * line_len + line_offset; +#ifdef DEBUG + printf("WRITEOUT %08"PRIu64" + %02d -- " + "[%08"PRIu64"..%08"PRIu64"]\n", + offset / context.sector_size, blockingnr, + offset / context.sector_size, + offset / context.sector_size + blockingnr-1); +#endif + if (pwrite(fd, linebuf, line_len, offset) < 0) { + perror("Writing failed"); + return errno; + } + line_start = new_line_start; + bzero(linebuf, line_len); + } + + relpos = (pos->sectornr - wrtrack_skew) % blockingnr; + adr = linebuf + relpos * context.sector_size; + memcpy(adr, pos->sector_data, context.sector_size); + } + /* writeout last chunk */ + offset = (uint64_t) line_start * line_len + line_offset; +#ifdef DEBUG + printf("WRITEOUT %08"PRIu64" + %02d -- [%08"PRIu64"..%08"PRIu64"]\n", + offset / context.sector_size, blockingnr, + offset / context.sector_size, + offset / context.sector_size + blockingnr-1); +#endif + if (pwrite(fd, linebuf, line_len, offset) < 0) { + perror("Writing failed"); + return errno; + } + + /* success */ + return 0; +} + +/* --------------------------------------------------------------------- */ + +/* + * mmc_discinfo and mmc_trackinfo readers modified from origional in udf main + * code in sys/fs/udf/ + */ + +#ifdef DEBUG +static void +udf_dump_discinfo(struct mmc_discinfo *di) +{ + char bits[128]; + + printf("Device/media info :\n"); + printf("\tMMC profile 0x%02x\n", di->mmc_profile); + printf("\tderived class %d\n", di->mmc_class); + printf("\tsector size %d\n", di->sector_size); + printf("\tdisc state %d\n", di->disc_state); + printf("\tlast ses state %d\n", di->last_session_state); + printf("\tbg format state %d\n", di->bg_format_state); + printf("\tfrst track %d\n", di->first_track); + printf("\tfst on last ses %d\n", di->first_track_last_session); + printf("\tlst on last ses %d\n", di->last_track_last_session); + printf("\tlink block penalty %d\n", di->link_block_penalty); + snprintb(bits, sizeof(bits), MMC_DFLAGS_FLAGBITS, (uint64_t) di->disc_flags); + printf("\tdisc flags %s\n", bits); + printf("\tdisc id %x\n", di->disc_id); + printf("\tdisc barcode %"PRIx64"\n", di->disc_barcode); + + printf("\tnum sessions %d\n", di->num_sessions); + printf("\tnum tracks %d\n", di->num_tracks); + + snprintb(bits, sizeof(bits), MMC_CAP_FLAGBITS, di->mmc_cur); + printf("\tcapabilities cur %s\n", bits); + snprintb(bits, sizeof(bits), MMC_CAP_FLAGBITS, di->mmc_cap); + printf("\tcapabilities cap %s\n", bits); + printf("\n"); + printf("\tlast_possible_lba %d\n", di->last_possible_lba); + printf("\n"); +} +#else +#define udf_dump_discinfo(a); +#endif + +/* --------------------------------------------------------------------- */ + +static int +udf_update_discinfo(struct mmc_discinfo *di) +{ + struct stat st; + struct disklabel disklab; + struct partition *dp; + off_t size, sectors, secsize; + int partnr, error; + + memset(di, 0, sizeof(struct mmc_discinfo)); + + /* check if we're on a MMC capable device, i.e. CD/DVD */ + error = ioctl(fd, MMCGETDISCINFO, di); + if (error == 0) + return 0; + + /* (re)fstat the file */ + fstat(fd, &st); + + if (S_ISREG(st.st_mode)) { + /* file support; we pick the minimum sector size allowed */ + size = st.st_size; + secsize = imagefile_secsize; + sectors = size / secsize; + } else { + /* + * disc partition support; note we can't use DIOCGPART in + * userland so get disc label and use the stat info to get the + * partition number. + */ + if (ioctl(fd, DIOCGDINFO, &disklab) == -1) { + /* failed to get disclabel! */ + perror("disklabel"); + return errno; + } + + /* get disk partition it refers to */ + fstat(fd, &st); + partnr = DISKPART(st.st_rdev); + dp = &disklab.d_partitions[partnr]; + + /* TODO problem with last_possible_lba on resizable VND */ + if (dp->p_size == 0) { + perror("faulty disklabel partition returned, " + "check label\n"); + return EIO; + } + + sectors = dp->p_size; + secsize = disklab.d_secsize; + } + + /* set up a disc info profile for partitions */ + di->mmc_profile = 0x01; /* disc type */ + di->mmc_class = MMC_CLASS_DISC; + di->disc_state = MMC_STATE_CLOSED; + di->last_session_state = MMC_STATE_CLOSED; + di->bg_format_state = MMC_BGFSTATE_COMPLETED; + di->link_block_penalty = 0; + + di->mmc_cur = MMC_CAP_RECORDABLE | MMC_CAP_REWRITABLE | + MMC_CAP_ZEROLINKBLK | MMC_CAP_HW_DEFECTFREE; + di->mmc_cap = di->mmc_cur; + di->disc_flags = MMC_DFLAGS_UNRESTRICTED; + + di->last_possible_lba = sectors - 1; + di->sector_size = secsize; + + di->num_sessions = 1; + di->num_tracks = 1; + + di->first_track = 1; + di->first_track_last_session = di->last_track_last_session = 1; + + return 0; +} + + +int +udf_update_trackinfo(struct mmc_discinfo *di, struct mmc_trackinfo *ti) +{ + int error, class; + + class = di->mmc_class; + if (class != MMC_CLASS_DISC) { + /* tracknr specified in struct ti */ + error = ioctl(fd, MMCGETTRACKINFO, ti); + return error; + } + + /* discs partition support */ + if (ti->tracknr != 1) + return EIO; + + /* create fake ti (TODO check for resized vnds) */ + ti->sessionnr = 1; + + ti->track_mode = 0; /* XXX */ + ti->data_mode = 0; /* XXX */ + ti->flags = MMC_TRACKINFO_LRA_VALID | MMC_TRACKINFO_NWA_VALID; + + ti->track_start = 0; + ti->packet_size = emul_packetsize; + + /* TODO support for resizable vnd */ + ti->track_size = di->last_possible_lba; + ti->next_writable = di->last_possible_lba; + ti->last_recorded = ti->next_writable; + ti->free_blocks = 0; + + return 0; +} + + +static int +udf_setup_writeparams(struct mmc_discinfo *di) +{ + struct mmc_writeparams mmc_writeparams; + int error; + + if (di->mmc_class == MMC_CLASS_DISC) + return 0; + + /* + * only CD burning normally needs setting up, but other disc types + * might need other settings to be made. The MMC framework will set up + * the nessisary recording parameters according to the disc + * characteristics read in. Modifications can be made in the discinfo + * structure passed to change the nature of the disc. + */ + memset(&mmc_writeparams, 0, sizeof(struct mmc_writeparams)); + mmc_writeparams.mmc_class = di->mmc_class; + mmc_writeparams.mmc_cur = di->mmc_cur; + + /* + * UDF dictates first track to determine track mode for the whole + * disc. [UDF 1.50/6.10.1.1, UDF 1.50/6.10.2.1] + * To prevent problems with a `reserved' track in front we start with + * the 2nd track and if that is not valid, go for the 1st. + */ + mmc_writeparams.tracknr = 2; + mmc_writeparams.data_mode = MMC_DATAMODE_DEFAULT; /* XA disc */ + mmc_writeparams.track_mode = MMC_TRACKMODE_DEFAULT; /* data */ + + error = ioctl(fd, MMCSETUPWRITEPARAMS, &mmc_writeparams); + if (error) { + mmc_writeparams.tracknr = 1; + error = ioctl(fd, MMCSETUPWRITEPARAMS, &mmc_writeparams); + } + return error; +} + + +static void +udf_synchronise_caches(void) +{ + struct mmc_op mmc_op; + + bzero(&mmc_op, sizeof(struct mmc_op)); + mmc_op.operation = MMC_OP_SYNCHRONISECACHE; + + /* this device might not know this ioct, so just be ignorant */ + (void) ioctl(fd, MMCOP, &mmc_op); +} + +/* --------------------------------------------------------------------- */ + +static int +udf_prepare_disc(void) +{ + struct mmc_trackinfo ti; + struct mmc_op op; + int tracknr, error; + + /* If the last track is damaged, repair it */ + ti.tracknr = mmc_discinfo.last_track_last_session; + error = udf_update_trackinfo(&mmc_discinfo, &ti); + if (error) + return error; + + if (ti.flags & MMC_TRACKINFO_DAMAGED) { + /* + * Need to repair last track before anything can be done. + * this is an optional command, so ignore its error but report + * warning. + */ + memset(&op, 0, sizeof(op)); + op.operation = MMC_OP_REPAIRTRACK; + op.mmc_profile = mmc_discinfo.mmc_profile; + op.tracknr = ti.tracknr; + error = ioctl(fd, MMCOP, &op); + + if (error) + (void)printf("Drive can't explicitly repair last " + "damaged track, but it might autorepair\n"); + } + /* last track (if any) might not be damaged now, operations are ok now */ + + /* setup write parameters from discinfo */ + error = udf_setup_writeparams(&mmc_discinfo); + if (error) + return error; + + /* if the drive is not sequential, we're done */ + if ((mmc_discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) == 0) + return 0; + +#ifdef notyet + /* if last track is not the reserved but an empty track, unreserve it */ + if (ti.flags & MMC_TRACKINFO_BLANK) { + if (ti.flags & MMC_TRACKINFO_RESERVED == 0) { + memset(&op, 0, sizeof(op)); + op.operation = MMC_OP_UNRESERVETRACK; + op.mmc_profile = mmc_discinfo.mmc_profile; + op.tracknr = ti.tracknr; + error = ioctl(fd, MMCOP, &op); + if (error) + return error; + + /* update discinfo since it changed by the operation */ + error = udf_update_discinfo(&mmc_discinfo); + if (error) + return error; + } + } +#endif + + /* close the last session if its still open */ + if (mmc_discinfo.last_session_state == MMC_STATE_INCOMPLETE) { + printf("Closing last open session if present\n"); + /* close all associated tracks */ + tracknr = mmc_discinfo.first_track_last_session; + while (tracknr <= mmc_discinfo.last_track_last_session) { + ti.tracknr = tracknr; + error = udf_update_trackinfo(&mmc_discinfo, &ti); + if (error) + return error; + printf("\tClosing open track %d\n", tracknr); + memset(&op, 0, sizeof(op)); + op.operation = MMC_OP_CLOSETRACK; + op.mmc_profile = mmc_discinfo.mmc_profile; + op.tracknr = tracknr; + error = ioctl(fd, MMCOP, &op); + if (error) + return error; + tracknr ++; + } + printf("Closing session\n"); + memset(&op, 0, sizeof(op)); + op.operation = MMC_OP_CLOSESESSION; + op.mmc_profile = mmc_discinfo.mmc_profile; + op.sessionnr = mmc_discinfo.num_sessions; + error = ioctl(fd, MMCOP, &op); + if (error) + return error; + + /* update discinfo since it changed by the operations */ + error = udf_update_discinfo(&mmc_discinfo); + if (error) + return error; + } + + if (format_flags & FORMAT_TRACK512) { + /* get last track again */ + ti.tracknr = mmc_discinfo.last_track_last_session; + error = udf_update_trackinfo(&mmc_discinfo, &ti); + if (error) + return error; + + /* Split up the space at 512 for iso cd9660 hooking */ + memset(&op, 0, sizeof(op)); + op.operation = MMC_OP_RESERVETRACK_NWA; /* UPTO nwa */ + op.mmc_profile = mmc_discinfo.mmc_profile; + op.extent = 512; /* size */ + error = ioctl(fd, MMCOP, &op); + if (error) + return error; + } + + return 0; +} + +/* --------------------------------------------------------------------- */ + +int +udf_surface_check(void) +{ + uint32_t loc, block_bytes; + uint32_t sector_size, blockingnr, bpos; + uint8_t *buffer; + int error, num_errors; + + sector_size = context.sector_size; + blockingnr = layout.blockingnr; + + block_bytes = layout.blockingnr * sector_size; + if ((buffer = malloc(block_bytes)) == NULL) + return ENOMEM; + + /* set all one to not kill Flash memory? */ + for (bpos = 0; bpos < block_bytes; bpos++) + buffer[bpos] = 0x00; + + printf("\nChecking disc surface : phase 1 - writing\n"); + num_errors = 0; + loc = layout.first_lba; + while (loc <= layout.last_lba) { + /* write blockingnr sectors */ + error = pwrite(fd, buffer, block_bytes, loc*sector_size); + printf(" %08d + %d (%02d %%)\r", loc, blockingnr, + (int)((100.0 * loc)/layout.last_lba)); + fflush(stdout); + if (error == -1) { + /* block is bad */ + printf("BAD block at %08d + %d \n", + loc, layout.blockingnr); + if ((error = udf_register_bad_block(loc))) { + free(buffer); + return error; + } + num_errors ++; + } + loc += layout.blockingnr; + } + + printf("\nChecking disc surface : phase 2 - reading\n"); + num_errors = 0; + loc = layout.first_lba; + while (loc <= layout.last_lba) { + /* read blockingnr sectors */ + error = pread(fd, buffer, block_bytes, loc*sector_size); + printf(" %08d + %d (%02d %%)\r", loc, blockingnr, + (int)((100.0 * loc)/layout.last_lba)); + fflush(stdout); + if (error == -1) { + /* block is bad */ + printf("BAD block at %08d + %d \n", + loc, layout.blockingnr); + if ((error = udf_register_bad_block(loc))) { + free(buffer); + return error; + } + num_errors ++; + } + loc += layout.blockingnr; + } + printf("Scan complete : %d bad blocks found\n", num_errors); + free(buffer); + + return 0; +} + + +/* --------------------------------------------------------------------- */ + +static int +udf_do_newfs(void) +{ + int error; + + error = udf_do_newfs_prefix(); + if (error) + return error; + error = udf_do_rootdir(); + if (error) + return error; + error = udf_do_newfs_postfix(); + + return error; +} + + + +/* --------------------------------------------------------------------- */ + +static void +usage(void) +{ + (void)fprintf(stderr, "Usage: %s [-cFM] [-L loglabel] " + "[-P discid] [-S sectorsize] [-s size] [-p perc] " + "[-t gmtoff] [-v min_udf] [-V max_udf] special\n", getprogname()); + exit(EXIT_FAILURE); +} + + +int +main(int argc, char **argv) +{ + struct tm *tm; + struct stat st; + time_t now; + off_t setsize; + char scrap[255], *colon; + int ch, req_enable, req_disable, force; + int error; + + setprogname(argv[0]); + + /* initialise */ + format_str = strdup(""); + req_enable = req_disable = 0; + format_flags = FORMAT_INVALID; + force = 0; + check_surface = 0; + setsize = 0; + imagefile_secsize = 512; /* minimum allowed sector size */ + emul_packetsize = 32; /* reasonable default */ + + srandom((unsigned long) time(NULL)); + udf_init_create_context(); + context.app_name = "*NetBSD newfs"; + context.app_version_main = APP_VERSION_MAIN; + context.app_version_sub = APP_VERSION_SUB; + context.impl_name = IMPL_NAME; + + /* minimum and maximum UDF versions we advise */ + context.min_udf = 0x201; + context.max_udf = 0x201; + + /* use user's time zone as default */ + (void)time(&now); + tm = localtime(&now); + context.gmtoff = tm->tm_gmtoff; + + /* process options */ + while ((ch = getopt(argc, argv, "cFL:Mp:P:s:S:B:t:v:V:")) != -1) { + switch (ch) { + case 'c' : + check_surface = 1; + break; + case 'F' : + force = 1; + break; + case 'L' : + if (context.logvol_name) free(context.logvol_name); + context.logvol_name = strdup(optarg); + break; + case 'M' : + req_disable |= FORMAT_META; + break; + case 'p' : + meta_perc = a_num(optarg, "meta_perc"); + /* limit to `sensible` values */ + meta_perc = MIN(meta_perc, 99); + meta_perc = MAX(meta_perc, 1); + meta_fract = (float) meta_perc/100.0; + break; + case 'v' : + context.min_udf = a_udf_version(optarg, "min_udf"); + if (context.min_udf > context.max_udf) + context.max_udf = context.min_udf; + break; + case 'V' : + context.max_udf = a_udf_version(optarg, "max_udf"); + if (context.min_udf > context.max_udf) + context.min_udf = context.max_udf; + break; + case 'P' : + /* check if there is a ':' in the name */ + if ((colon = strstr(optarg, ":"))) { + if (context.volset_name) + free(context.volset_name); + *colon = 0; + context.volset_name = strdup(optarg); + optarg = colon+1; + } + if (context.primary_name) + free(context.primary_name); + if ((strstr(optarg, ":"))) { + perror("primary name can't have ':' in its name"); + return EXIT_FAILURE; + } + context.primary_name = strdup(optarg); + break; + case 's' : + /* support for files, set file size */ + /* XXX support for formatting recordables on vnd/file? */ + if (dehumanize_number(optarg, &setsize) < 0) { + perror("can't parse size argument"); + return EXIT_FAILURE; + } + setsize = MAX(0, setsize); + break; + case 'S' : + imagefile_secsize = a_num(optarg, "secsize"); + imagefile_secsize = MAX(512, imagefile_secsize); + break; + case 'B' : + emul_packetsize = a_num(optarg, + "blockingnr, packetsize"); + emul_packetsize = MAX(emul_packetsize, 1); + emul_packetsize = MIN(emul_packetsize, 32); + break; + case 't' : + /* time zone overide */ + context.gmtoff = a_num(optarg, "gmtoff"); + break; + default : + usage(); + /* NOTREACHED */ + } + } + + if (optind + 1 != argc) + usage(); + + /* get device and directory specifier */ + dev = argv[optind]; + + /* open device */ + if ((fd = open(dev, O_RDWR, 0)) == -1) { + /* check if we need to create a file */ + fd = open(dev, O_RDONLY, 0); + if (fd > 0) { + perror("device is there but can't be opened for read/write"); + return EXIT_FAILURE; + } + if (!force) { + perror("can't open device"); + return EXIT_FAILURE; + } + if (setsize == 0) { + perror("need to create image file but no size specified"); + return EXIT_FAILURE; + } + /* need to create a file */ + fd = open(dev, O_RDWR | O_CREAT | O_TRUNC, 0777); + if (fd == -1) { + perror("can't create image file"); + return EXIT_FAILURE; + } + } + + /* stat the device */ + if (fstat(fd, &st) != 0) { + perror("can't stat the device"); + close(fd); + return EXIT_FAILURE; + } + + if (S_ISREG(st.st_mode)) { + if (setsize == 0) + setsize = st.st_size; + /* sanitise arguments */ + imagefile_secsize &= ~511; + setsize &= ~(imagefile_secsize-1); + + if (ftruncate(fd, setsize)) { + perror("can't resize file"); + return EXIT_FAILURE; + } + } + + /* formatting can only be done on raw devices */ + if (!S_ISREG(st.st_mode) && !S_ISCHR(st.st_mode)) { + printf("%s is not a raw device\n", dev); + close(fd); + return EXIT_FAILURE; + } + + /* just in case something went wrong, synchronise the drive's cache */ + udf_synchronise_caches(); + + /* get 'disc' information */ + error = udf_update_discinfo(&mmc_discinfo); + if (error) { + perror("can't retrieve discinfo"); + close(fd); + return EXIT_FAILURE; + } + + /* derive disc identifiers when not specified and check given */ + error = udf_proces_names(); + if (error) { + /* error message has been printed */ + close(fd); + return EXIT_FAILURE; + } + + /* derive newfs disc format from disc profile */ + error = udf_derive_format(req_enable, req_disable, force); + if (error) { + /* error message has been printed */ + close(fd); + return EXIT_FAILURE; + } + + udf_dump_discinfo(&mmc_discinfo); + printf("Formatting disc compatible with UDF version %x to %x\n\n", + context.min_udf, context.max_udf); + (void)snprintb(scrap, sizeof(scrap), FORMAT_FLAGBITS, + (uint64_t) format_flags); + printf("UDF properties %s\n", scrap); + printf("Volume set `%s'\n", context.volset_name); + printf("Primary volume `%s`\n", context.primary_name); + printf("Logical volume `%s`\n", context.logvol_name); + if (format_flags & FORMAT_META) + printf("Metadata percentage %d %%\n", meta_perc); + printf("\n"); + + /* prepare disc if nessisary (recordables mainly) */ + error = udf_prepare_disc(); + if (error) { + perror("preparing disc failed"); + close(fd); + return EXIT_FAILURE; + }; + + /* setup sector writeout queue's */ + TAILQ_INIT(&write_queue); + + /* perform the newfs itself */ + error = udf_do_newfs(); + if (!error) { + /* write out sectors */ + error = writeout_write_queue(); + } + + /* in any case, synchronise the drive's cache to prevent lockups */ + udf_synchronise_caches(); + + close(fd); + if (error) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} + +/* --------------------------------------------------------------------- */ + diff --git a/sbin/newfs_udf/newfs_udf.h b/sbin/newfs_udf/newfs_udf.h new file mode 100644 index 000000000..1c21a0b48 --- /dev/null +++ b/sbin/newfs_udf/newfs_udf.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2006, 2008, 2013 Reinoud Zandijk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _FS_UDF_NEWFS_UDF_H_ +#define _FS_UDF_NEWFS_UDF_H_ + +/* general settings */ +#define UDF_512_TRACK 0 /* NOT recommended */ +#define UDF_META_PERC 20 /* picked */ + +/* Identifying myself */ +#define APP_VERSION_MAIN 0 +#define APP_VERSION_SUB 5 +#define IMPL_NAME "*NetBSD userland UDF" + + +/* global variables describing disc and format requests */ +extern int fd; /* device: file descriptor */ +extern char *dev; /* device: name */ +extern struct mmc_discinfo mmc_discinfo;/* device: disc info */ + +extern char *format_str; /* format: string representation */ +extern int format_flags; /* format: attribute flags */ +extern int media_accesstype; /* derived from current mmc cap */ +extern int check_surface; /* for rewritables */ + +extern int wrtrack_skew; +extern int meta_perc; +extern float meta_fract; + + +/* shared structure between udf_create.c users */ +struct udf_create_context context; +struct udf_disclayout layout; + +/* prototypes */ +int udf_write_sector(void *sector, uint64_t location); +int udf_update_trackinfo(struct mmc_discinfo *di, struct mmc_trackinfo *ti); + +/* tmp */ +int writeout_write_queue(void); +int udf_surface_check(void); + +#endif /* _FS_UDF_UDF_WRITE_H_ */ diff --git a/sbin/newfs_udf/udf_create.c b/sbin/newfs_udf/udf_create.c new file mode 100644 index 000000000..e40c70868 --- /dev/null +++ b/sbin/newfs_udf/udf_create.c @@ -0,0 +1,2392 @@ +/* $NetBSD: udf_create.c,v 1.24 2013/10/19 01:09:59 christos Exp $ */ + +/* + * Copyright (c) 2006, 2008 Reinoud Zandijk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__RCSID("$NetBSD: udf_create.c,v 1.24 2013/10/19 01:09:59 christos Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "unicode.h" +#include "udf_create.h" + + +#if 0 +# ifndef DEBUG +# define DEBUG +# endif +#endif + +/* + * NOTE that there is some overlap between this code and the udf kernel fs. + * This is intentially though it might better be factored out one day. + */ + +void +udf_init_create_context(void) +{ + /* clear */ + memset(&context, 0, sizeof(struct udf_create_context)); + + /* fill with defaults currently known */ + context.dscrver = 3; + context.min_udf = 0x0102; + context.max_udf = 0x0260; + context.serialnum = 1; /* default */ + + context.gmtoff = 0; + context.sector_size = 512; /* minimum for UDF */ + + context.logvol_name = NULL; + context.primary_name = NULL; + context.volset_name = NULL; + context.fileset_name = NULL; + + /* most basic identification */ + context.app_name = "*NetBSD"; + context.app_version_main = 0; + context.app_version_sub = 0; + context.impl_name = "*NetBSD"; + + context.vds_seq = 0; /* first one starts with zero */ + + /* Minimum value of 16 : UDF 3.2.1.1, 3.3.3.4. */ + context.unique_id = 0x10; + + context.num_files = 0; + context.num_directories = 0; + + context.data_part = 0; + context.metadata_part = 0; + context.metadata_alloc_pos = 0; + context.data_alloc_pos = 0; +} + + +/* version can be specified as 0xabc or a.bc */ +static int +parse_udfversion(const char *pos, uint32_t *version) { + int hex = 0; + char c1, c2, c3, c4; + + *version = 0; + if (*pos == '0') { + pos++; + /* expect hex format */ + hex = 1; + if (*pos++ != 'x') + return 1; + } + + c1 = *pos++; + if (c1 < '0' || c1 > '9') + return 1; + c1 -= '0'; + + c2 = *pos++; + if (!hex) { + if (c2 != '.') + return 1; + c2 = *pos++; + } + if (c2 < '0' || c2 > '9') + return 1; + c2 -= '0'; + + c3 = *pos++; + if (c3 < '0' || c3 > '9') + return 1; + c3 -= '0'; + + c4 = *pos++; + if (c4 != 0) + return 1; + + *version = c1 * 0x100 + c2 * 0x10 + c3; + return 0; +} + + +/* parse a given string for an udf version */ +int +a_udf_version(const char *s, const char *id_type) +{ + uint32_t version; + + if (parse_udfversion(s, &version)) + errx(1, "unknown %s id %s; specify as hex or float", id_type, s); + return version; +} + + +static uint32_t +udf_space_bitmap_len(uint32_t part_size) +{ + return sizeof(struct space_bitmap_desc)-1 + + part_size/8; +} + + +static uint32_t +udf_bytes_to_sectors(uint64_t bytes) +{ + uint32_t sector_size = layout.sector_size; + return (bytes + sector_size -1) / sector_size; +} + + +int +udf_calculate_disc_layout(int format_flags, int min_udf, + uint32_t wrtrack_skew, + uint32_t first_lba, uint32_t last_lba, + uint32_t sector_size, uint32_t blockingnr, + uint32_t sparable_blocks, float meta_fract) +{ + uint64_t kbsize, bytes; + uint32_t sparable_blockingnr; + uint32_t align_blockingnr; + uint32_t pos, mpos; + + /* clear */ + memset(&layout, 0, sizeof(layout)); + + /* fill with parameters */ + layout.wrtrack_skew = wrtrack_skew; + layout.first_lba = first_lba; + layout.last_lba = last_lba; + layout.sector_size = sector_size; + layout.blockingnr = blockingnr; + layout.sparable_blocks = sparable_blocks; + + /* start disc layouting */ + + /* + * location of iso9660 vrs is defined as first sector AFTER 32kb, + * minimum `sector size' 2048 + */ + layout.iso9660_vrs = ((32*1024 + sector_size - 1) / sector_size) + + first_lba; + + /* anchor starts at specified offset in sectors */ + layout.anchors[0] = first_lba + 256; + if (format_flags & FORMAT_TRACK512) + layout.anchors[0] = first_lba + 512; + layout.anchors[1] = last_lba - 256; + layout.anchors[2] = last_lba; + + /* update workable space */ + first_lba = layout.anchors[0] + blockingnr; + last_lba = layout.anchors[1] - 1; + + /* XXX rest of anchor packet can be added to unallocated space descr */ + + /* reserve space for VRS and VRS copy and associated tables */ + layout.vds_size = MAX(16, blockingnr); /* UDF 2.2.3.1+2 */ + layout.vds1 = first_lba; + first_lba += layout.vds_size; /* next packet */ + + if (format_flags & FORMAT_SEQUENTIAL) { + /* for sequential, append them ASAP */ + layout.vds2 = first_lba; + first_lba += layout.vds_size; + } else { + layout.vds2 = layout.anchors[1] - layout.vds_size; + last_lba = layout.vds2 - 1; /* XXX -1 ?? */ + } + + /* reserve space for logvol integrity sequence */ + layout.lvis_size = MAX(8192/sector_size, 2 * blockingnr); + if (format_flags & FORMAT_VAT) + layout.lvis_size = 2; + if (format_flags & FORMAT_WORM) + layout.lvis_size = 64 * blockingnr; + + /* TODO skip bad blocks in LVID sequence; for now use f.e. */ +//first_lba+=128; + layout.lvis = first_lba; + first_lba += layout.lvis_size; + + /* initial guess of UDF partition size */ + layout.part_start_lba = first_lba; + layout.part_size_lba = last_lba - layout.part_start_lba; + + /* all non sequential media needs an unallocated space bitmap */ + layout.alloc_bitmap_dscr_size = 0; + if ((format_flags & (FORMAT_SEQUENTIAL | FORMAT_READONLY)) == 0) { + bytes = udf_space_bitmap_len(layout.part_size_lba); + layout.alloc_bitmap_dscr_size = udf_bytes_to_sectors(bytes); + + /* XXX freed space map when applicable */ + } + + /* + * Note that for (bug) compatibility with version UDF 2.00 (fixed in + * 2.01 and higher) the blocking size needs to be 32 sectors otherwise + * the drive's blockingnr. + */ + + sparable_blockingnr = blockingnr; + if (min_udf <= 0x200) + sparable_blockingnr = 32; + + align_blockingnr = blockingnr; + if (format_flags & (FORMAT_SPARABLE | FORMAT_META)) + align_blockingnr = sparable_blockingnr; + + layout.align_blockingnr = align_blockingnr; + layout.sparable_blockingnr = sparable_blockingnr; + + /* + * Align partition LBA space to blocking granularity. Not strickly + * nessisary for non sparables but safer for the VRS data since it is + * not updated sporadically + */ + + if ((format_flags & (FORMAT_SEQUENTIAL | FORMAT_READONLY)) == 0) { +#ifdef DEBUG + printf("Lost %d slack sectors at start\n", UDF_ROUNDUP( + first_lba - wrtrack_skew, align_blockingnr) - + (first_lba - wrtrack_skew)); + printf("Lost %d slack sectors at end\n", + (first_lba - wrtrack_skew) - UDF_ROUNDDOWN( + first_lba - wrtrack_skew, align_blockingnr)); +#endif + + first_lba = UDF_ROUNDUP( first_lba - wrtrack_skew, + align_blockingnr); + last_lba = UDF_ROUNDDOWN(last_lba - wrtrack_skew, + align_blockingnr); + } + + if ((format_flags & FORMAT_SPARABLE) == 0) + layout.sparable_blocks = 0; + + if (format_flags & FORMAT_SPARABLE) { + layout.sparable_area_size = + layout.sparable_blocks * sparable_blockingnr; + + /* a sparing table descriptor is a whole blockingnr sectors */ + layout.sparing_table_dscr_lbas = sparable_blockingnr; + + /* place the descriptors at the start and end of the area */ + layout.spt_1 = first_lba; + first_lba += layout.sparing_table_dscr_lbas; + + layout.spt_2 = last_lba - layout.sparing_table_dscr_lbas; + last_lba -= layout.sparing_table_dscr_lbas; + + /* allocate sparable section */ + layout.sparable_area = first_lba; + first_lba += layout.sparable_area_size; + } + + /* update guess of UDF partition size */ + layout.part_start_lba = first_lba; + layout.part_size_lba = last_lba - layout.part_start_lba; + + /* determine partition selection for data and metadata */ + context.data_part = 0; + context.metadata_part = context.data_part; + if ((format_flags & FORMAT_VAT) || (format_flags & FORMAT_META)) + context.metadata_part = context.data_part + 1; + + /* + * Pick fixed logical space sector numbers for main FSD, rootdir and + * unallocated space. The reason for this pre-allocation is that they + * are referenced in the volume descriptor sequence and hence can't be + * allocated later. + */ + pos = 0; + layout.unalloc_space = pos; + pos += layout.alloc_bitmap_dscr_size; + + /* claim metadata descriptors and partition space [UDF 2.2.10] */ + if (format_flags & FORMAT_META) { + /* note: all in backing partition space */ + layout.meta_file = pos++; + layout.meta_bitmap = pos++;; + layout.meta_mirror = layout.part_size_lba-1; + layout.meta_alignment = MAX(blockingnr, sparable_blockingnr); + layout.meta_blockingnr = MAX(layout.meta_alignment, 32); + + /* calculate our partition length and store in sectors */ + layout.meta_part_size_lba = layout.part_size_lba * meta_fract; + layout.meta_part_size_lba = MAX(layout.meta_part_size_lba, 32); + layout.meta_part_size_lba = + UDF_ROUNDDOWN(layout.meta_part_size_lba, layout.meta_blockingnr); + + /* calculate positions */ + bytes = udf_space_bitmap_len(layout.meta_part_size_lba); + layout.meta_bitmap_dscr_size = udf_bytes_to_sectors(bytes); + + layout.meta_bitmap_space = pos; + pos += layout.meta_bitmap_dscr_size; + + layout.meta_part_start_lba = UDF_ROUNDUP(pos, layout.meta_alignment); + } + + mpos = (context.metadata_part == context.data_part) ? pos : 0; + layout.fsd = mpos; mpos += 1; + layout.rootdir = mpos; mpos += 1; + layout.vat = mpos; mpos += 1; /* if present */ + +#if 0 + printf("Summary so far\n"); + printf("\tiso9660_vrs\t\t%d\n", layout.iso9660_vrs); + printf("\tanchor0\t\t\t%d\n", layout.anchors[0]); + printf("\tanchor1\t\t\t%d\n", layout.anchors[1]); + printf("\tanchor2\t\t\t%d\n", layout.anchors[2]); + printf("\tvds_size\t\t%d\n", layout.vds_size); + printf("\tvds1\t\t\t%d\n", layout.vds1); + printf("\tvds2\t\t\t%d\n", layout.vds2); + printf("\tlvis_size\t\t%d\n", layout.lvis_size); + printf("\tlvis\t\t\t%d\n", layout.lvis); + if (format_flags & FORMAT_SPARABLE) { + printf("\tsparable size\t\t%d\n", layout.sparable_area_size); + printf("\tsparable\t\t%d\n", layout.sparable_area); + } + printf("\tpartition start lba\t%d\n", layout.part_start_lba); + printf("\tpartition size\t\t%d KiB, %d MiB\n", + (layout.part_size_lba * sector_size) / 1024, + (layout.part_size_lba * sector_size) / (1024*1024)); + if ((format_flags & FORMAT_SEQUENTIAL) == 0) { + printf("\tpart bitmap start\t%d\n", layout.unalloc_space); + printf("\t\tfor %d lba\n", layout.alloc_bitmap_dscr_size); + } + if (format_flags & FORMAT_META) { + printf("\tmeta blockingnr\t\t%d\n", layout.meta_blockingnr); + printf("\tmeta alignment\t\t%d\n", layout.meta_alignment); + printf("\tmeta size\t\t%d KiB, %d MiB\n", + (layout.meta_part_size_lba * sector_size) / 1024, + (layout.meta_part_size_lba * sector_size) / (1024*1024)); + printf("\tmeta file\t\t%d\n", layout.meta_file); + printf("\tmeta mirror\t\t%d\n", layout.meta_mirror); + printf("\tmeta bitmap\t\t%d\n", layout.meta_bitmap); + printf("\tmeta bitmap start\t%d\n", layout.meta_bitmap_space); + printf("\t\tfor %d lba\n", layout.meta_bitmap_dscr_size); + printf("\tmeta space start\t%d\n", layout.meta_part_start_lba); + printf("\t\tfor %d lba\n", layout.meta_part_size_lba); + } + printf("\n"); +#endif + + kbsize = (uint64_t) last_lba * sector_size; + printf("Total space on this medium approx. " + "%"PRIu64" KiB, %"PRIu64" MiB\n", + kbsize/1024, kbsize/(1024*1024)); + kbsize = (uint64_t)(layout.part_size_lba - layout.alloc_bitmap_dscr_size + - layout.meta_bitmap_dscr_size) * sector_size; + printf("Free space on this volume approx. " + "%"PRIu64" KiB, %"PRIu64" MiB\n\n", + kbsize/1024, kbsize/(1024*1024)); + + return 0; +} + + +int +udf_validate_tag_sum(union dscrptr *dscr) +{ + struct desc_tag *tag = &dscr->tag; + uint8_t *pos, sum, cnt; + + /* calculate TAG header checksum */ + pos = (uint8_t *) tag; + sum = 0; + + for(cnt = 0; cnt < 16; cnt++) { + if (cnt != 4) sum += *pos; + pos++; + }; + tag->cksum = sum; /* 8 bit */ + + return 0; +} + + +/* assumes sector number of descriptor to be allready present */ +int +udf_validate_tag_and_crc_sums(union dscrptr *dscr) +{ + struct desc_tag *tag = &dscr->tag; + uint16_t crc; + + /* check payload CRC if applicable */ + if (udf_rw16(tag->desc_crc_len) > 0) { + crc = udf_cksum(((uint8_t *) tag) + UDF_DESC_TAG_LENGTH, + udf_rw16(tag->desc_crc_len)); + tag->desc_crc = udf_rw16(crc); + }; + + /* calculate TAG header checksum */ + return udf_validate_tag_sum(dscr); +} + + +void +udf_inittag(struct desc_tag *tag, int tagid, uint32_t loc) +{ + tag->id = udf_rw16(tagid); + tag->descriptor_ver = udf_rw16(context.dscrver); + tag->cksum = 0; + tag->reserved = 0; + tag->serial_num = udf_rw16(context.serialnum); + tag->tag_loc = udf_rw32(loc); +} + + +int +udf_create_anchor(int num) +{ + struct anchor_vdp *avdp; + uint32_t vds_extent_len = layout.vds_size * context.sector_size; + + if ((avdp = calloc(1, context.sector_size)) == NULL) + return ENOMEM; + + udf_inittag(&avdp->tag, TAGID_ANCHOR, layout.anchors[num]); + + avdp->main_vds_ex.loc = udf_rw32(layout.vds1); + avdp->main_vds_ex.len = udf_rw32(vds_extent_len); + + avdp->reserve_vds_ex.loc = udf_rw32(layout.vds2); + avdp->reserve_vds_ex.len = udf_rw32(vds_extent_len); + + /* CRC length for an anchor is 512 - tag length; defined in Ecma 167 */ + avdp->tag.desc_crc_len = udf_rw16(512-UDF_DESC_TAG_LENGTH); + + context.anchors[num] = avdp; + return 0; +} + + +void +udf_create_terminator(union dscrptr *dscr, uint32_t loc) +{ + memset(dscr, 0, context.sector_size); + udf_inittag(&dscr->tag, TAGID_TERM, loc); + + /* CRC length for an anchor is 512 - tag length; defined in Ecma 167 */ + dscr->tag.desc_crc_len = udf_rw16(512-UDF_DESC_TAG_LENGTH); +} + + +void +udf_osta_charset(struct charspec *charspec) +{ + memset(charspec, 0, sizeof(*charspec)); + charspec->type = 0; + strcpy((char *) charspec->inf, "OSTA Compressed Unicode"); +} + + +void +udf_encode_osta_id(char *osta_id, uint16_t len, char *text) +{ + uint16_t u16_name[1024]; + uint8_t *pos; + uint16_t *pos16; + + memset(osta_id, 0, len); + if (!text || (strlen(text) == 0)) return; + + memset(u16_name, 0, sizeof(uint16_t) * 1023); + + /* convert ascii to 16 bits unicode */ + pos = (uint8_t *) text; + pos16 = u16_name; + while (*pos) { + *pos16 = *pos; + pos++; pos16++; + }; + *pos16 = 0; + + udf_CompressUnicode(len, 8, (unicode_t *) u16_name, (byte *) osta_id); + + /* Ecma 167/7.2.13 states that length is recorded in the last byte */ + osta_id[len-1] = strlen(text)+1; +} + + +/* first call udf_set_regid and then the suffix */ +void +udf_set_regid(struct regid *regid, char const *name) +{ + memset(regid, 0, sizeof(*regid)); + regid->flags = 0; /* not dirty and not protected */ + strcpy((char *) regid->id, name); +} + + +void +udf_add_domain_regid(struct regid *regid) +{ + uint16_t *ver; + + ver = (uint16_t *) regid->id_suffix; + *ver = udf_rw16(context.min_udf); +} + + +void +udf_add_udf_regid(struct regid *regid) +{ + uint16_t *ver; + + ver = (uint16_t *) regid->id_suffix; + *ver = udf_rw16(context.min_udf); + + regid->id_suffix[2] = 4; /* unix */ + regid->id_suffix[3] = 8; /* NetBSD */ +} + + +void +udf_add_impl_regid(struct regid *regid) +{ + regid->id_suffix[0] = 4; /* unix */ + regid->id_suffix[1] = 8; /* NetBSD */ +} + + +void +udf_add_app_regid(struct regid *regid) +{ + regid->id_suffix[0] = context.app_version_main; + regid->id_suffix[1] = context.app_version_sub; +} + + +/* + * Fill in timestamp structure based on clock_gettime(). Time is reported back + * as a time_t accompanied with a nano second field. + * + * The husec, usec and csec could be relaxed in type. + */ +static void +udf_timespec_to_timestamp(struct timespec *timespec, struct timestamp *timestamp) +{ + struct tm tm; + uint64_t husec, usec, csec; + + memset(timestamp, 0, sizeof(*timestamp)); + gmtime_r(×pec->tv_sec, &tm); + + /* + * Time type and time zone : see ECMA 1/7.3, UDF 2., 2.1.4.1, 3.1.1. + * + * Lower 12 bits are two complement signed timezone offset if bit 12 + * (method 1) is clear. Otherwise if bit 12 is set, specify timezone + * offset to -2047 i.e. unsigned `zero' + */ + + /* set method 1 for CUT/GMT */ + timestamp->type_tz = udf_rw16((1<<12) + 0); + timestamp->year = udf_rw16(tm.tm_year + 1900); + timestamp->month = tm.tm_mon + 1; /* `tm' uses 0..11 for months */ + timestamp->day = tm.tm_mday; + timestamp->hour = tm.tm_hour; + timestamp->minute = tm.tm_min; + timestamp->second = tm.tm_sec; + + usec = (timespec->tv_nsec + 500) / 1000; /* round */ + husec = usec / 100; + usec -= husec * 100; /* only 0-99 in usec */ + csec = husec / 100; /* only 0-99 in csec */ + husec -= csec * 100; /* only 0-99 in husec */ + + /* in rare cases there is overflow in csec */ + csec = MIN(99, csec); + husec = MIN(99, husec); + usec = MIN(99, usec); + + timestamp->centisec = csec; + timestamp->hund_usec = husec; + timestamp->usec = usec; +} + + +void +udf_set_timestamp_now(struct timestamp *timestamp) +{ + struct timespec now; + +#ifdef CLOCK_REALTIME + (void)clock_gettime(CLOCK_REALTIME, &now); +#else + struct timeval time_of_day; + + (void)gettimeofday(&time_of_day, NULL); + now.tv_sec = time_of_day.tv_sec; + now.tv_nsec = time_of_day.tv_usec * 1000; +#endif + udf_timespec_to_timestamp(&now, timestamp); +} + + +/* some code copied from sys/fs/udf */ + +static void +udf_set_timestamp(struct timestamp *timestamp, time_t value) +{ + struct timespec t; + + memset(&t, 0, sizeof(struct timespec)); + t.tv_sec = value; + t.tv_nsec = 0; + udf_timespec_to_timestamp(&t, timestamp); +} + + +static uint32_t +unix_mode_to_udf_perm(mode_t mode) +{ + uint32_t perm; + + perm = ((mode & S_IRWXO) ); + perm |= ((mode & S_IRWXG) << 2); + perm |= ((mode & S_IRWXU) << 4); + perm |= ((mode & S_IWOTH) << 3); + perm |= ((mode & S_IWGRP) << 5); + perm |= ((mode & S_IWUSR) << 7); + + return perm; +} + +/* end of copied code */ + + +int +udf_create_primaryd(void) +{ + struct pri_vol_desc *pri; + uint16_t crclen; + + pri = calloc(1, context.sector_size); + if (pri == NULL) + return ENOMEM; + + memset(pri, 0, context.sector_size); + udf_inittag(&pri->tag, TAGID_PRI_VOL, /* loc */ 0); + pri->seq_num = udf_rw32(context.vds_seq); context.vds_seq++; + + pri->pvd_num = udf_rw32(0); /* default serial */ + udf_encode_osta_id(pri->vol_id, 32, context.primary_name); + + /* set defaults for single disc volumes as UDF prescribes */ + pri->vds_num = udf_rw16(1); + pri->max_vol_seq = udf_rw16(1); + pri->ichg_lvl = udf_rw16(2); + pri->max_ichg_lvl = udf_rw16(3); + pri->flags = udf_rw16(0); + + pri->charset_list = udf_rw32(1); /* only CS0 */ + pri->max_charset_list = udf_rw32(1); /* only CS0 */ + + udf_encode_osta_id(pri->volset_id, 128, context.volset_name); + udf_osta_charset(&pri->desc_charset); + udf_osta_charset(&pri->explanatory_charset); + + udf_set_regid(&pri->app_id, context.app_name); + udf_add_app_regid(&pri->app_id); + + udf_set_regid(&pri->imp_id, context.impl_name); + udf_add_impl_regid(&pri->imp_id); + + udf_set_timestamp_now(&pri->time); + + crclen = sizeof(struct pri_vol_desc) - UDF_DESC_TAG_LENGTH; + pri->tag.desc_crc_len = udf_rw16(crclen); + + context.primary_vol = pri; + + return 0; +} + + +/* XXX no support for unallocated or freed space tables yet (!) */ +int +udf_create_partitiond(int part_num, int part_accesstype) +{ + struct part_desc *pd; + struct part_hdr_desc *phd; + uint32_t sector_size, bitmap_bytes; + uint16_t crclen; + + sector_size = context.sector_size; + bitmap_bytes = layout.alloc_bitmap_dscr_size * sector_size; + + if (context.partitions[part_num]) { + printf("Internal error: partition %d allready defined\n", + part_num); + return EINVAL; + } + + pd = calloc(1, context.sector_size); + if (pd == NULL) + return ENOMEM; + phd = &pd->_impl_use.part_hdr; + + udf_inittag(&pd->tag, TAGID_PARTITION, /* loc */ 0); + pd->seq_num = udf_rw32(context.vds_seq); context.vds_seq++; + + pd->flags = udf_rw16(1); /* allocated */ + pd->part_num = udf_rw16(part_num); /* only one physical partition */ + + if (context.dscrver == 2) { + udf_set_regid(&pd->contents, "+NSR02"); + } else { + udf_set_regid(&pd->contents, "+NSR03"); + } + udf_add_app_regid(&pd->contents); + + phd->unalloc_space_bitmap.len = udf_rw32(bitmap_bytes); + phd->unalloc_space_bitmap.lb_num = udf_rw32(layout.unalloc_space); + + if (layout.freed_space) { + phd->freed_space_bitmap.len = udf_rw32(bitmap_bytes); + phd->freed_space_bitmap.lb_num = udf_rw32(layout.freed_space); + } + + pd->access_type = udf_rw32(part_accesstype); + pd->start_loc = udf_rw32(layout.part_start_lba); + pd->part_len = udf_rw32(layout.part_size_lba); + + udf_set_regid(&pd->imp_id, context.impl_name); + udf_add_impl_regid(&pd->imp_id); + + crclen = sizeof(struct part_desc) - UDF_DESC_TAG_LENGTH; + pd->tag.desc_crc_len = udf_rw16(crclen); + + context.partitions[part_num] = pd; + + return 0; +} + + +int +udf_create_unalloc_spaced(void) +{ + struct unalloc_sp_desc *usd; + uint16_t crclen; + + usd = calloc(1, context.sector_size); + if (usd == NULL) + return ENOMEM; + + udf_inittag(&usd->tag, TAGID_UNALLOC_SPACE, /* loc */ 0); + usd->seq_num = udf_rw32(context.vds_seq); context.vds_seq++; + + /* no default entries */ + usd->alloc_desc_num = udf_rw32(0); /* no entries */ + + crclen = sizeof(struct unalloc_sp_desc) - sizeof(struct extent_ad); + crclen -= UDF_DESC_TAG_LENGTH; + usd->tag.desc_crc_len = udf_rw16(crclen); + + context.unallocated = usd; + + return 0; +} + + +static int +udf_create_base_logical_dscr(void) +{ + struct logvol_desc *lvd; + uint32_t sector_size; + uint16_t crclen; + + sector_size = context.sector_size; + + lvd = calloc(1, sector_size); + if (lvd == NULL) + return ENOMEM; + + udf_inittag(&lvd->tag, TAGID_LOGVOL, /* loc */ 0); + lvd->seq_num = udf_rw32(context.vds_seq); context.vds_seq++; + + udf_osta_charset(&lvd->desc_charset); + udf_encode_osta_id(lvd->logvol_id, 128, context.logvol_name); + lvd->lb_size = udf_rw32(context.sector_size); + + udf_set_regid(&lvd->domain_id, "*OSTA UDF Compliant"); + udf_add_domain_regid(&lvd->domain_id); + + /* no partition mappings/entries yet */ + lvd->mt_l = udf_rw32(0); + lvd->n_pm = udf_rw32(0); + + udf_set_regid(&lvd->imp_id, context.impl_name); + udf_add_impl_regid(&lvd->imp_id); + + lvd->integrity_seq_loc.loc = udf_rw32(layout.lvis); + lvd->integrity_seq_loc.len = udf_rw32(layout.lvis_size * sector_size); + + /* just one fsd for now */ + lvd->lv_fsd_loc.len = udf_rw32(sector_size); + lvd->lv_fsd_loc.loc.part_num = udf_rw32(context.metadata_part); + lvd->lv_fsd_loc.loc.lb_num = udf_rw32(layout.fsd); + + crclen = sizeof(struct logvol_desc) - 1 - UDF_DESC_TAG_LENGTH; + lvd->tag.desc_crc_len = udf_rw16(crclen); + + context.logical_vol = lvd; + context.vtop_tp[UDF_VTOP_RAWPART] = UDF_VTOP_TYPE_RAW; + context.vtop_offset[UDF_VTOP_RAWPART] = 0; + + return 0; +} + + +static void +udf_add_logvol_part_physical(uint16_t phys_part) +{ + struct logvol_desc *logvol = context.logical_vol; + union udf_pmap *pmap; + uint8_t *pmap_pos; + uint16_t crclen; + uint32_t pmap1_size, log_part; + + log_part = udf_rw32(logvol->n_pm); + pmap_pos = logvol->maps + udf_rw32(logvol->mt_l); + pmap1_size = sizeof(struct part_map_1); + + pmap = (union udf_pmap *) pmap_pos; + pmap->pm1.type = 1; + pmap->pm1.len = sizeof(struct part_map_1); + pmap->pm1.vol_seq_num = udf_rw16(1); /* no multi-volume */ + pmap->pm1.part_num = udf_rw16(phys_part); + + context.vtop [log_part] = phys_part; + context.vtop_tp [log_part] = UDF_VTOP_TYPE_PHYS; + context.vtop_offset[log_part] = layout.part_start_lba; + context.part_size[log_part] = layout.part_size_lba; + context.part_free[log_part] = layout.part_size_lba; + + /* increment number of partitions and length */ + logvol->n_pm = udf_rw32(log_part + 1); + logvol->mt_l = udf_rw32(udf_rw32(logvol->mt_l) + pmap1_size); + + crclen = udf_rw16(logvol->tag.desc_crc_len) + pmap1_size; + logvol->tag.desc_crc_len = udf_rw16(crclen); +} + + +static void +udf_add_logvol_part_virtual(uint16_t phys_part) +{ + union udf_pmap *pmap; + struct logvol_desc *logvol = context.logical_vol; + uint8_t *pmap_pos; + uint16_t crclen; + uint32_t pmapv_size, log_part; + + log_part = udf_rw32(logvol->n_pm); + pmap_pos = logvol->maps + udf_rw32(logvol->mt_l); + pmapv_size = sizeof(struct part_map_2); + + pmap = (union udf_pmap *) pmap_pos; + pmap->pmv.type = 2; + pmap->pmv.len = pmapv_size; + + udf_set_regid(&pmap->pmv.id, "*UDF Virtual Partition"); + udf_add_udf_regid(&pmap->pmv.id); + + pmap->pmv.vol_seq_num = udf_rw16(1); /* no multi-volume */ + pmap->pmv.part_num = udf_rw16(phys_part); + + context.vtop [log_part] = phys_part; + context.vtop_tp [log_part] = UDF_VTOP_TYPE_VIRT; + context.vtop_offset[log_part] = context.vtop_offset[phys_part]; + context.part_size[log_part] = 0xffffffff; + context.part_free[log_part] = 0xffffffff; + + /* increment number of partitions and length */ + logvol->n_pm = udf_rw32(log_part + 1); + logvol->mt_l = udf_rw32(udf_rw32(logvol->mt_l) + pmapv_size); + + crclen = udf_rw16(logvol->tag.desc_crc_len) + pmapv_size; + logvol->tag.desc_crc_len = udf_rw16(crclen); +} + + +/* sparing table size is in bytes */ +static void +udf_add_logvol_part_sparable(uint16_t phys_part) +{ + union udf_pmap *pmap; + struct logvol_desc *logvol = context.logical_vol; + uint32_t *st_pos, sparable_bytes, pmaps_size; + uint8_t *pmap_pos, num; + uint16_t crclen; + uint32_t log_part; + + log_part = udf_rw32(logvol->n_pm); + pmap_pos = logvol->maps + udf_rw32(logvol->mt_l); + pmaps_size = sizeof(struct part_map_2); + sparable_bytes = layout.sparable_area_size * context.sector_size; + + pmap = (union udf_pmap *) pmap_pos; + pmap->pms.type = 2; + pmap->pms.len = pmaps_size; + + udf_set_regid(&pmap->pmv.id, "*UDF Sparable Partition"); + udf_add_udf_regid(&pmap->pmv.id); + + pmap->pms.vol_seq_num = udf_rw16(1); /* no multi-volume */ + pmap->pms.part_num = udf_rw16(phys_part); + + pmap->pms.packet_len = udf_rw16(layout.sparable_blockingnr); + pmap->pms.st_size = udf_rw32(sparable_bytes); + + /* enter spare tables */ + st_pos = &pmap->pms.st_loc[0]; + *st_pos++ = udf_rw32(layout.spt_1); + *st_pos++ = udf_rw32(layout.spt_2); + + num = 2; + if (layout.spt_2 == 0) num--; + if (layout.spt_1 == 0) num--; + pmap->pms.n_st = num; /* 8 bit */ + + /* the vtop_offset needs to explicitly set since there is no phys. */ + context.vtop [log_part] = phys_part; + context.vtop_tp [log_part] = UDF_VTOP_TYPE_SPARABLE; + context.vtop_offset[log_part] = layout.part_start_lba; + context.part_size[log_part] = layout.part_size_lba; + context.part_free[log_part] = layout.part_size_lba; + + /* increment number of partitions and length */ + logvol->n_pm = udf_rw32(log_part + 1); + logvol->mt_l = udf_rw32(udf_rw32(logvol->mt_l) + pmaps_size); + + crclen = udf_rw16(logvol->tag.desc_crc_len) + pmaps_size; + logvol->tag.desc_crc_len = udf_rw16(crclen); +} + + +int +udf_create_sparing_tabled(void) +{ + struct udf_sparing_table *spt; + struct spare_map_entry *sme; + uint32_t loc, cnt; + uint32_t crclen; /* XXX: should be 16; need to detect overflow */ + + spt = calloc(context.sector_size, layout.sparing_table_dscr_lbas); + if (spt == NULL) + return ENOMEM; + + /* a sparing table descriptor is a whole sparable_blockingnr sectors */ + udf_inittag(&spt->tag, TAGID_SPARING_TABLE, /* loc */ 0); + + udf_set_regid(&spt->id, "*UDF Sparing Table"); + udf_add_udf_regid(&spt->id); + + spt->rt_l = udf_rw16(layout.sparable_blocks); + spt->seq_num = udf_rw32(0); /* first generation */ + + for (cnt = 0; cnt < layout.sparable_blocks; cnt++) { + sme = &spt->entries[cnt]; + loc = layout.sparable_area + cnt * layout.sparable_blockingnr; + sme->org = udf_rw32(0xffffffff); /* open for reloc */ + sme->map = udf_rw32(loc); + } + + /* calculate crc len for actual size */ + crclen = sizeof(struct udf_sparing_table) - UDF_DESC_TAG_LENGTH; + crclen += (layout.sparable_blocks-1) * sizeof(struct spare_map_entry); +/* XXX ensure crclen doesn't exceed UINT16_MAX ? */ + spt->tag.desc_crc_len = udf_rw16((uint16_t)crclen); + + context.sparing_table = spt; + + return 0; +} + + +static void +udf_add_logvol_part_meta(uint16_t phys_part) +{ + union udf_pmap *pmap; + struct logvol_desc *logvol = context.logical_vol; + uint8_t *pmap_pos; + uint32_t pmapv_size, log_part; + uint16_t crclen; + + log_part = udf_rw32(logvol->n_pm); + pmap_pos = logvol->maps + udf_rw32(logvol->mt_l); + pmapv_size = sizeof(struct part_map_2); + + pmap = (union udf_pmap *) pmap_pos; + pmap->pmm.type = 2; + pmap->pmm.len = pmapv_size; + + udf_set_regid(&pmap->pmm.id, "*UDF Metadata Partition"); + udf_add_udf_regid(&pmap->pmm.id); + + pmap->pmm.vol_seq_num = udf_rw16(1); /* no multi-volume */ + pmap->pmm.part_num = udf_rw16(phys_part); + + /* fill in meta data file(s) and alloc/alignment unit sizes */ + pmap->pmm.meta_file_lbn = udf_rw32(layout.meta_file); + pmap->pmm.meta_mirror_file_lbn = udf_rw32(layout.meta_mirror); + pmap->pmm.meta_bitmap_file_lbn = udf_rw32(layout.meta_bitmap); + pmap->pmm.alloc_unit_size = udf_rw32(layout.meta_blockingnr); + pmap->pmm.alignment_unit_size = udf_rw16(layout.meta_alignment); + pmap->pmm.flags = 0; /* METADATA_DUPLICATED */ + + context.vtop [log_part] = phys_part; + context.vtop_tp [log_part] = UDF_VTOP_TYPE_META; + context.vtop_offset[log_part] = + context.vtop_offset[phys_part] + layout.meta_part_start_lba; + context.part_size[log_part] = layout.meta_part_size_lba; + context.part_free[log_part] = layout.meta_part_size_lba; + + /* increment number of partitions and length */ + logvol->n_pm = udf_rw32(log_part + 1); + logvol->mt_l = udf_rw32(udf_rw32(logvol->mt_l) + pmapv_size); + + crclen = udf_rw16(logvol->tag.desc_crc_len) + pmapv_size; + logvol->tag.desc_crc_len = udf_rw16(crclen); +} + + +int +udf_create_logical_dscr(int format_flags) +{ + int error; + + if ((error = udf_create_base_logical_dscr())) + return error; + + /* we pass data_part for there might be a read-only part one day */ + if (format_flags & FORMAT_SPARABLE) { + /* sparable partition mapping has no physical mapping */ + udf_add_logvol_part_sparable(context.data_part); + } else { + udf_add_logvol_part_physical(context.data_part); + } + + if (format_flags & FORMAT_VAT) { + /* add VAT virtual mapping; reflects on datapart */ + udf_add_logvol_part_virtual(context.data_part); + } + if (format_flags & FORMAT_META) { + /* add META data mapping; reflects on datapart */ + udf_add_logvol_part_meta(context.data_part); + } + + return 0; +} + + +int +udf_create_impvold(char *field1, char *field2, char *field3) +{ + struct impvol_desc *ivd; + struct udf_lv_info *lvi; + uint16_t crclen; + + ivd = calloc(1, context.sector_size); + if (ivd == NULL) + return ENOMEM; + lvi = &ivd->_impl_use.lv_info; + + udf_inittag(&ivd->tag, TAGID_IMP_VOL, /* loc */ 0); + ivd->seq_num = udf_rw32(context.vds_seq); context.vds_seq++; + + udf_set_regid(&ivd->impl_id, "*UDF LV Info"); + udf_add_udf_regid(&ivd->impl_id); + + /* fill in UDF specific part */ + udf_osta_charset(&lvi->lvi_charset); + udf_encode_osta_id(lvi->logvol_id, 128, context.logvol_name); + + udf_encode_osta_id(lvi->lvinfo1, 36, field1); + udf_encode_osta_id(lvi->lvinfo2, 36, field2); + udf_encode_osta_id(lvi->lvinfo3, 36, field3); + + udf_set_regid(&lvi->impl_id, context.impl_name); + udf_add_impl_regid(&lvi->impl_id); + + crclen = sizeof(struct impvol_desc) - UDF_DESC_TAG_LENGTH; + ivd->tag.desc_crc_len = udf_rw16(crclen); + + context.implementation = ivd; + + return 0; +} + + +/* XXX might need to be sanitised a bit later */ +void +udf_update_lvintd(int type) +{ + struct logvol_int_desc *lvid; + struct udf_logvol_info *lvinfo; + struct logvol_desc *logvol; + uint32_t *pos; + uint32_t cnt, l_iu, num_partmappings; + uint32_t crclen; /* XXX: should be 16; need to detect overflow */ + + lvid = context.logvol_integrity; + logvol = context.logical_vol; + + assert(lvid); + assert(logvol); + + lvid->integrity_type = udf_rw32(type); + + num_partmappings = udf_rw32(logvol->n_pm); + + udf_set_timestamp_now(&lvid->time); + + lvinfo = (struct udf_logvol_info *) + (lvid->tables + num_partmappings * 2); + udf_set_regid(&lvinfo->impl_id, context.impl_name); + udf_add_impl_regid(&lvinfo->impl_id); + + lvinfo->num_files = udf_rw32(context.num_files); + lvinfo->num_directories = udf_rw32(context.num_directories); + + lvid->lvint_next_unique_id = udf_rw64(context.unique_id); + + /* XXX sane enough ? */ + lvinfo->min_udf_readver = udf_rw16(context.min_udf); + lvinfo->min_udf_writever = udf_rw16(context.min_udf); + lvinfo->max_udf_writever = udf_rw16(context.max_udf); + + lvid->num_part = udf_rw32(num_partmappings); + + /* no impl. use needed */ + l_iu = sizeof(struct udf_logvol_info); + lvid->l_iu = udf_rw32(l_iu); + + pos = &lvid->tables[0]; + for (cnt = 0; cnt < num_partmappings; cnt++) { + *pos++ = udf_rw32(context.part_free[cnt]); + } + for (cnt = 0; cnt < num_partmappings; cnt++) { + *pos++ = udf_rw32(context.part_size[cnt]); + } + + crclen = sizeof(struct logvol_int_desc) -4 -UDF_DESC_TAG_LENGTH + l_iu; + crclen += num_partmappings * 2 * 4; +/* XXX ensure crclen doesn't exceed UINT16_MAX ? */ + lvid->tag.desc_crc_len = udf_rw16(crclen); + + context.logvol_info = lvinfo; +} + + +int +udf_create_lvintd(int type) +{ + struct logvol_int_desc *lvid; + + lvid = calloc(1, context.sector_size); + if (lvid == NULL) + return ENOMEM; + + udf_inittag(&lvid->tag, TAGID_LOGVOL_INTEGRITY, /* loc */ 0); + + context.logvol_integrity = lvid; + + udf_update_lvintd(type); + + return 0; +} + + +int +udf_create_fsd(void) +{ + struct fileset_desc *fsd; + uint16_t crclen; + + fsd = calloc(1, context.sector_size); + if (fsd == NULL) + return ENOMEM; + + udf_inittag(&fsd->tag, TAGID_FSD, /* loc */ 0); + + udf_set_timestamp_now(&fsd->time); + fsd->ichg_lvl = udf_rw16(3); /* UDF 2.3.2.1 */ + fsd->max_ichg_lvl = udf_rw16(3); /* UDF 2.3.2.2 */ + + fsd->charset_list = udf_rw32(1); /* only CS0 */ + fsd->max_charset_list = udf_rw32(1); /* only CS0 */ + + fsd->fileset_num = udf_rw32(0); /* only one fsd */ + fsd->fileset_desc_num = udf_rw32(0); /* origional */ + + udf_osta_charset(&fsd->logvol_id_charset); + udf_encode_osta_id(fsd->logvol_id, 128, context.logvol_name); + + udf_osta_charset(&fsd->fileset_charset); + udf_encode_osta_id(fsd->fileset_id, 32, context.fileset_name); + + /* copyright file and abstract file names obmitted */ + + fsd->rootdir_icb.len = udf_rw32(context.sector_size); + fsd->rootdir_icb.loc.lb_num = udf_rw32(layout.rootdir); + fsd->rootdir_icb.loc.part_num = udf_rw16(context.metadata_part); + + udf_set_regid(&fsd->domain_id, "*OSTA UDF Compliant"); + udf_add_domain_regid(&fsd->domain_id); + + /* next_ex stays zero */ + /* no system streamdirs yet */ + + crclen = sizeof(struct fileset_desc) - UDF_DESC_TAG_LENGTH; + fsd->tag.desc_crc_len = udf_rw16(crclen); + + context.fileset_desc = fsd; + + return 0; +} + + +int +udf_create_space_bitmap(uint32_t dscr_size, uint32_t part_size_lba, + struct space_bitmap_desc **sbdp) +{ + struct space_bitmap_desc *sbd; + uint32_t cnt; + uint16_t crclen; + + *sbdp = NULL; + sbd = calloc(context.sector_size, dscr_size); + if (sbd == NULL) + return ENOMEM; + + udf_inittag(&sbd->tag, TAGID_SPACE_BITMAP, /* loc */ 0); + + sbd->num_bits = udf_rw32(part_size_lba); + sbd->num_bytes = udf_rw32((part_size_lba + 7)/8); + + /* fill space with 0xff to indicate free */ + for (cnt = 0; cnt < udf_rw32(sbd->num_bytes); cnt++) + sbd->data[cnt] = 0xff; + + /* set crc to only cover the header (UDF 2.3.1.2, 2.3.8.1) */ + crclen = sizeof(struct space_bitmap_desc) -1 - UDF_DESC_TAG_LENGTH; + sbd->tag.desc_crc_len = udf_rw16(crclen); + + *sbdp = sbd; + return 0; +} + + +/* --------------------------------------------------------------------- */ + +int +udf_register_bad_block(uint32_t location) +{ + struct udf_sparing_table *spt; + struct spare_map_entry *sme, *free_sme; + uint32_t cnt; + + spt = context.sparing_table; + if (spt == NULL) { + printf("internal error: adding bad block to non sparable\n"); + return EINVAL; + } + + /* find us a free spare map entry */ + free_sme = NULL; + for (cnt = 0; cnt < layout.sparable_blocks; cnt++) { + sme = &spt->entries[cnt]; + /* if we are allready in it, bail out */ + if (udf_rw32(sme->org) == location) + return 0; + if (udf_rw32(sme->org) == 0xffffffff) { + free_sme = sme; + break; + } + } + if (free_sme == NULL) { + printf("Disc relocation blocks full; disc too damanged\n"); + return EINVAL; + } + free_sme->org = udf_rw32(location); + + return 0; +} + + +void +udf_mark_allocated(uint32_t start_lb, int partnr, uint32_t blocks) +{ + union dscrptr *dscr; + uint8_t *bpos; + uint32_t cnt, bit; + + /* account for space used on underlying partition */ + context.part_free[partnr] -= blocks; +#ifdef DEBUG + printf("mark allocated : partnr %d, start_lb %d for %d blocks\n", + partnr, start_lb, blocks); +#endif + + switch (context.vtop_tp[partnr]) { + case UDF_VTOP_TYPE_VIRT: + /* nothing */ + break; + case UDF_VTOP_TYPE_PHYS: + case UDF_VTOP_TYPE_SPARABLE: + case UDF_VTOP_TYPE_META: + if (context.part_unalloc_bits[context.vtop[partnr]] == NULL) { + context.part_free[partnr] = 0; + break; + } +#ifdef DEBUG + printf("Marking %d+%d as used\n", start_lb, blocks); +#endif + dscr = (union dscrptr *) (context.part_unalloc_bits[partnr]); + for (cnt = start_lb; cnt < start_lb + blocks; cnt++) { + bpos = &dscr->sbd.data[cnt / 8]; + bit = cnt % 8; + *bpos &= ~(1<< bit); + } + break; + default: + printf("internal error: reality check in mapping type %d\n", + context.vtop_tp[partnr]); + exit(EXIT_FAILURE); + } +} + + +void +udf_advance_uniqueid(void) +{ + /* Minimum value of 16 : UDF 3.2.1.1, 3.3.3.4. */ + context.unique_id++; + if (context.unique_id < 0x10) + context.unique_id = 0x10; +} + +/* --------------------------------------------------------------------- */ + +static void +unix_to_udf_name(char *result, uint8_t *result_len, + char const *name, int name_len, struct charspec *chsp) +{ + uint16_t *raw_name; + uint16_t *outchp; + const char *inchp; + const char *osta_id = "OSTA Compressed Unicode"; + int udf_chars, is_osta_typ0, bits; + size_t cnt; + + /* allocate temporary unicode-16 buffer */ + raw_name = malloc(1024); + assert(raw_name); + + /* convert utf8 to unicode-16 */ + *raw_name = 0; + inchp = name; + outchp = raw_name; + bits = 8; + for (cnt = name_len, udf_chars = 0; cnt;) { + *outchp = wget_utf8(&inchp, &cnt); + if (*outchp > 0xff) + bits=16; + outchp++; + udf_chars++; + } + /* null terminate just in case */ + *outchp++ = 0; + + is_osta_typ0 = (chsp->type == 0); + is_osta_typ0 &= (strcmp((char *) chsp->inf, osta_id) == 0); + if (is_osta_typ0) { + udf_chars = udf_CompressUnicode(udf_chars, bits, + (unicode_t *) raw_name, + (byte *) result); + } else { + printf("unix to udf name: no CHSP0 ?\n"); + /* XXX assume 8bit char length byte latin-1 */ + *result++ = 8; udf_chars = 1; + strncpy(result, name + 1, name_len); + udf_chars += name_len; + } + *result_len = udf_chars; + free(raw_name); +} + + +#define UDF_SYMLINKBUFLEN (64*1024) /* picked */ +int +udf_encode_symlink(uint8_t **pathbufp, uint32_t *pathlenp, char *target) +{ + struct charspec osta_charspec; + struct pathcomp pathcomp; + char *pathbuf, *pathpos, *compnamepos; +// char *mntonname; +// int mntonnamelen; + int pathlen, len, compnamelen; + int error; + + /* process `target' to an UDF structure */ + pathbuf = malloc(UDF_SYMLINKBUFLEN); + assert(pathbuf); + + *pathbufp = NULL; + *pathlenp = 0; + + pathpos = pathbuf; + pathlen = 0; + udf_osta_charset(&osta_charspec); + + if (*target == '/') { + /* symlink starts from the root */ + len = UDF_PATH_COMP_SIZE; + memset(&pathcomp, 0, len); + pathcomp.type = UDF_PATH_COMP_ROOT; + +#if 0 + /* XXX how to check for in makefs? */ + /* check if its mount-point relative! */ + mntonname = udf_node->ump->vfs_mountp->mnt_stat.f_mntonname; + mntonnamelen = strlen(mntonname); + if (strlen(target) >= mntonnamelen) { + if (strncmp(target, mntonname, mntonnamelen) == 0) { + pathcomp.type = UDF_PATH_COMP_MOUNTROOT; + target += mntonnamelen; + } + } else { + target++; + } +#else + target++; +#endif + + memcpy(pathpos, &pathcomp, len); + pathpos += len; + pathlen += len; + } + + error = 0; + while (*target) { + /* ignore multiple '/' */ + while (*target == '/') { + target++; + } + if (!*target) + break; + + /* extract component name */ + compnamelen = 0; + compnamepos = target; + while ((*target) && (*target != '/')) { + target++; + compnamelen++; + } + + /* just trunc if too long ?? (security issue) */ + if (compnamelen >= 127) { + error = ENAMETOOLONG; + break; + } + + /* convert unix name to UDF name */ + len = sizeof(struct pathcomp); + memset(&pathcomp, 0, len); + pathcomp.type = UDF_PATH_COMP_NAME; + len = UDF_PATH_COMP_SIZE; + + if ((compnamelen == 2) && (strncmp(compnamepos, "..", 2) == 0)) + pathcomp.type = UDF_PATH_COMP_PARENTDIR; + if ((compnamelen == 1) && (*compnamepos == '.')) + pathcomp.type = UDF_PATH_COMP_CURDIR; + + if (pathcomp.type == UDF_PATH_COMP_NAME) { + unix_to_udf_name( + (char *) &pathcomp.ident, &pathcomp.l_ci, + compnamepos, compnamelen, + &osta_charspec); + len = UDF_PATH_COMP_SIZE + pathcomp.l_ci; + } + + if (pathlen + len >= UDF_SYMLINKBUFLEN) { + error = ENAMETOOLONG; + break; + } + + memcpy(pathpos, &pathcomp, len); + pathpos += len; + pathlen += len; + } + + if (error) { + /* aparently too big */ + free(pathbuf); + return error; + } + + /* return status of symlink contents writeout */ + *pathbufp = (uint8_t *) pathbuf; + *pathlenp = pathlen; + + return 0; + +} +#undef UDF_SYMLINKBUFLEN + + +int +udf_fidsize(struct fileid_desc *fid) +{ + uint32_t size; + + if (udf_rw16(fid->tag.id) != TAGID_FID) + errx(EINVAL, "got udf_fidsize on non FID\n"); + + size = UDF_FID_SIZE + fid->l_fi + udf_rw16(fid->l_iu); + size = (size + 3) & ~3; + + return size; +} + + +int +udf_create_parentfid(struct fileid_desc *fid, struct long_ad *parent) +{ + /* the size of an empty FID is 38 but needs to be a multiple of 4 */ + int fidsize = 40; + + udf_inittag(&fid->tag, TAGID_FID, udf_rw32(parent->loc.lb_num)); + fid->file_version_num = udf_rw16(1); /* UDF 2.3.4.1 */ + fid->file_char = UDF_FILE_CHAR_DIR | UDF_FILE_CHAR_PAR; + fid->icb = *parent; + fid->icb.longad_uniqueid = parent->longad_uniqueid; + fid->tag.desc_crc_len = udf_rw16(fidsize - UDF_DESC_TAG_LENGTH); + + /* we have to do the fid here explicitly for simplicity */ + udf_validate_tag_and_crc_sums((union dscrptr *) fid); + + return fidsize; +} + + +void +udf_create_fid(uint32_t diroff, struct fileid_desc *fid, char *name, + int file_char, struct long_ad *ref) +{ + struct charspec osta_charspec; + uint32_t endfid; + uint32_t fidsize, lb_rest; + + memset(fid, 0, sizeof(*fid)); + udf_inittag(&fid->tag, TAGID_FID, udf_rw32(ref->loc.lb_num)); + fid->file_version_num = udf_rw16(1); /* UDF 2.3.4.1 */ + fid->file_char = file_char; + fid->l_iu = udf_rw16(0); + fid->icb = *ref; + fid->icb.longad_uniqueid = ref->longad_uniqueid; + + udf_osta_charset(&osta_charspec); + unix_to_udf_name((char *) fid->data, &fid->l_fi, name, strlen(name), + &osta_charspec); + + /* + * OK, tricky part: we need to pad so the next descriptor header won't + * cross the sector boundary + */ + endfid = diroff + udf_fidsize(fid); + lb_rest = context.sector_size - (endfid % context.sector_size); + if (lb_rest < sizeof(struct desc_tag)) { + /* add at least 32 */ + fid->l_iu = udf_rw16(32); + udf_set_regid((struct regid *) fid->data, context.impl_name); + udf_add_impl_regid((struct regid *) fid->data); + + unix_to_udf_name((char *) fid->data + udf_rw16(fid->l_iu), + &fid->l_fi, name, strlen(name), &osta_charspec); + } + + fidsize = udf_fidsize(fid); + fid->tag.desc_crc_len = udf_rw16(fidsize - UDF_DESC_TAG_LENGTH); + + /* make sure the header sums stays correct */ + udf_validate_tag_and_crc_sums((union dscrptr *)fid); +} + + +static void +udf_append_parentfid(union dscrptr *dscr, struct long_ad *parent_icb) +{ + struct file_entry *fe; + struct extfile_entry *efe; + struct fileid_desc *fid; + uint32_t l_ea; + uint32_t fidsize, crclen; + uint8_t *bpos, *data; + + fe = NULL; + efe = NULL; + if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) { + fe = &dscr->fe; + data = fe->data; + l_ea = udf_rw32(fe->l_ea); + } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) { + efe = &dscr->efe; + data = efe->data; + l_ea = udf_rw32(efe->l_ea); + } else { + errx(1, "Bad tag passed to udf_append_parentfid"); + } + + /* create '..' */ + bpos = data + l_ea; + fid = (struct fileid_desc *) bpos; + fidsize = udf_create_parentfid(fid, parent_icb); + + /* record fidlength information */ + if (fe) { + fe->inf_len = udf_rw64(fidsize); + fe->l_ad = udf_rw32(fidsize); + fe->logblks_rec = udf_rw64(0); /* intern */ + crclen = sizeof(struct file_entry); + } else { + efe->inf_len = udf_rw64(fidsize); + efe->obj_size = udf_rw64(fidsize); + efe->l_ad = udf_rw32(fidsize); + efe->logblks_rec = udf_rw64(0); /* intern */ + crclen = sizeof(struct extfile_entry); + } + crclen -= 1 + UDF_DESC_TAG_LENGTH; + crclen += l_ea + fidsize; + dscr->tag.desc_crc_len = udf_rw16(crclen); + + /* make sure the header sums stays correct */ + udf_validate_tag_and_crc_sums(dscr); +} + + + +/* + * Order of extended attributes : + * ECMA 167 EAs + * Non block aligned Implementation Use EAs + * Block aligned Implementation Use EAs (not in newfs_udf) + * Application Use EAs (not in newfs_udf) + * + * no checks for doubles, must be called in-order + */ +static void +udf_extattr_append_internal(union dscrptr *dscr, struct extattr_entry *extattr) +{ + struct file_entry *fe; + struct extfile_entry *efe; + struct extattrhdr_desc *extattrhdr; + struct impl_extattr_entry *implext; + uint32_t impl_attr_loc, appl_attr_loc, l_ea, a_l, exthdr_len; + uint32_t *l_eap, l_ad; + uint16_t *spos; + uint8_t *bpos, *data; + + if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) { + fe = &dscr->fe; + data = fe->data; + l_eap = &fe->l_ea; + l_ad = udf_rw32(fe->l_ad); + } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) { + efe = &dscr->efe; + data = efe->data; + l_eap = &efe->l_ea; + l_ad = udf_rw32(efe->l_ad); + } else { + errx(1, "Bad tag passed to udf_extattr_append_internal"); + } + + /* should have a header! */ + extattrhdr = (struct extattrhdr_desc *) data; + l_ea = udf_rw32(*l_eap); + if (l_ea == 0) { + assert(l_ad == 0); + /* create empty extended attribute header */ + exthdr_len = sizeof(struct extattrhdr_desc); + + udf_inittag(&extattrhdr->tag, TAGID_EXTATTR_HDR, /* loc */ 0); + extattrhdr->impl_attr_loc = udf_rw32(exthdr_len); + extattrhdr->appl_attr_loc = udf_rw32(exthdr_len); + extattrhdr->tag.desc_crc_len = udf_rw16(8); + + /* record extended attribute header length */ + l_ea = exthdr_len; + *l_eap = udf_rw32(l_ea); + } + + /* extract locations */ + impl_attr_loc = udf_rw32(extattrhdr->impl_attr_loc); + appl_attr_loc = udf_rw32(extattrhdr->appl_attr_loc); + if (impl_attr_loc == UDF_IMPL_ATTR_LOC_NOT_PRESENT) + impl_attr_loc = l_ea; + if (appl_attr_loc == UDF_IMPL_ATTR_LOC_NOT_PRESENT) + appl_attr_loc = l_ea; + + /* Ecma 167 EAs */ + if (udf_rw32(extattr->type) < 2048) { + assert(impl_attr_loc == l_ea); + assert(appl_attr_loc == l_ea); + } + + /* implementation use extended attributes */ + if (udf_rw32(extattr->type) == 2048) { + assert(appl_attr_loc == l_ea); + + /* calculate and write extended attribute header checksum */ + implext = (struct impl_extattr_entry *) extattr; + assert(udf_rw32(implext->iu_l) == 4); /* [UDF 3.3.4.5] */ + spos = (uint16_t *) implext->data; + *spos = udf_rw16(udf_ea_cksum((uint8_t *) implext)); + } + + /* application use extended attributes */ + assert(udf_rw32(extattr->type) != 65536); + assert(appl_attr_loc == l_ea); + + /* append the attribute at the end of the current space */ + bpos = data + udf_rw32(*l_eap); + a_l = udf_rw32(extattr->a_l); + + /* update impl. attribute locations */ + if (udf_rw32(extattr->type) < 2048) { + impl_attr_loc = l_ea + a_l; + appl_attr_loc = l_ea + a_l; + } + if (udf_rw32(extattr->type) == 2048) { + appl_attr_loc = l_ea + a_l; + } + + /* copy and advance */ + memcpy(bpos, extattr, a_l); + l_ea += a_l; + *l_eap = udf_rw32(l_ea); + + /* do the `dance` again backwards */ + if (context.dscrver != 2) { + if (impl_attr_loc == l_ea) + impl_attr_loc = UDF_IMPL_ATTR_LOC_NOT_PRESENT; + if (appl_attr_loc == l_ea) + appl_attr_loc = UDF_APPL_ATTR_LOC_NOT_PRESENT; + } + + /* store offsets */ + extattrhdr->impl_attr_loc = udf_rw32(impl_attr_loc); + extattrhdr->appl_attr_loc = udf_rw32(appl_attr_loc); + + /* make sure the header sums stays correct */ + udf_validate_tag_and_crc_sums((union dscrptr *) extattrhdr); +} + + +int +udf_create_new_fe(struct file_entry **fep, int file_type, struct stat *st) +{ + struct file_entry *fe; + struct icb_tag *icb; + struct timestamp birthtime; + struct filetimes_extattr_entry *ft_extattr; + uint32_t crclen; /* XXX: should be 16; need to detect overflow */ + uint16_t icbflags; + + *fep = NULL; + fe = calloc(1, context.sector_size); + if (fe == NULL) + return ENOMEM; + + udf_inittag(&fe->tag, TAGID_FENTRY, /* loc */ 0); + icb = &fe->icbtag; + + /* + * Always use strategy type 4 unless on WORM wich we don't support + * (yet). Fill in defaults and set for internal allocation of data. + */ + icb->strat_type = udf_rw16(4); + icb->max_num_entries = udf_rw16(1); + icb->file_type = file_type; /* 8 bit */ + icb->flags = udf_rw16(UDF_ICB_INTERN_ALLOC); + + fe->perm = udf_rw32(0x7fff); /* all is allowed */ + fe->link_cnt = udf_rw16(0); /* explicit setting */ + + fe->ckpoint = udf_rw32(1); /* user supplied file version */ + + udf_set_timestamp_now(&birthtime); + udf_set_timestamp_now(&fe->atime); + udf_set_timestamp_now(&fe->attrtime); + udf_set_timestamp_now(&fe->mtime); + + /* set attributes */ + if (st) { +#if !HAVE_NBTOOL_CONFIG_H + udf_set_timestamp(&birthtime, st->st_birthtime); +#else + udf_set_timestamp(&birthtime, 0); +#endif + udf_set_timestamp(&fe->atime, st->st_atime); + udf_set_timestamp(&fe->attrtime, st->st_ctime); + udf_set_timestamp(&fe->mtime, st->st_mtime); + fe->uid = udf_rw32(st->st_uid); + fe->gid = udf_rw32(st->st_gid); + + fe->perm = unix_mode_to_udf_perm(st->st_mode); + + icbflags = udf_rw16(fe->icbtag.flags); + icbflags &= ~UDF_ICB_TAG_FLAGS_SETUID; + icbflags &= ~UDF_ICB_TAG_FLAGS_SETGID; + icbflags &= ~UDF_ICB_TAG_FLAGS_STICKY; + if (st->st_mode & S_ISUID) + icbflags |= UDF_ICB_TAG_FLAGS_SETUID; + if (st->st_mode & S_ISGID) + icbflags |= UDF_ICB_TAG_FLAGS_SETGID; + if (st->st_mode & S_ISVTX) + icbflags |= UDF_ICB_TAG_FLAGS_STICKY; + fe->icbtag.flags = udf_rw16(icbflags); + } + + udf_set_regid(&fe->imp_id, context.impl_name); + udf_add_impl_regid(&fe->imp_id); + fe->unique_id = udf_rw64(context.unique_id); + udf_advance_uniqueid(); + + fe->l_ea = udf_rw32(0); + + /* create extended attribute to record our creation time */ + ft_extattr = calloc(1, UDF_FILETIMES_ATTR_SIZE(1)); + ft_extattr->hdr.type = udf_rw32(UDF_FILETIMES_ATTR_NO); + ft_extattr->hdr.subtype = 1; /* [4/48.10.5] */ + ft_extattr->hdr.a_l = udf_rw32(UDF_FILETIMES_ATTR_SIZE(1)); + ft_extattr->d_l = udf_rw32(UDF_TIMESTAMP_SIZE); /* one item */ + ft_extattr->existence = UDF_FILETIMES_FILE_CREATION; + ft_extattr->times[0] = birthtime; + + udf_extattr_append_internal((union dscrptr *) fe, + (struct extattr_entry *) ft_extattr); + free(ft_extattr); + + /* record fidlength information */ + fe->inf_len = udf_rw64(0); + fe->l_ad = udf_rw32(0); + fe->logblks_rec = udf_rw64(0); /* intern */ + + crclen = sizeof(struct file_entry) - 1 - UDF_DESC_TAG_LENGTH; + crclen += udf_rw32(fe->l_ea); + + /* make sure the header sums stays correct */ + fe->tag.desc_crc_len = udf_rw16(crclen); + udf_validate_tag_and_crc_sums((union dscrptr *) fe); + + *fep = fe; + return 0; +} + + +int +udf_create_new_efe(struct extfile_entry **efep, int file_type, struct stat *st) +{ + struct extfile_entry *efe; + struct icb_tag *icb; + uint32_t crclen; /* XXX: should be 16; need to detect overflow */ + uint16_t icbflags; + + *efep = NULL; + efe = calloc(1, context.sector_size); + if (efe == NULL) + return ENOMEM; + + udf_inittag(&efe->tag, TAGID_EXTFENTRY, /* loc */ 0); + icb = &efe->icbtag; + + /* + * Always use strategy type 4 unless on WORM wich we don't support + * (yet). Fill in defaults and set for internal allocation of data. + */ + icb->strat_type = udf_rw16(4); + icb->max_num_entries = udf_rw16(1); + icb->file_type = file_type; /* 8 bit */ + icb->flags = udf_rw16(UDF_ICB_INTERN_ALLOC); + + efe->perm = udf_rw32(0x7fff); /* all is allowed */ + efe->link_cnt = udf_rw16(0); /* explicit setting */ + + efe->ckpoint = udf_rw32(1); /* user supplied file version */ + + udf_set_timestamp_now(&efe->ctime); + udf_set_timestamp_now(&efe->atime); + udf_set_timestamp_now(&efe->attrtime); + udf_set_timestamp_now(&efe->mtime); + + /* set attributes */ + if (st) { +#if !HAVE_NBTOOL_CONFIG_H + udf_set_timestamp(&efe->ctime, st->st_birthtime); +#else + udf_set_timestamp(&efe->ctime, 0); +#endif + udf_set_timestamp(&efe->atime, st->st_atime); + udf_set_timestamp(&efe->attrtime, st->st_ctime); + udf_set_timestamp(&efe->mtime, st->st_mtime); + efe->uid = udf_rw32(st->st_uid); + efe->gid = udf_rw32(st->st_gid); + + efe->perm = unix_mode_to_udf_perm(st->st_mode); + + icbflags = udf_rw16(efe->icbtag.flags); + icbflags &= ~UDF_ICB_TAG_FLAGS_SETUID; + icbflags &= ~UDF_ICB_TAG_FLAGS_SETGID; + icbflags &= ~UDF_ICB_TAG_FLAGS_STICKY; + if (st->st_mode & S_ISUID) + icbflags |= UDF_ICB_TAG_FLAGS_SETUID; + if (st->st_mode & S_ISGID) + icbflags |= UDF_ICB_TAG_FLAGS_SETGID; + if (st->st_mode & S_ISVTX) + icbflags |= UDF_ICB_TAG_FLAGS_STICKY; + efe->icbtag.flags = udf_rw16(icbflags); + } + + udf_set_regid(&efe->imp_id, context.impl_name); + udf_add_impl_regid(&efe->imp_id); + + efe->unique_id = udf_rw64(context.unique_id); + udf_advance_uniqueid(); + + /* record fidlength information */ + efe->inf_len = udf_rw64(0); + efe->obj_size = udf_rw64(0); + efe->l_ad = udf_rw32(0); + efe->logblks_rec = udf_rw64(0); + + crclen = sizeof(struct extfile_entry) - 1 - UDF_DESC_TAG_LENGTH; + + /* make sure the header sums stays correct */ + efe->tag.desc_crc_len = udf_rw16(crclen); + udf_validate_tag_and_crc_sums((union dscrptr *) efe); + + *efep = efe; + return 0; +} + +/* --------------------------------------------------------------------- */ + +/* for METADATA file appending only */ +static void +udf_append_meta_mapping_part_to_efe(struct extfile_entry *efe, + struct short_ad *mapping) +{ + struct icb_tag *icb; + uint64_t inf_len, obj_size, logblks_rec; + uint32_t l_ad, l_ea; + uint16_t crclen; + uint8_t *bpos; + + inf_len = udf_rw64(efe->inf_len); + obj_size = udf_rw64(efe->obj_size); + logblks_rec = udf_rw64(efe->logblks_rec); + l_ad = udf_rw32(efe->l_ad); + l_ea = udf_rw32(efe->l_ea); + crclen = udf_rw16(efe->tag.desc_crc_len); + icb = &efe->icbtag; + + /* set our allocation to shorts if not already done */ + icb->flags = udf_rw16(UDF_ICB_SHORT_ALLOC); + + /* append short_ad */ + bpos = (uint8_t *) efe->data + l_ea + l_ad; + memcpy(bpos, mapping, sizeof(struct short_ad)); + + l_ad += sizeof(struct short_ad); + crclen += sizeof(struct short_ad); + inf_len += UDF_EXT_LEN(udf_rw32(mapping->len)); + obj_size += UDF_EXT_LEN(udf_rw32(mapping->len)); + logblks_rec = UDF_ROUNDUP(inf_len, context.sector_size) / + context.sector_size; + + efe->l_ad = udf_rw32(l_ad); + efe->inf_len = udf_rw64(inf_len); + efe->obj_size = udf_rw64(obj_size); + efe->logblks_rec = udf_rw64(logblks_rec); + efe->tag.desc_crc_len = udf_rw16(crclen); +} + + +/* for METADATA file appending only */ +static void +udf_append_meta_mapping_to_efe(struct extfile_entry *efe, + uint16_t partnr, uint32_t lb_num, + uint64_t len) +{ + struct short_ad mapping; + uint64_t max_len, part_len; + + /* calculate max length meta allocation sizes */ + max_len = UDF_EXT_MAXLEN / context.sector_size; /* in sectors */ + max_len = (max_len / layout.meta_blockingnr) * layout.meta_blockingnr; + max_len = max_len * context.sector_size; + + memset(&mapping, 0, sizeof(mapping)); + while (len) { + part_len = MIN(len, max_len); + mapping.lb_num = udf_rw32(lb_num); + mapping.len = udf_rw32(part_len); + + udf_append_meta_mapping_part_to_efe(efe, &mapping); + + lb_num += part_len / context.sector_size; + len -= part_len; + } +} + + +int +udf_create_meta_files(void) +{ + struct extfile_entry *efe; + struct long_ad meta_icb; + uint64_t bytes; + uint32_t sector_size; + int filetype, error; + + sector_size = context.sector_size; + + memset(&meta_icb, 0, sizeof(meta_icb)); + meta_icb.len = udf_rw32(sector_size); + meta_icb.loc.part_num = udf_rw16(context.data_part); + + /* create metadata file */ + meta_icb.loc.lb_num = udf_rw32(layout.meta_file); + filetype = UDF_ICB_FILETYPE_META_MAIN; + error = udf_create_new_efe(&efe, filetype, NULL); + if (error) + return error; + context.meta_file = efe; + + /* create metadata mirror file */ + meta_icb.loc.lb_num = udf_rw32(layout.meta_mirror); + filetype = UDF_ICB_FILETYPE_META_MIRROR; + error = udf_create_new_efe(&efe, filetype, NULL); + if (error) + return error; + context.meta_mirror = efe; + + /* create metadata bitmap file */ + meta_icb.loc.lb_num = udf_rw32(layout.meta_bitmap); + filetype = UDF_ICB_FILETYPE_META_BITMAP; + error = udf_create_new_efe(&efe, filetype, NULL); + if (error) + return error; + context.meta_bitmap = efe; + + /* patch up files */ + context.meta_file->unique_id = udf_rw64(0); + context.meta_mirror->unique_id = udf_rw64(0); + context.meta_bitmap->unique_id = udf_rw64(0); + + /* restart unique id */ + context.unique_id = 0x10; + + /* XXX no support for metadata mirroring yet */ + /* insert extents */ + efe = context.meta_file; + udf_append_meta_mapping_to_efe(efe, context.data_part, + layout.meta_part_start_lba, + (uint64_t) layout.meta_part_size_lba * sector_size); + + efe = context.meta_mirror; + udf_append_meta_mapping_to_efe(efe, context.data_part, + layout.meta_part_start_lba, + (uint64_t) layout.meta_part_size_lba * sector_size); + + efe = context.meta_bitmap; + bytes = udf_space_bitmap_len(layout.meta_part_size_lba); + udf_append_meta_mapping_to_efe(efe, context.data_part, + layout.meta_bitmap_space, bytes); + + return 0; +} + + +/* --------------------------------------------------------------------- */ + +int +udf_create_new_rootdir(union dscrptr **dscr) +{ + struct file_entry *fe; + struct extfile_entry *efe; + struct long_ad root_icb; + int filetype, error; + + memset(&root_icb, 0, sizeof(root_icb)); + root_icb.len = udf_rw32(context.sector_size); + root_icb.loc.lb_num = udf_rw32(layout.rootdir); + root_icb.loc.part_num = udf_rw16(context.metadata_part); + + filetype = UDF_ICB_FILETYPE_DIRECTORY; + if (context.dscrver == 2) { + error = udf_create_new_fe(&fe, filetype, NULL); + *dscr = (union dscrptr *) fe; + } else { + error = udf_create_new_efe(&efe, filetype, NULL); + *dscr = (union dscrptr *) efe; + } + if (error) + return error; + + /* append '..' */ + udf_append_parentfid(*dscr, &root_icb); + + /* rootdir has explicit only one link on creation; '..' is no link */ + if (context.dscrver == 2) { + fe->link_cnt = udf_rw16(1); + } else { + efe->link_cnt = udf_rw16(1); + } + + context.num_directories++; + assert(context.num_directories == 1); + + return 0; +} + + +void +udf_prepend_VAT_file(void) +{ + /* old style VAT has no prepend */ + if (context.dscrver == 2) { + context.vat_start = 0; + context.vat_size = 0; + return; + } + + context.vat_start = offsetof(struct udf_vat, data); + context.vat_size = offsetof(struct udf_vat, data); +} + + +void +udf_vat_update(uint32_t virt, uint32_t phys) +{ + uint32_t *vatpos; + uint32_t new_size; + + if (context.vtop_tp[context.metadata_part] != UDF_VTOP_TYPE_VIRT) + return; + + new_size = MAX(context.vat_size, + (context.vat_start + (virt+1)*sizeof(uint32_t))); + + if (new_size > context.vat_allocated) { + context.vat_allocated = + UDF_ROUNDUP(new_size, context.sector_size); + context.vat_contents = realloc(context.vat_contents, + context.vat_allocated); + assert(context.vat_contents); + /* XXX could also report error */ + } + vatpos = (uint32_t *) (context.vat_contents + context.vat_start); + vatpos[virt] = udf_rw32(phys); + + context.vat_size = MAX(context.vat_size, + (context.vat_start + (virt+1)*sizeof(uint32_t))); +} + + +int +udf_append_VAT_file(void) +{ + struct udf_oldvat_tail *oldvat_tail; + struct udf_vat *vathdr; + int32_t len_diff; + + /* new style VAT has VAT LVInt analog in front */ + if (context.dscrver == 3) { + /* set up VATv2 descriptor */ + vathdr = (struct udf_vat *) context.vat_contents; + vathdr->header_len = udf_rw16(sizeof(struct udf_vat) - 1); + vathdr->impl_use_len = udf_rw16(0); + memcpy(vathdr->logvol_id, context.logical_vol->logvol_id, 128); + vathdr->prev_vat = udf_rw32(UDF_NO_PREV_VAT); + vathdr->num_files = udf_rw32(context.num_files); + vathdr->num_directories = udf_rw32(context.num_directories); + + vathdr->min_udf_readver = udf_rw16(context.min_udf); + vathdr->min_udf_writever = udf_rw16(context.min_udf); + vathdr->max_udf_writever = udf_rw16(context.max_udf); + + return 0; + } + + /* old style VAT has identifier appended */ + + /* append "*UDF Virtual Alloc Tbl" id and prev. VAT location */ + len_diff = context.vat_allocated - context.vat_size; + assert(len_diff >= 0); + if (len_diff < (int32_t) sizeof(struct udf_oldvat_tail)) { + context.vat_allocated += context.sector_size; + context.vat_contents = realloc(context.vat_contents, + context.vat_allocated); + assert(context.vat_contents); + /* XXX could also report error */ + } + + oldvat_tail = (struct udf_oldvat_tail *) (context.vat_contents + + context.vat_size); + + udf_set_regid(&oldvat_tail->id, "*UDF Virtual Alloc Tbl"); + udf_add_udf_regid(&oldvat_tail->id); + oldvat_tail->prev_vat = udf_rw32(UDF_NO_PREV_VAT); + + context.vat_size += sizeof(struct udf_oldvat_tail); + + return 0; +} + + +int +udf_create_VAT(union dscrptr **vat_dscr) +{ + struct file_entry *fe; + struct extfile_entry *efe; + struct impl_extattr_entry *implext; + struct vatlvext_extattr_entry *vatlvext; + struct long_ad dataloc, *allocpos; + uint8_t *bpos, *extattr; + uint32_t ea_len, inf_len, vat_len, blks; + int filetype; + int error; + + assert((layout.rootdir < 2) && (layout.fsd < 2)); + + memset(&dataloc, 0, sizeof(dataloc)); + dataloc.len = udf_rw32(context.vat_size); + dataloc.loc.part_num = udf_rw16(context.data_part); + dataloc.loc.lb_num = udf_rw32(layout.vat); + + if (context.dscrver == 2) { + /* old style VAT */ + filetype = UDF_ICB_FILETYPE_UNKNOWN; + error = udf_create_new_fe(&fe, filetype, NULL); + if (error) + return error; + + /* append VAT LVExtension attribute */ + ea_len = sizeof(struct impl_extattr_entry) - 1 + + sizeof(struct vatlvext_extattr_entry) + 4; + + extattr = calloc(1, ea_len); + + implext = (struct impl_extattr_entry *) extattr; + implext->hdr.type = udf_rw32(2048); /* [4/48.10.8] */ + implext->hdr.subtype = 1; /* [4/48.10.8.2] */ + implext->hdr.a_l = udf_rw32(ea_len); /* VAT LVext EA size */ + /* use 4 bytes of imp use for UDF checksum [UDF 3.3.4.5] */ + implext->iu_l = udf_rw32(4); + udf_set_regid(&implext->imp_id, "*UDF VAT LVExtension"); + udf_add_udf_regid(&implext->imp_id); + + /* VAT LVExtension data follows UDF IU space */ + bpos = ((uint8_t *) implext->data) + 4; + vatlvext = (struct vatlvext_extattr_entry *) bpos; + + vatlvext->unique_id_chk = udf_rw64(fe->unique_id); + vatlvext->num_files = udf_rw32(context.num_files); + vatlvext->num_directories = udf_rw32(context.num_directories); + memcpy(vatlvext->logvol_id, context.logical_vol->logvol_id,128); + + udf_extattr_append_internal((union dscrptr *) fe, + (struct extattr_entry *) extattr); + + free(extattr); + + fe->icbtag.flags = udf_rw16(UDF_ICB_LONG_ALLOC); + + allocpos = (struct long_ad *) (fe->data + udf_rw32(fe->l_ea)); + *allocpos = dataloc; + + /* set length */ + inf_len = context.vat_size; + fe->inf_len = udf_rw64(inf_len); + fe->l_ad = udf_rw32(sizeof(struct long_ad)); + blks = UDF_ROUNDUP(inf_len, context.sector_size) / + context.sector_size; + fe->logblks_rec = udf_rw32(blks); + + /* update vat descriptor's CRC length */ + vat_len = sizeof(struct file_entry) - 1 - UDF_DESC_TAG_LENGTH; + vat_len += udf_rw32(fe->l_ad) + udf_rw32(fe->l_ea); + fe->tag.desc_crc_len = udf_rw16(vat_len); + + *vat_dscr = (union dscrptr *) fe; + } else { + /* new style VAT */ + filetype = UDF_ICB_FILETYPE_VAT; + error = udf_create_new_efe(&efe, filetype, NULL); + if (error) + return error; + + efe->icbtag.flags = udf_rw16(UDF_ICB_LONG_ALLOC); + + allocpos = (struct long_ad *) efe->data; + *allocpos = dataloc; + + /* set length */ + inf_len = context.vat_size; + efe->inf_len = udf_rw64(inf_len); + efe->obj_size = udf_rw64(inf_len); + efe->l_ad = udf_rw32(sizeof(struct long_ad)); + blks = UDF_ROUNDUP(inf_len, context.sector_size) / + context.sector_size; + efe->logblks_rec = udf_rw32(blks); + + vat_len = sizeof(struct extfile_entry)-1 - UDF_DESC_TAG_LENGTH; + vat_len += udf_rw32(efe->l_ad); + efe->tag.desc_crc_len = udf_rw16(vat_len); + + *vat_dscr = (union dscrptr *) efe; + } + + return 0; +} + diff --git a/sbin/newfs_udf/udf_create.h b/sbin/newfs_udf/udf_create.h new file mode 100644 index 000000000..001d64eec --- /dev/null +++ b/sbin/newfs_udf/udf_create.h @@ -0,0 +1,283 @@ +/* $NetBSD: udf_create.h,v 1.7 2013/08/09 15:11:08 reinoud Exp $ */ + +/* + * Copyright (c) 2006, 2008 Reinoud Zandijk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _FS_UDF_UDF_CREATE_H_ +#define _FS_UDF_UDF_CREATE_H_ + +#include +#include +#if !HAVE_NBTOOL_CONFIG_H +#include +#else +#include "../../sys/fs/udf/ecma167-udf.h" +#endif +#include "udf_bswap.h" +#include "udf_osta.h" + + +/* format flags indicating properties of disc to create */ +#define FORMAT_WRITEONCE 0x00001 +#define FORMAT_SEQUENTIAL 0x00002 +#define FORMAT_REWRITABLE 0x00004 +#define FORMAT_SPARABLE 0x00008 +#define FORMAT_META 0x00010 +#define FORMAT_LOW 0x00020 +#define FORMAT_VAT 0x00040 +#define FORMAT_WORM 0x00080 +#define FORMAT_TRACK512 0x00100 +#define FORMAT_INVALID 0x00200 +#define FORMAT_READONLY 0x00400 +#define FORMAT_FLAGBITS \ + "\10\1WRITEONCE\2SEQUENTIAL\3REWRITABLE\4SPARABLE\5META\6LOW" \ + "\7VAT\10WORM\11TRACK512\12INVALID\13READONLY" + + +/* structure space */ +#define UDF_ANCHORS 4 /* 256, 512, N-256, N */ +#define UDF_PARTITIONS 4 /* overkill */ +#define UDF_PMAPS 4 /* overkill */ + +/* misc constants */ +#define UDF_MAX_NAMELEN 255 /* as per SPEC */ + +/* translation constants */ +#define UDF_VTOP_RAWPART UDF_PMAPS /* [0..UDF_PMAPS> are normal */ + +/* virtual to physical mapping types */ +#define UDF_VTOP_TYPE_RAW 0 +#define UDF_VTOP_TYPE_UNKNOWN 0 +#define UDF_VTOP_TYPE_PHYS 1 +#define UDF_VTOP_TYPE_VIRT 2 +#define UDF_VTOP_TYPE_SPARABLE 3 +#define UDF_VTOP_TYPE_META 4 + +#define UDF_TRANS_ZERO ((uint64_t) -1) +#define UDF_TRANS_UNMAPPED ((uint64_t) -2) +#define UDF_TRANS_INTERN ((uint64_t) -3) +#define UDF_MAX_SECTOR ((uint64_t) -10) /* high water mark */ + +/* handys */ +#define UDF_ROUNDUP(val, gran) \ + ((uint64_t) (gran) * (((uint64_t)(val) + (gran)-1) / (gran))) + +#define UDF_ROUNDDOWN(val, gran) \ + ((uint64_t) (gran) * (((uint64_t)(val)) / (gran))) + + +/* disc offsets for various structures and their sizes */ +struct udf_disclayout { + uint32_t wrtrack_skew; + + uint32_t iso9660_vrs; + uint32_t anchors[UDF_ANCHORS]; + uint32_t vds_size, vds1, vds2; + uint32_t lvis_size, lvis; + + uint32_t first_lba, last_lba; + uint32_t sector_size; + uint32_t blockingnr, align_blockingnr, sparable_blockingnr; + uint32_t meta_blockingnr, meta_alignment; + + /* sparables */ + uint32_t sparable_blocks; + uint32_t sparable_area, sparable_area_size; + uint32_t sparing_table_dscr_lbas; + uint32_t spt_1, spt_2; + + /* bitmaps */ + uint32_t alloc_bitmap_dscr_size; + uint32_t unalloc_space, freed_space; + + uint32_t meta_bitmap_dscr_size; + uint32_t meta_bitmap_space; + + /* metadata partition */ + uint32_t meta_file, meta_mirror, meta_bitmap; + uint32_t meta_part_start_lba, meta_part_size_lba; + + /* main partition */ + uint32_t part_start_lba, part_size_lba; + + uint32_t fsd, rootdir, vat; + +}; + + +/* all info about discs and descriptors building */ +struct udf_create_context { + /* descriptors */ + int dscrver; /* 2 or 3 */ + int min_udf; /* hex */ + int max_udf; /* hex */ + int serialnum; /* format serialno */ + + int gmtoff; /* in minutes */ + + /* XXX to layout? */ + uint32_t sector_size; + + /* identification */ + char *logvol_name; + char *primary_name; + char *volset_name; + char *fileset_name; + + char const *app_name; + char const *impl_name; + int app_version_main; + int app_version_sub; + + /* building */ + int vds_seq; /* for building functions */ + int unique_id; /* only first few are used */ + + /* constructed structures */ + struct anchor_vdp *anchors[UDF_ANCHORS]; /* anchors to VDS */ + struct pri_vol_desc *primary_vol; /* identification */ + struct logvol_desc *logical_vol; /* main mapping v->p */ + struct unalloc_sp_desc *unallocated; /* free UDF space */ + struct impvol_desc *implementation; /* likely reduntant */ + struct logvol_int_desc *logvol_integrity; /* current integrity */ + struct part_desc *partitions[UDF_PARTITIONS]; /* partitions */ + + /* XXX to layout? */ + int data_part; + int metadata_part; + + /* block numbers as offset in partition */ + uint32_t metadata_alloc_pos; + uint32_t data_alloc_pos; + + /* derived; points *into* other structures */ + struct udf_logvol_info *logvol_info; /* inside integrity */ + + /* fileset and root directories */ + struct fileset_desc *fileset_desc; /* normally one */ + + /* logical to physical translations */ + int vtop[UDF_PMAPS+1]; /* vpartnr trans */ + int vtop_tp[UDF_PMAPS+1]; /* type of trans */ + uint64_t vtop_offset[UDF_PMAPS+1]; /* offset in lb */ + + /* sparable */ + struct udf_sparing_table*sparing_table; /* replacements */ + + /* VAT file */ + uint32_t vat_size; /* length */ + uint32_t vat_allocated; /* allocated length */ + uint32_t vat_start; /* offset 1st entry */ + uint8_t *vat_contents; /* the VAT */ + + /* meta data partition */ + struct extfile_entry *meta_file; + struct extfile_entry *meta_mirror; + struct extfile_entry *meta_bitmap; + + /* lvint */ + int num_files; + int num_directories; + uint32_t part_size[UDF_PARTITIONS]; + uint32_t part_free[UDF_PARTITIONS]; + + struct space_bitmap_desc*part_unalloc_bits[UDF_PARTITIONS]; + struct space_bitmap_desc*part_freed_bits [UDF_PARTITIONS]; +}; + + +/* globals */ + +extern struct udf_create_context context; +extern struct udf_disclayout layout; + +/* prototypes */ +void udf_init_create_context(void); +int a_udf_version(const char *s, const char *id_type); + +int udf_calculate_disc_layout(int format_flags, int min_udf, + uint32_t wrtrack_skew, + uint32_t first_lba, uint32_t last_lba, + uint32_t sector_size, uint32_t blockingnr, + uint32_t sparable_blocks, + float meta_fract); + +void udf_osta_charset(struct charspec *charspec); +void udf_encode_osta_id(char *osta_id, uint16_t len, char *text); + +void udf_set_regid(struct regid *regid, char const *name); +void udf_add_domain_regid(struct regid *regid); +void udf_add_udf_regid(struct regid *regid); +void udf_add_impl_regid(struct regid *regid); +void udf_add_app_regid(struct regid *regid); + +int udf_validate_tag_sum(union dscrptr *dscr); +int udf_validate_tag_and_crc_sums(union dscrptr *dscr); + +void udf_set_timestamp_now(struct timestamp *timestamp); + +void udf_inittag(struct desc_tag *tag, int tagid, uint32_t loc); +int udf_create_anchor(int num); + +void udf_create_terminator(union dscrptr *dscr, uint32_t loc); +int udf_create_primaryd(void); +int udf_create_partitiond(int part_num, int part_accesstype); +int udf_create_unalloc_spaced(void); +int udf_create_sparing_tabled(void); +int udf_create_space_bitmap(uint32_t dscr_size, uint32_t part_size_lba, + struct space_bitmap_desc **sbdp); +int udf_create_logical_dscr(int format_flags); +int udf_create_impvold(char *field1, char *field2, char *field3); +int udf_create_fsd(void); +int udf_create_lvintd(int type); +void udf_update_lvintd(int type); + +int udf_register_bad_block(uint32_t location); +void udf_mark_allocated(uint32_t start_lb, int partnr, uint32_t blocks); + +int udf_create_new_fe(struct file_entry **fep, int file_type, + struct stat *st); +int udf_create_new_efe(struct extfile_entry **efep, int file_type, + struct stat *st); + +int udf_encode_symlink(uint8_t **pathbufp, uint32_t *pathlenp, char *target); + +void udf_advance_uniqueid(void); +int udf_fidsize(struct fileid_desc *fid); +void udf_create_fid(uint32_t diroff, struct fileid_desc *fid, + char *name, int namelen, struct long_ad *ref); +int udf_create_parentfid(struct fileid_desc *fid, struct long_ad *parent); + +int udf_create_meta_files(void); +int udf_create_new_rootdir(union dscrptr **dscr); + +int udf_create_VAT(union dscrptr **vat_dscr); +void udf_prepend_VAT_file(void); +void udf_vat_update(uint32_t virt, uint32_t phys); +int udf_append_VAT_file(void); + +#endif /* _FS_UDF_UDF_CREATE_H_ */ + diff --git a/sbin/newfs_udf/udf_write.c b/sbin/newfs_udf/udf_write.c new file mode 100644 index 000000000..c4fb4b57f --- /dev/null +++ b/sbin/newfs_udf/udf_write.c @@ -0,0 +1,906 @@ +/* $NetBSD: udf_write.c,v 1.8 2013/08/25 14:13:47 reinoud Exp $ */ + +/* + * Copyright (c) 2006, 2008, 2013 Reinoud Zandijk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__RCSID("$NetBSD: udf_write.c,v 1.8 2013/08/25 14:13:47 reinoud Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !HAVE_NBTOOL_CONFIG_H +#define _EXPOSE_MMC +#include +#else +#include "udf/cdio_mmc_structs.h" +#endif + +#include "udf_create.h" +#include "udf_write.h" +#include "newfs_udf.h" + + +union dscrptr *terminator_dscr; + +static int +udf_write_phys(void *blob, uint32_t location, uint32_t sects) +{ + uint32_t phys, cnt; + uint8_t *bpos; + int error; + + for (cnt = 0; cnt < sects; cnt++) { + bpos = (uint8_t *) blob; + bpos += context.sector_size * cnt; + + phys = location + cnt; + error = udf_write_sector(bpos, phys); + if (error) + return error; + } + return 0; +} + + +static int +udf_write_dscr_phys(union dscrptr *dscr, uint32_t location, + uint32_t sects) +{ + dscr->tag.tag_loc = udf_rw32(location); + (void) udf_validate_tag_and_crc_sums(dscr); + + return udf_write_phys(dscr, location, sects); +} + + +int +udf_write_dscr_virt(union dscrptr *dscr, uint32_t location, uint32_t vpart, + uint32_t sects) +{ + struct file_entry *fe; + struct extfile_entry *efe; + struct extattrhdr_desc *extattrhdr; + uint32_t phys; + + extattrhdr = NULL; + if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) { + fe = (struct file_entry *) dscr; + if (udf_rw32(fe->l_ea) > 0) + extattrhdr = (struct extattrhdr_desc *) fe->data; + } + if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) { + efe = (struct extfile_entry *) dscr; + if (udf_rw32(efe->l_ea) > 0) + extattrhdr = (struct extattrhdr_desc *) efe->data; + } + if (extattrhdr) { + extattrhdr->tag.tag_loc = udf_rw32(location); + udf_validate_tag_and_crc_sums((union dscrptr *) extattrhdr); + } + + dscr->tag.tag_loc = udf_rw32(location); + udf_validate_tag_and_crc_sums(dscr); + + /* determine physical location */ + phys = context.vtop_offset[vpart]; + if (context.vtop_tp[vpart] == UDF_VTOP_TYPE_VIRT) { + udf_vat_update(location, context.data_alloc_pos); + phys += context.data_alloc_pos++; + } else { + phys += location; + } + + return udf_write_phys(dscr, phys, sects); +} + + +void +udf_metadata_alloc(int nblk, struct long_ad *pos) +{ + memset(pos, 0, sizeof(*pos)); + pos->len = udf_rw32(nblk * context.sector_size); + pos->loc.lb_num = udf_rw32(context.metadata_alloc_pos); + pos->loc.part_num = udf_rw16(context.metadata_part); + + udf_mark_allocated(context.metadata_alloc_pos, context.metadata_part, + nblk); + + context.metadata_alloc_pos += nblk; + if (context.metadata_part == context.data_part) + context.data_alloc_pos = context.metadata_alloc_pos; +} + + +void +udf_data_alloc(int nblk, struct long_ad *pos) +{ + memset(pos, 0, sizeof(*pos)); + pos->len = udf_rw32(nblk * context.sector_size); + pos->loc.lb_num = udf_rw32(context.data_alloc_pos); + pos->loc.part_num = udf_rw16(context.data_part); + + udf_mark_allocated(context.data_alloc_pos, context.data_part, nblk); + context.data_alloc_pos += nblk; + if (context.metadata_part == context.data_part) + context.metadata_alloc_pos = context.data_alloc_pos; +} + + + +/* --------------------------------------------------------------------- */ + +/* + * udf_derive_format derives the format_flags from the disc's mmc_discinfo. + * The resulting flags uniquely define a disc format. Note there are at least + * 7 distinct format types defined in UDF. + */ + +#define UDF_VERSION(a) \ + (((a) == 0x100) || ((a) == 0x102) || ((a) == 0x150) || ((a) == 0x200) || \ + ((a) == 0x201) || ((a) == 0x250) || ((a) == 0x260)) + +int +udf_derive_format(int req_enable, int req_disable, int force) +{ + /* disc writability, formatted, appendable */ + if ((mmc_discinfo.mmc_cur & MMC_CAP_RECORDABLE) == 0) { + (void)printf("Can't newfs readonly device\n"); + return EROFS; + } + if (mmc_discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) { + /* sequentials need sessions appended */ + if (mmc_discinfo.disc_state == MMC_STATE_CLOSED) { + (void)printf("Can't append session to a closed disc\n"); + return EROFS; + } + if ((mmc_discinfo.disc_state != MMC_STATE_EMPTY) && !force) { + (void)printf("Disc not empty! Use -F to force " + "initialisation\n"); + return EROFS; + } + } else { + /* check if disc (being) formatted or has been started on */ + if (mmc_discinfo.disc_state == MMC_STATE_EMPTY) { + (void)printf("Disc is not formatted\n"); + return EROFS; + } + } + + /* determine UDF format */ + format_flags = 0; + if (mmc_discinfo.mmc_cur & MMC_CAP_REWRITABLE) { + /* all rewritable media */ + format_flags |= FORMAT_REWRITABLE; + if (context.min_udf >= 0x0250) { + /* standard dictates meta as default */ + format_flags |= FORMAT_META; + } + + if ((mmc_discinfo.mmc_cur & MMC_CAP_HW_DEFECTFREE) == 0) { + /* sparables for defect management */ + if (context.min_udf >= 0x150) + format_flags |= FORMAT_SPARABLE; + } + } else { + /* all once recordable media */ + format_flags |= FORMAT_WRITEONCE; + if (mmc_discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) { + format_flags |= FORMAT_SEQUENTIAL; + + if (mmc_discinfo.mmc_cur & MMC_CAP_PSEUDOOVERWRITE) { + /* logical overwritable */ + format_flags |= FORMAT_LOW; + } else { + /* have to use VAT for overwriting */ + format_flags |= FORMAT_VAT; + } + } else { + /* rare WORM devices, but BluRay has one, strat4096 */ + format_flags |= FORMAT_WORM; + } + } + + /* enable/disable requests */ + if (req_disable & FORMAT_META) { + format_flags &= ~(FORMAT_META | FORMAT_LOW); + req_disable &= ~FORMAT_META; + } + if ((format_flags & FORMAT_VAT) & UDF_512_TRACK) + format_flags |= FORMAT_TRACK512; + + if (req_enable & FORMAT_READONLY) { + format_flags |= FORMAT_READONLY; + } + + /* determine partition/media access type */ + media_accesstype = UDF_ACCESSTYPE_NOT_SPECIFIED; + if (mmc_discinfo.mmc_cur & MMC_CAP_REWRITABLE) { + media_accesstype = UDF_ACCESSTYPE_OVERWRITABLE; + if (mmc_discinfo.mmc_cur & MMC_CAP_ERASABLE) + media_accesstype = UDF_ACCESSTYPE_REWRITEABLE; + } else { + /* all once recordable media */ + media_accesstype = UDF_ACCESSTYPE_WRITE_ONCE; + } + if (mmc_discinfo.mmc_cur & MMC_CAP_PSEUDOOVERWRITE) + media_accesstype = UDF_ACCESSTYPE_PSEUDO_OVERWITE; + + /* patch up media accesstype */ + if (req_enable & FORMAT_READONLY) { + /* better now */ + media_accesstype = UDF_ACCESSTYPE_READ_ONLY; + } + + /* adjust minimum version limits */ + if (format_flags & FORMAT_VAT) + context.min_udf = MAX(context.min_udf, 0x0150); + if (format_flags & FORMAT_SPARABLE) + context.min_udf = MAX(context.min_udf, 0x0150); + if (format_flags & FORMAT_META) + context.min_udf = MAX(context.min_udf, 0x0250); + if (format_flags & FORMAT_LOW) + context.min_udf = MAX(context.min_udf, 0x0260); + + /* adjust maximum version limits not to tease or break things */ + if (!(format_flags & (FORMAT_META | FORMAT_LOW)) && + (context.max_udf > 0x200)) + context.max_udf = 0x201; + + if ((format_flags & (FORMAT_VAT | FORMAT_SPARABLE)) == 0) + if (context.max_udf <= 0x150) + context.min_udf = 0x102; + + /* limit Ecma 167 descriptor if possible/needed */ + context.dscrver = 3; + if ((context.min_udf < 0x200) || (context.max_udf < 0x200)) { + context.dscrver = 2; + context.max_udf = 0x150; /* last version < 0x200 */ + } + + /* is it possible ? */ + if (context.min_udf > context.max_udf) { + (void)printf("Initialisation prohibited by specified maximum " + "UDF version 0x%04x. Minimum version required 0x%04x\n", + context.max_udf, context.min_udf); + return EPERM; + } + + if (!UDF_VERSION(context.min_udf) || !UDF_VERSION(context.max_udf)) { + printf("Choose UDF version numbers from " + "0x102, 0x150, 0x200, 0x201, 0x250 and 0x260\n"); + printf("Default version is 0x201\n"); + return EPERM; + } + + return 0; +} + +#undef UDF_VERSION + + +/* --------------------------------------------------------------------- */ + +int +udf_proces_names(void) +{ + uint32_t primary_nr; + uint64_t volset_nr; + + if (context.logvol_name == NULL) + context.logvol_name = strdup("anonymous"); + if (context.primary_name == NULL) { + if (mmc_discinfo.disc_flags & MMC_DFLAGS_DISCIDVALID) { + primary_nr = mmc_discinfo.disc_id; + } else { + primary_nr = (uint32_t) random(); + } + context.primary_name = calloc(32, 1); + sprintf(context.primary_name, "%08"PRIx32, primary_nr); + } + if (context.volset_name == NULL) { + if (mmc_discinfo.disc_flags & MMC_DFLAGS_BARCODEVALID) { + volset_nr = mmc_discinfo.disc_barcode; + } else { + volset_nr = (uint32_t) random(); + volset_nr |= ((uint64_t) random()) << 32; + } + context.volset_name = calloc(128,1); + sprintf(context.volset_name, "%016"PRIx64, volset_nr); + } + if (context.fileset_name == NULL) + context.fileset_name = strdup("anonymous"); + + /* check passed/created identifiers */ + if (strlen(context.logvol_name) > 128) { + (void)printf("Logical volume name too long\n"); + return EINVAL; + } + if (strlen(context.primary_name) > 32) { + (void)printf("Primary volume name too long\n"); + return EINVAL; + } + if (strlen(context.volset_name) > 128) { + (void)printf("Volume set name too long\n"); + return EINVAL; + } + if (strlen(context.fileset_name) > 32) { + (void)printf("Fileset name too long\n"); + return EINVAL; + } + + /* signal all OK */ + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int +udf_write_iso9660_vrs(void) +{ + struct vrs_desc *iso9660_vrs_desc; + uint32_t pos; + int error, cnt, dpos; + + /* create ISO/Ecma-167 identification descriptors */ + if ((iso9660_vrs_desc = calloc(1, context.sector_size)) == NULL) + return ENOMEM; + + /* + * All UDF formats should have their ISO/Ecma-167 descriptors written + * except when not possible due to track reservation in the case of + * VAT + */ + if ((format_flags & FORMAT_TRACK512) == 0) { + dpos = (2048 + context.sector_size - 1) / context.sector_size; + + /* wipe at least 6 times 2048 byte `sectors' */ + for (cnt = 0; cnt < 6 *dpos; cnt++) { + pos = layout.iso9660_vrs + cnt; + if ((error = udf_write_sector(iso9660_vrs_desc, pos))) { + free(iso9660_vrs_desc); + return error; + } + } + + /* common VRS fields in all written out ISO descriptors */ + iso9660_vrs_desc->struct_type = 0; + iso9660_vrs_desc->version = 1; + pos = layout.iso9660_vrs; + + /* BEA01, NSR[23], TEA01 */ + memcpy(iso9660_vrs_desc->identifier, "BEA01", 5); + if ((error = udf_write_sector(iso9660_vrs_desc, pos))) { + free(iso9660_vrs_desc); + return error; + } + pos += dpos; + + if (context.dscrver == 2) + memcpy(iso9660_vrs_desc->identifier, "NSR02", 5); + else + memcpy(iso9660_vrs_desc->identifier, "NSR03", 5); + ; + if ((error = udf_write_sector(iso9660_vrs_desc, pos))) { + free(iso9660_vrs_desc); + return error; + } + pos += dpos; + + memcpy(iso9660_vrs_desc->identifier, "TEA01", 5); + if ((error = udf_write_sector(iso9660_vrs_desc, pos))) { + free(iso9660_vrs_desc); + return error; + } + } + + free(iso9660_vrs_desc); + /* return success */ + return 0; +} + + +/* --------------------------------------------------------------------- */ + +/* + * Main function that creates and writes out disc contents based on the + * format_flags's that uniquely define the type of disc to create. + */ + +int +udf_do_newfs_prefix(void) +{ + union dscrptr *zero_dscr; + union dscrptr *dscr; + struct mmc_trackinfo ti; + uint32_t sparable_blocks; + uint32_t sector_size, blockingnr; + uint32_t cnt, loc, len; + int sectcopy; + int error, integrity_type; + int data_part, metadata_part; + + /* init */ + sector_size = mmc_discinfo.sector_size; + + /* determine span/size */ + ti.tracknr = mmc_discinfo.first_track_last_session; + error = udf_update_trackinfo(&mmc_discinfo, &ti); + if (error) + return error; + + if (mmc_discinfo.sector_size < context.sector_size) { + fprintf(stderr, "Impossible to format: sectorsize too small\n"); + return EIO; + } + context.sector_size = sector_size; + + /* determine blockingnr */ + blockingnr = ti.packet_size; + if (blockingnr <= 1) { + /* paranoia on blockingnr */ + switch (mmc_discinfo.mmc_profile) { + case 0x08 : /* CDROM */ + case 0x09 : /* CD-R */ + case 0x0a : /* CD-RW */ + blockingnr = 32; /* UDF requirement */ + break; + case 0x10 : /* DVDROM */ + case 0x11 : /* DVD-R (DL) */ + case 0x12 : /* DVD-RAM */ + case 0x1b : /* DVD+R */ + case 0x2b : /* DVD+R Dual layer */ + case 0x13 : /* DVD-RW restricted overwrite */ + case 0x14 : /* DVD-RW sequential */ + blockingnr = 16; /* SCSI definition */ + break; + case 0x40 : /* BDROM */ + case 0x41 : /* BD-R Sequential recording (SRM) */ + case 0x42 : /* BD-R Random recording (RRM) */ + case 0x43 : /* BD-RE */ + case 0x51 : /* HD DVD-R */ + case 0x52 : /* HD DVD-RW */ + blockingnr = 32; /* SCSI definition */ + break; + default: + break; + } + } + if (blockingnr <= 0) { + printf("Can't fixup blockingnumber for device " + "type %d\n", mmc_discinfo.mmc_profile); + + printf("Device is not returning valid blocking" + " number and media type is unknown.\n"); + + return EINVAL; + } + wrtrack_skew = ti.track_start % blockingnr; + + if (mmc_discinfo.mmc_class == MMC_CLASS_CD) { + /* not too much for CD-RW, still 20MiB */ + sparable_blocks = 32; + } else { + /* take a value for DVD*RW mainly, BD is `defect free' */ + sparable_blocks = 512; + } + + /* get layout */ + error = udf_calculate_disc_layout(format_flags, context.min_udf, + wrtrack_skew, + ti.track_start, mmc_discinfo.last_possible_lba, + context.sector_size, blockingnr, sparable_blocks, + meta_fract); + + /* cache partition for we need it often */ + data_part = context.data_part; + metadata_part = context.metadata_part; + + /* Create sparing table descriptor if applicable */ + if (format_flags & FORMAT_SPARABLE) { + if ((error = udf_create_sparing_tabled())) + return error; + + if (check_surface) { + if ((error = udf_surface_check())) + return error; + } + } + + /* Create a generic terminator descriptor (later reused) */ + terminator_dscr = calloc(1, sector_size); + if (terminator_dscr == NULL) + return ENOMEM; + udf_create_terminator(terminator_dscr, 0); + + /* + * Start with wipeout of VRS1 upto start of partition. This allows + * formatting for sequentials with the track reservation and it + * cleans old rubbish on rewritables. For sequentuals without the + * track reservation all is wiped from track start. + */ + if ((zero_dscr = calloc(1, context.sector_size)) == NULL) + return ENOMEM; + + loc = (format_flags & FORMAT_TRACK512) ? layout.vds1 : ti.track_start; + for (; loc < layout.part_start_lba; loc++) { + if ((error = udf_write_sector(zero_dscr, loc))) { + free(zero_dscr); + return error; + } + } + free(zero_dscr); + + /* Create anchors */ + for (cnt = 0; cnt < 3; cnt++) { + if ((error = udf_create_anchor(cnt))) { + return error; + } + } + + /* + * Create the two Volume Descriptor Sets (VDS) each containing the + * following descriptors : primary volume, partition space, + * unallocated space, logical volume, implementation use and the + * terminator + */ + + /* start of volume recognision sequence building */ + context.vds_seq = 0; + + /* Create primary volume descriptor */ + if ((error = udf_create_primaryd())) + return error; + + /* Create partition descriptor */ + if ((error = udf_create_partitiond(context.data_part, media_accesstype))) + return error; + + /* Create unallocated space descriptor */ + if ((error = udf_create_unalloc_spaced())) + return error; + + /* Create logical volume descriptor */ + if ((error = udf_create_logical_dscr(format_flags))) + return error; + + /* Create implementation use descriptor */ + /* TODO input of fields 1,2,3 and passing them */ + if ((error = udf_create_impvold(NULL, NULL, NULL))) + return error; + + /* write out what we've created so far */ + + /* writeout iso9660 vrs */ + if ((error = udf_write_iso9660_vrs())) + return error; + + /* Writeout anchors */ + for (cnt = 0; cnt < 3; cnt++) { + dscr = (union dscrptr *) context.anchors[cnt]; + loc = layout.anchors[cnt]; + if ((error = udf_write_dscr_phys(dscr, loc, 1))) + return error; + + /* sequential media has only one anchor */ + if (format_flags & FORMAT_SEQUENTIAL) + break; + } + + /* write out main and secondary VRS */ + for (sectcopy = 1; sectcopy <= 2; sectcopy++) { + loc = (sectcopy == 1) ? layout.vds1 : layout.vds2; + + /* primary volume descriptor */ + dscr = (union dscrptr *) context.primary_vol; + error = udf_write_dscr_phys(dscr, loc, 1); + if (error) + return error; + loc++; + + /* partition descriptor(s) */ + for (cnt = 0; cnt < UDF_PARTITIONS; cnt++) { + dscr = (union dscrptr *) context.partitions[cnt]; + if (dscr) { + error = udf_write_dscr_phys(dscr, loc, 1); + if (error) + return error; + loc++; + } + } + + /* unallocated space descriptor */ + dscr = (union dscrptr *) context.unallocated; + error = udf_write_dscr_phys(dscr, loc, 1); + if (error) + return error; + loc++; + + /* logical volume descriptor */ + dscr = (union dscrptr *) context.logical_vol; + error = udf_write_dscr_phys(dscr, loc, 1); + if (error) + return error; + loc++; + + /* implementation use descriptor */ + dscr = (union dscrptr *) context.implementation; + error = udf_write_dscr_phys(dscr, loc, 1); + if (error) + return error; + loc++; + + /* terminator descriptor */ + error = udf_write_dscr_phys(terminator_dscr, loc, 1); + if (error) + return error; + loc++; + } + + /* writeout the two sparable table descriptors (if needed) */ + if (format_flags & FORMAT_SPARABLE) { + for (sectcopy = 1; sectcopy <= 2; sectcopy++) { + loc = (sectcopy == 1) ? layout.spt_1 : layout.spt_2; + dscr = (union dscrptr *) context.sparing_table; + len = layout.sparing_table_dscr_lbas; + + /* writeout */ + error = udf_write_dscr_phys(dscr, loc, len); + if (error) + return error; + } + } + + /* + * Create unallocated space bitmap descriptor. Sequential recorded + * media report their own free/used space; no free/used space tables + * should be recorded for these. + */ + if ((format_flags & (FORMAT_SEQUENTIAL | FORMAT_READONLY)) == 0) { + error = udf_create_space_bitmap( + layout.alloc_bitmap_dscr_size, + layout.part_size_lba, + &context.part_unalloc_bits[data_part]); + if (error) + return error; + /* TODO: freed space bitmap if applicable */ + + /* mark space allocated for the unallocated space bitmap */ + udf_mark_allocated(layout.unalloc_space, data_part, + layout.alloc_bitmap_dscr_size); + } + + /* + * Create metadata partition file entries and allocate and init their + * space and free space maps. + */ + if (format_flags & FORMAT_META) { + error = udf_create_space_bitmap( + layout.meta_bitmap_dscr_size, + layout.meta_part_size_lba, + &context.part_unalloc_bits[metadata_part]); + if (error) + return error; + + error = udf_create_meta_files(); + if (error) + return error; + + /* mark space allocated for meta partition and its bitmap */ + udf_mark_allocated(layout.meta_file, data_part, 1); + udf_mark_allocated(layout.meta_mirror, data_part, 1); + udf_mark_allocated(layout.meta_bitmap, data_part, 1); + udf_mark_allocated(layout.meta_part_start_lba, data_part, + layout.meta_part_size_lba); + + /* mark space allocated for the unallocated space bitmap */ + udf_mark_allocated(layout.meta_bitmap_space, data_part, + layout.meta_bitmap_dscr_size); + } + + /* create logical volume integrity descriptor */ + context.num_files = 0; + context.num_directories = 0; + integrity_type = UDF_INTEGRITY_OPEN; + if ((error = udf_create_lvintd(integrity_type))) + return error; + + /* writeout initial open integrity sequence + terminator */ + loc = layout.lvis; + dscr = (union dscrptr *) context.logvol_integrity; + error = udf_write_dscr_phys(dscr, loc, 1); + if (error) + return error; + loc++; + error = udf_write_dscr_phys(terminator_dscr, loc, 1); + if (error) + return error; + + /* create VAT if needed */ + if (format_flags & FORMAT_VAT) { + context.vat_allocated = context.sector_size; + context.vat_contents = malloc(context.vat_allocated); + assert(context.vat_contents); + + udf_prepend_VAT_file(); + } + + /* create FSD and writeout */ + if ((error = udf_create_fsd())) + return error; + udf_mark_allocated(layout.fsd, metadata_part, 1); + + dscr = (union dscrptr *) context.fileset_desc; + error = udf_write_dscr_virt(dscr, layout.fsd, metadata_part, 1); + + return error; +} + + +/* specific routine for newfs to create empty rootdirectory */ +int +udf_do_rootdir(void) { + union dscrptr *root_dscr; + int error; + + /* create root directory and write out */ + assert(context.unique_id == 0x10); + context.unique_id = 0; + if ((error = udf_create_new_rootdir(&root_dscr))) + return error; + udf_mark_allocated(layout.rootdir, context.metadata_part, 1); + + error = udf_write_dscr_virt(root_dscr, + layout.rootdir, context.metadata_part, 1); + + free(root_dscr); + + return error; +} + + +int +udf_do_newfs_postfix(void) +{ + union dscrptr *vat_dscr; + union dscrptr *dscr; + struct long_ad vatdata_pos; + uint32_t loc, len, phys, sects; + int data_part, metadata_part; + int error; + + /* cache partition for we need it often */ + data_part = context.data_part; + metadata_part = context.metadata_part; + + if ((format_flags & FORMAT_SEQUENTIAL) == 0) { + /* update lvint and mark it closed */ + udf_update_lvintd(UDF_INTEGRITY_CLOSED); + + /* overwrite initial terminator */ + loc = layout.lvis+1; + dscr = (union dscrptr *) context.logvol_integrity; + error = udf_write_dscr_phys(dscr, loc, 1); + if (error) + return error; + loc++; + + /* mark end of integrity desciptor sequence again */ + error = udf_write_dscr_phys(terminator_dscr, loc, 1); + if (error) + return error; + } + + /* write out unallocated space bitmap on non sequential media */ + if ((format_flags & (FORMAT_SEQUENTIAL | FORMAT_READONLY)) == 0) { + /* writeout unallocated space bitmap */ + loc = layout.unalloc_space; + dscr = (union dscrptr *) (context.part_unalloc_bits[data_part]); + len = layout.alloc_bitmap_dscr_size; + error = udf_write_dscr_virt(dscr, loc, data_part, len); + if (error) + return error; + } + + if (format_flags & FORMAT_META) { + loc = layout.meta_file; + dscr = (union dscrptr *) context.meta_file; + error = udf_write_dscr_virt(dscr, loc, data_part, 1); + if (error) + return error; + + loc = layout.meta_mirror; + dscr = (union dscrptr *) context.meta_mirror; + error = udf_write_dscr_virt(dscr, loc, data_part, 1); + if (error) + return error; + + loc = layout.meta_bitmap; + dscr = (union dscrptr *) context.meta_bitmap; + error = udf_write_dscr_virt(dscr, loc, data_part, 1); + if (error) + return error; + + /* writeout unallocated space bitmap */ + loc = layout.meta_bitmap_space; + dscr = (union dscrptr *) + (context.part_unalloc_bits[metadata_part]); + len = layout.meta_bitmap_dscr_size; + error = udf_write_dscr_virt(dscr, loc, data_part, len); + if (error) + return error; + } + + /* create a VAT and account for FSD+root */ + vat_dscr = NULL; + if (format_flags & FORMAT_VAT) { + /* update lvint to reflect the newest values (no writeout) */ + udf_update_lvintd(UDF_INTEGRITY_CLOSED); + + error = udf_append_VAT_file(); + if (error) + return error; + + /* write out VAT data */ + sects = UDF_ROUNDUP(context.vat_size, context.sector_size) / + context.sector_size; + layout.vat = context.data_alloc_pos; + udf_data_alloc(sects, &vatdata_pos); + + loc = udf_rw32(vatdata_pos.loc.lb_num); + phys = context.vtop_offset[context.data_part] + loc; + + error = udf_write_phys(context.vat_contents, phys, sects); + if (error) + return error; + loc += sects; + + /* create new VAT descriptor */ + error = udf_create_VAT(&vat_dscr); + if (error) + return error; + context.data_alloc_pos++; + loc++; + + error = udf_write_dscr_virt(vat_dscr, loc, metadata_part, 1); + free(vat_dscr); + if (error) + return error; + } + + /* done */ + return 0; +} diff --git a/sbin/newfs_udf/udf_write.h b/sbin/newfs_udf/udf_write.h new file mode 100644 index 000000000..d2fd8ac0c --- /dev/null +++ b/sbin/newfs_udf/udf_write.h @@ -0,0 +1,54 @@ +/* $NetBSD: udf_write.h,v 1.4 2013/08/05 20:52:08 reinoud Exp $ */ + +/* + * Copyright (c) 2006, 2008, 2013 Reinoud Zandijk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _FS_UDF_UDF_WRITE_H_ +#define _FS_UDF_UDF_WRITE_H_ + +#include "udf_create.h" +#if !HAVE_NBTOOL_CONFIG_H +#define _EXPOSE_MMC +#include +#else +#include "udf/cdio_mmc_structs.h" +#endif + +/* prototypes */ + +int udf_write_dscr_virt(union dscrptr *dscr, uint32_t location, uint32_t vpart, + uint32_t sects); +void udf_metadata_alloc(int nblk, struct long_ad *pos); +void udf_data_alloc(int nblk, struct long_ad *pos); + +int udf_derive_format(int req_enable, int req_disable, int force); +int udf_proces_names(void); + +int udf_do_newfs_prefix(void); +int udf_do_rootdir(void); +int udf_do_newfs_postfix(void); + +#endif /* _FS_UDF_UDF_WRITE_H_ */ diff --git a/sbin/newfs_udf/unicode.h b/sbin/newfs_udf/unicode.h new file mode 100644 index 000000000..f54f32076 --- /dev/null +++ b/sbin/newfs_udf/unicode.h @@ -0,0 +1,161 @@ +/* $NetBSD: unicode.h,v 1.1 2013/08/05 14:11:30 reinoud Exp $ */ + +/*- + * Copyright (c) 2001, 2004 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Paul Borman at Krystal Technologies. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Routines for handling Unicode encoded in UTF-8 form, code derived from + * src/lib/libc/locale/utf2.c. + */ +static u_int16_t wget_utf8(const char **, size_t *) __unused; +static int wput_utf8(char *, size_t, u_int16_t) __unused; + +/* + * Read one UTF8-encoded character off the string, shift the string pointer + * and return the character. + */ +static u_int16_t +wget_utf8(const char **str, size_t *sz) +{ + unsigned int c; + u_int16_t rune = 0; + const char *s = *str; + static const int _utf_count[16] = { + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 2, 2, 3, 0, + }; + + /* must be called with at least one byte remaining */ + assert(*sz > 0); + + c = _utf_count[(s[0] & 0xf0) >> 4]; + if (c == 0 || c > *sz) { + decoding_error: + /* + * The first character is in range 128-255 and doesn't + * mark valid a valid UTF-8 sequence. There is not much + * we can do with this, so handle by returning + * the first character as if it would be a correctly + * encoded ISO-8859-1 character. + */ + c = 1; + } + + switch (c) { + case 1: + rune = s[0] & 0xff; + break; + case 2: + if ((s[1] & 0xc0) != 0x80) + goto decoding_error; + rune = ((s[0] & 0x1F) << 6) | (s[1] & 0x3F); + break; + case 3: + if ((s[1] & 0xC0) != 0x80 || (s[2] & 0xC0) != 0x80) + goto decoding_error; + rune = ((s[0] & 0x0F) << 12) | ((s[1] & 0x3F) << 6) + | (s[2] & 0x3F); + break; + } + + *str += c; + *sz -= c; + return rune; +} + +/* + * Encode wide character and write it to the string. 'n' specifies + * how much buffer space remains in 's'. Returns number of bytes written + * to the target string 's'. + */ +static int +wput_utf8(char *s, size_t n, u_int16_t wc) +{ + if (wc & 0xf800) { + if (n < 3) { + /* bound check failure */ + return 0; + } + + s[0] = 0xE0 | (wc >> 12); + s[1] = 0x80 | ((wc >> 6) & 0x3F); + s[2] = 0x80 | ((wc) & 0x3F); + return 3; + } else if (wc & 0x0780) { + if (n < 2) { + /* bound check failure */ + return 0; + } + + s[0] = 0xC0 | (wc >> 6); + s[1] = 0x80 | ((wc) & 0x3F); + return 2; + } else { + if (n < 1) { + /* bound check failure */ + return 0; + } + + s[0] = wc; + return 1; + } +} diff --git a/sbin/newfs_v7fs/Makefile b/sbin/newfs_v7fs/Makefile new file mode 100644 index 000000000..961a05c66 --- /dev/null +++ b/sbin/newfs_v7fs/Makefile @@ -0,0 +1,21 @@ +# $NetBSD: Makefile,v 1.4 2012/09/05 23:01:42 riz Exp $ + +.include + +V7FS = ${NETBSDSRCDIR}/sys/fs/v7fs +PROG= newfs_v7fs +MAN= newfs_v7fs.8 +SRCS= newfs_v7fs.c main.c v7fs_endian.c v7fs_superblock.c v7fs_inode.c \ +v7fs_datablock.c v7fs_dirent.c v7fs_io.c v7fs_io_user.c progress.c + +# use progress meter. +FSCK= ${NETBSDSRCDIR}/sbin/fsck + +DPADD+= ${LIBUTIL} +LDADD+= -lutil +CPPFLAGS+=-DV7FS_EI -I${V7FS} -I${FSCK} +.PATH: ${V7FS} ${FSCK} + +COPTS.newfs_v7fs.c+= -Wno-pointer-sign + +.include diff --git a/sbin/newfs_v7fs/main.c b/sbin/newfs_v7fs/main.c new file mode 100644 index 000000000..a31ebdeb5 --- /dev/null +++ b/sbin/newfs_v7fs/main.c @@ -0,0 +1,316 @@ +/* $NetBSD: main.c,v 1.10 2011/08/10 11:31:49 uch Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#ifndef lint +__RCSID("$NetBSD: main.c,v 1.10 2011/08/10 11:31:49 uch Exp $"); +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include + +#include "v7fs.h" +#include "v7fs_impl.h" +#include "v7fs_endian.h" +#include "v7fs_superblock.h" +#include "v7fs_inode.h" +#include "v7fs_datablock.h" /*v7fs_datablock_expand/last */ +#include "newfs_v7fs.h" +#include "progress.h" /*../sbin/fsck */ + +#define VPRINTF(lv, fmt, args...) { if (v7fs_newfs_verbose >= lv) \ + printf(fmt, ##args); } + +static v7fs_daddr_t +determine_ilist_size(v7fs_daddr_t volume_size, int32_t files) +{ + v7fs_daddr_t ilist_size; + + if (files) + ilist_size = howmany(files, V7FS_INODE_PER_BLOCK); + else + ilist_size = volume_size / 25; /* 4% */ + if (ilist_size > (v7fs_daddr_t)V7FS_ILISTBLK_MAX) + ilist_size = V7FS_ILISTBLK_MAX; + + return ilist_size; +} + +static int +partition_check(struct v7fs_self *fs) +{ + struct v7fs_superblock *sb = &fs->superblock; + int error; + + if ((error = v7fs_superblock_load(fs))) { + if (error != EINVAL) { + /* Invalid superblock information is OK. */ + warnx("Can't read superblock sector."); + } + } + sb->modified = 1; + if ((error = v7fs_superblock_writeback(fs))) { + if (errno == EROFS) { + warnx("Overwriting disk label? "); + } + warnx("Can't write superblock sector."); + } + + return error; +} + +static int +make_root(struct v7fs_self *fs) +{ + struct v7fs_inode inode; + struct v7fs_dirent *dir; + int error; + + /* INO 1 badblk (don't used) */ + memset(&inode, 0, sizeof(inode)); + inode.inode_number = 1; + inode.mode = V7FS_IFREG; /* V7 manner */ + v7fs_inode_writeback(fs, &inode); + + /* INO 2 root */ + v7fs_ino_t ino; + if ((error = v7fs_inode_allocate(fs, &ino))) { + errno = error; + warn("Can't allocate / inode"); + return error; + } + + memset(&inode, 0, sizeof(inode)); + inode.inode_number = ino; + inode.mode = 0777 | V7FS_IFDIR; + inode.uid = 0; + inode.gid = 0; + inode.nlink = 2; /* . + .. */ + inode.atime = inode.mtime = inode.ctime = time(0); + + /* root dirent. */ + v7fs_datablock_expand(fs, &inode, sizeof(*dir) * 2); + v7fs_daddr_t blk = inode.addr[0]; + void *buf; + if (!(buf = scratch_read(fs, blk))) { + v7fs_inode_deallocate(fs, ino); + errno = error = EIO; + warn("Can't read / dirent."); + return error; + } + dir = (struct v7fs_dirent *)buf; /*disk endian */ + + strcpy(dir[0].name, "."); + dir[0].inode_number = V7FS_VAL16(fs, ino); + strcpy(dir[1].name, ".."); + dir[1].inode_number = V7FS_VAL16(fs, ino); + if (!fs->io.write(fs->io.cookie, buf, blk)) {/*writeback */ + scratch_free(fs, buf); + errno = error = EIO; + warn("Can't write / dirent."); + return error; + } + scratch_free(fs, buf); + v7fs_inode_writeback(fs, &inode); + if ((error = v7fs_superblock_writeback(fs))) { + errno = error; + warnx("Can't write superblock."); + } + + return error; +} + +static v7fs_daddr_t +make_freeblocklist(struct v7fs_self *fs, v7fs_daddr_t listblk, uint8_t *buf) +{ + uint32_t (*val32)(uint32_t) = fs->val.conv32; + uint16_t (*val16)(uint16_t) = fs->val.conv16; + struct v7fs_freeblock *fb = (struct v7fs_freeblock *)buf; + int i, j, k; + + memset(buf, 0, V7FS_BSIZE); + + for (i = V7FS_MAX_FREEBLOCK - 1, j = listblk + 1, k = 0; i >= 0; + i--, j++, k++) { + progress(0); + if (j == (int32_t)fs->superblock.volume_size) + { + VPRINTF(4, "\nlast freeblock #%d\n", + (*val32)(fb->freeblock[i + 1])); + + memmove(fb->freeblock + 1, fb->freeblock + i + 1, k * + sizeof(v7fs_daddr_t)); + fb->freeblock[0] = 0; /* Terminate link; */ + fb->nfreeblock = (*val16)(k + 1); + VPRINTF(4, "last freeblock contains #%d\n", + (*val16)(fb->nfreeblock)); + fs->io.write(fs->io.cookie, buf, listblk); + return 0; + } + fb->freeblock[i] = (*val32)(j); + } + fb->nfreeblock = (*val16)(k); + + if (!fs->io.write(fs->io.cookie, buf, listblk)) { + errno = EIO; + warn("blk=%ld", (long)listblk); + return 0; + } + + /* Return next link block */ + return (*val32)(fb->freeblock[0]); +} + +static int +make_filesystem(struct v7fs_self *fs, v7fs_daddr_t volume_size, + v7fs_daddr_t ilist_size) +{ + struct v7fs_superblock *sb; + v7fs_daddr_t blk; + uint8_t buf[V7FS_BSIZE]; + int error = 0; + int32_t i, j; + + /* Setup ilist. (ilist must be zero filled. becuase of they are free) */ + VPRINTF(4, "Zero clear ilist.\n"); + progress(&(struct progress_arg){ .label = "zero ilist", .tick = + ilist_size / PROGRESS_BAR_GRANULE }); + memset(buf, 0, sizeof buf); + for (i = V7FS_ILIST_SECTOR; i < (int32_t)ilist_size; i++) { + fs->io.write(fs->io.cookie, buf, i); + progress(0); + } +#ifndef HAVE_NBTOOL_CONFIG_H + progress_done(); +#endif + VPRINTF(4, "\n"); + + /* Construct superblock */ + sb = &fs->superblock; + sb->volume_size = volume_size; + sb->datablock_start_sector = ilist_size + V7FS_ILIST_SECTOR; + sb->update_time = time(NULL); + + /* fill free inode cache. */ + VPRINTF(4, "Setup inode cache.\n"); + sb->nfreeinode = V7FS_MAX_FREEINODE; + for (i = V7FS_MAX_FREEINODE - 1, j = V7FS_ROOT_INODE; i >= 0; i--, j++) + sb->freeinode[i] = j; + sb->total_freeinode = ilist_size * V7FS_INODE_PER_BLOCK - 1; + + /* fill free block cache. */ + VPRINTF(4, "Setup free block cache.\n"); + sb->nfreeblock = V7FS_MAX_FREEBLOCK; + for (i = V7FS_MAX_FREEBLOCK - 1, j = sb->datablock_start_sector; i >= 0; + i--, j++) + sb->freeblock[i] = j; + + sb->total_freeblock = volume_size - sb->datablock_start_sector; + + /* Write superblock. */ + sb->modified = 1; + if ((error = v7fs_superblock_writeback(fs))) { + errno = error; + warn("Can't write back superblock."); + return error; + } + + /* Construct freeblock list */ + VPRINTF(4, "Setup whole freeblock list.\n"); + progress(&(struct progress_arg){ .label = "freeblock list", .tick = + (volume_size - sb->datablock_start_sector) / PROGRESS_BAR_GRANULE}); + blk = sb->freeblock[0]; + while ((blk = make_freeblocklist(fs, blk, buf))) + continue; +#ifndef HAVE_NBTOOL_CONFIG_H + progress_done(); +#endif + + VPRINTF(4, "done.\n"); + + return 0; +} + +int +v7fs_newfs(const struct v7fs_mount_device *mount, int32_t maxfile) +{ + struct v7fs_self *fs; + v7fs_daddr_t ilist_size; + int error; + v7fs_daddr_t volume_size = mount->sectors; + + /* Check and determine ilistblock, datablock size. */ + if (volume_size > V7FS_DADDR_MAX + 1) { + warnx("volume size %d over v7fs limit %d. truncated.", + volume_size, V7FS_DADDR_MAX + 1); + volume_size = V7FS_DADDR_MAX + 1; + } + + ilist_size = determine_ilist_size(volume_size, maxfile); + + VPRINTF(1, "volume size=%d, ilist size=%d, endian=%d, NAME_MAX=%d\n", + volume_size, ilist_size, mount->endian, V7FS_NAME_MAX); + + /* Setup I/O ops. */ + if ((error = v7fs_io_init(&fs, mount, V7FS_BSIZE))) { + errno = error; + warn("I/O setup failed."); + return error; + } + fs->endian = mount->endian; + v7fs_endian_init(fs); + + if ((error = partition_check(fs))) { + return error; + } + + /* Construct filesystem. */ + if ((error = make_filesystem(fs, volume_size, ilist_size))) { + return error; + } + + /* Setup root. */ + if ((error = make_root(fs))) { + return error; + } + + v7fs_io_fini(fs); + + return 0; +} diff --git a/sbin/newfs_v7fs/newfs_v7fs.8 b/sbin/newfs_v7fs/newfs_v7fs.8 new file mode 100644 index 000000000..7dd3669d2 --- /dev/null +++ b/sbin/newfs_v7fs/newfs_v7fs.8 @@ -0,0 +1,130 @@ +.\" $NetBSD: newfs_v7fs.8,v 1.3 2011/08/10 11:31:49 uch Exp $ +.\" +.\" Copyright (c) 2011 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by UCHIYAMA Yasushi. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.\" Copyright (c) 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)newlfs.8 8.1 (Berkeley) 6/19/93 +.\" +.Dd April 29, 2011 +.Dt NEWFS_V7FS 8 +.Os +.Sh NAME +.Nm newfs_v7fs +.Nd construct a new 7th Edition(V7) File System +.Sh SYNOPSIS +.Nm +.Op Fl FZ +.Op Fl B Ar byte-order +.Op Fl n Ar inodes +.Op Fl s Ar sectors +.Op Fl V Ar verbose +.Ar special +.Sh DESCRIPTION +.Nm +builds a 7th Edition(V7) file system on the specified +.Ar special . +If it is a device, the size information will be taken from the disk label and +before running +.Nm +the disk must be labeled using +.Xr disklabel 8 ; +the proper fstype is +.Dq Version 7 . +Otherwise, the size must be specified on the command line. +V7 filesystem's block size and sector size are 512 byte. +Disk address limits are 24 bit. +.Pp +The following arguments are supported: +.Bl -tag -width XBXbyteXorderXX +.It Fl B Ar byte-order +Specify the metadata byte order of the file system to be created. +Valid byte orders are +.Sq be , +.Sq le , +and +.Sq pdp . +If no byte order is specified, the file system is created in host +byte order. +.It Fl F +Create file system to a regular file (needs the +.Fl s +option). +.It Fl n Ar inodes +This specifies the number of inodes for the filesystem. +If the number of inodes exceeds 65536, it is reduced to 65536. +.It Fl s Ar sectors +Create file system with specified number of disk sectors. +.It Fl V Ar verbose +This controls the amount of information written to stdout: +.Bl -tag -width 3n -offset indent -compact +.It 0 +No output. +.It 1 +Overall size, ilist size, endian and filename length. +.It 2 +A progress bar. +.It 3 +.It 4 +More verbose message. +.El +The default is 3. +.It Fl Z +Fill file with zeroes instead of creating a sparse file. +.El +.Sh SEE ALSO +.Xr disklabel 5 , +.Xr disktab 5 , +.\" .Xr fs 5 , +.Xr disklabel 8 , +.Xr diskpart 8 diff --git a/sbin/newfs_v7fs/newfs_v7fs.c b/sbin/newfs_v7fs/newfs_v7fs.c new file mode 100644 index 000000000..565b5fdcb --- /dev/null +++ b/sbin/newfs_v7fs/newfs_v7fs.c @@ -0,0 +1,240 @@ +/* $NetBSD: newfs_v7fs.c,v 1.3 2011/08/10 12:13:20 wiz Exp $ */ + +/*- + * Copyright (c) 2004, 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#ifndef lint +__RCSID("$NetBSD: newfs_v7fs.c,v 1.3 2011/08/10 12:13:20 wiz Exp $"); +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "v7fs_impl.h" +#include "progress.h" +#include "newfs_v7fs.h" + +static void usage(void) __dead; +static bool progress_bar_enable = false; +int v7fs_newfs_verbose = 3; /* newfs compatible */ + +int +main(int argc, char **argv) +{ + const char *device; + struct disklabel d; + struct partition *p; + struct stat st; + uint32_t partsize; + int Fflag, Zflag; + int part; + int fd, ch; + int endian = _BYTE_ORDER; + int32_t maxfile = 0; + + if (argc < 2) + usage(); + + Fflag = Zflag = partsize = 0; + while ((ch = getopt(argc, argv, "Fs:Zs:n:B:V:")) != -1) { + switch (ch) { + case 'V': + v7fs_newfs_verbose = atoi(optarg); + break; + case 'F': + Fflag = 1; + break; + case 's': + partsize = atoi(optarg); + break; + case 'n': + maxfile = atoi(optarg); + break; + case 'Z': + Zflag = 1; + break; + case 'B': + switch (optarg[0]) { + case 'l': + endian = _LITTLE_ENDIAN; + break; + case 'b': + endian = _BIG_ENDIAN; + break; + case 'p': + endian = _PDP_ENDIAN; + break; + } + break; + default: + usage(); + /*NOTREACHED*/ + } + } + argc -= optind; + argv += optind; + + if (argc != 1) + usage(); + device = argv[0]; + + progress_bar_enable = v7fs_newfs_verbose > 1; + + + if (progress_bar_enable) { + progress_switch(progress_bar_enable); + progress_init(); + progress(&(struct progress_arg){ .cdev = device }); + } + + if (!Fflag) { + if ((fd = open(device, O_RDWR)) == -1) { + err(EXIT_FAILURE, "%s", device); + } + if (fstat(fd, &st) != 0) { + goto err_exit; + } + if (!S_ISCHR(st.st_mode)) { + warnx("not a raw device.\n"); + } + + part = DISKPART(st.st_rdev); + + if (ioctl(fd, DIOCGDINFO, &d) == -1) { + goto err_exit; + } + p = &d.d_partitions[part]; + if (v7fs_newfs_verbose) { + printf("partition=%d size=%d offset=%d fstype=%d" + " secsize=%d\n", part, p->p_size, p->p_offset, + p->p_fstype, d.d_secsize); + } + if (p->p_fstype != FS_V7) { + warnx("not a Version 7 partition."); + goto err_exit; + } + partsize = p->p_size; + } else { + off_t filesize; + uint8_t zbuf[8192] = {0, }; + + if (partsize == 0) { + errx(EXIT_FAILURE, "-F requires -s"); + } + + filesize = partsize << V7FS_BSHIFT; + + fd = open(device, O_RDWR|O_CREAT|O_TRUNC, 0666); + if (fd == -1) { + err(EXIT_FAILURE, "%s", device); + } + + if (Zflag) { + while (filesize > 0) { + size_t writenow = MIN(filesize, + (off_t)sizeof(zbuf)); + + if ((size_t)write(fd, zbuf, writenow) != + writenow) { + err(EXIT_FAILURE, NULL); + } + filesize -= writenow; + } + } else { + if (lseek(fd, filesize - 1, SEEK_SET) == -1) { + goto err_exit; + } + if (write(fd, zbuf, 1) != 1) { + goto err_exit; + } + if (lseek(fd, 0, SEEK_SET) == -1) { + goto err_exit; + } + } + } + + if (v7fs_newfs(&(struct v7fs_mount_device) + { .device.fd = fd, .endian = endian, .sectors = partsize }, + maxfile) != 0) + goto err_exit; + + close(fd); + + return EXIT_SUCCESS; + err_exit: + close(fd); + err(EXIT_FAILURE, NULL); +} + +void +progress(const struct progress_arg *p) +{ + static struct progress_arg Progress; + static char cdev[32]; + static char label[32]; + + if (!progress_bar_enable) + return; + + if (p) { + Progress = *p; + if (p->cdev) + strcpy(cdev, p->cdev); + if (p->label) + strcpy(label, p->label); + } + + if (!Progress.tick) + return; + if (++Progress.cnt > Progress.tick) { + Progress.cnt = 0; + Progress.total++; + progress_bar(cdev, label, Progress.total, PROGRESS_BAR_GRANULE); + } +} + +static void +usage(void) +{ + + (void)fprintf(stderr, "usage: \n%s [-FZ] [-B byte-order]" + " [-n inodes] [-s sectors] [-V verbose] special\n", getprogname()); + + exit(EXIT_FAILURE); +} diff --git a/sbin/newfs_v7fs/newfs_v7fs.h b/sbin/newfs_v7fs/newfs_v7fs.h new file mode 100644 index 000000000..52ddcea11 --- /dev/null +++ b/sbin/newfs_v7fs/newfs_v7fs.h @@ -0,0 +1,48 @@ +/* $NetBSD: newfs_v7fs.h,v 1.2 2011/08/10 11:31:49 uch Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SBIN_NEWFS_V7FS_NEWFS_V7FS_H_ +#define _SBIN_NEWFS_V7FS_NEWFS_V7FS_H_ + +#define PROGRESS_BAR_GRANULE 100 +struct progress_arg { + const char *cdev; + const char *label; + off_t tick; + off_t cnt; + off_t total; +}; +__BEGIN_DECLS +void progress(const struct progress_arg *); +int v7fs_newfs(const struct v7fs_mount_device *, int32_t); +extern int v7fs_newfs_verbose; +__END_DECLS +#endif /* !_SBIN_NEWFS_V7FS_NEWFS_V7FS_H_ */ diff --git a/sys/fs/Makefile b/sys/fs/Makefile index 42a39fb7f..1cb94a587 100644 --- a/sys/fs/Makefile +++ b/sys/fs/Makefile @@ -1,6 +1,6 @@ # $NetBSD: Makefile,v 1.20 2011/06/27 11:52:24 uch Exp $ SUBDIR= cd9660 msdosfs \ - puffs + puffs udf v7fs .include diff --git a/sys/fs/udf/Makefile b/sys/fs/udf/Makefile new file mode 100644 index 000000000..02c65a12b --- /dev/null +++ b/sys/fs/udf/Makefile @@ -0,0 +1,7 @@ +# $NetBSD: Makefile,v 1.1 2006/02/02 15:19:15 reinoud Exp $ + +INCSDIR= /usr/include/fs/udf + +INCS= ecma167-udf.h udf_mount.h + +.include diff --git a/sys/fs/udf/ecma167-udf.h b/sys/fs/udf/ecma167-udf.h new file mode 100644 index 000000000..c52b99de8 --- /dev/null +++ b/sys/fs/udf/ecma167-udf.h @@ -0,0 +1,835 @@ +/* $NetBSD: ecma167-udf.h,v 1.14 2011/07/07 17:45:38 reinoud Exp $ */ + +/*- + * Copyright (c) 2003, 2004, 2005, 2006, 2008, 2009 + * Reinoud Zandijk * + * Copyright (c) 2001, 2002 Scott Long + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * Extended and adapted for UDFv2.50+ bij Reinoud Zandijk based on the + * original by Scott Long. + * + * 20030508 Made some small typo and explanatory comments + * 20030510 Added UDF 2.01 structures + * 20030519 Added/correct comments on multi-partitioned logical volume space + * 20050616 Added pseudo overwrite + * 20050624 Added the missing extended attribute types and `magic values'. + * 20051106 Reworked some implementation use parts + * + */ + + +#ifndef _FS_UDF_ECMA167_UDF_H_ +#define _FS_UDF_ECMA167_UDF_H_ + + +/* + * in case of an older gcc versions, define the __packed as explicit + * attribute + */ + +/* + * You may specify the `aligned' and `transparent_union' attributes either in + * a `typedef' declaration or just past the closing curly brace of a complete + * enum, struct or union type _definition_ and the `packed' attribute only + * past the closing brace of a definition. You may also specify attributes + * between the enum, struct or union tag and the name of the type rather than + * after the closing brace. +*/ + +#ifndef __packed +#define __packed __attribute__((packed)) +#endif + + +/* ecma167-udf.h */ + +/* Volume recognition sequence ECMA 167 rev. 3 16.1 */ +struct vrs_desc { + uint8_t struct_type; + uint8_t identifier[5]; + uint8_t version; + uint8_t data[2041]; +} __packed; + + +#define VRS_NSR02 "NSR02" +#define VRS_NSR03 "NSR03" +#define VRS_BEA01 "BEA01" +#define VRS_TEA01 "TEA01" +#define VRS_CD001 "CD001" +#define VRS_CDW02 "CDW02" + + +/* Structure/definitions/constants a la ECMA 167 rev. 3 */ + + +#define MAX_TAGID_VOLUMES 9 +/* Tag identifiers */ +enum { + TAGID_SPARING_TABLE = 0, + TAGID_PRI_VOL = 1, + TAGID_ANCHOR = 2, + TAGID_VOL = 3, + TAGID_IMP_VOL = 4, + TAGID_PARTITION = 5, + TAGID_LOGVOL = 6, + TAGID_UNALLOC_SPACE = 7, + TAGID_TERM = 8, + TAGID_LOGVOL_INTEGRITY= 9, + TAGID_FSD = 256, + TAGID_FID = 257, + TAGID_ALLOCEXTENT = 258, + TAGID_INDIRECTENTRY = 259, + TAGID_ICB_TERM = 260, + TAGID_FENTRY = 261, + TAGID_EXTATTR_HDR = 262, + TAGID_UNALL_SP_ENTRY = 263, + TAGID_SPACE_BITMAP = 264, + TAGID_PART_INTEGRITY = 265, + TAGID_EXTFENTRY = 266, + TAGID_MAX = 266 +}; + + +enum { + UDF_DOMAIN_FLAG_HARD_WRITE_PROTECT = 1, + UDF_DOMAIN_FLAG_SOFT_WRITE_PROTECT = 2 +}; + + +enum { + UDF_ACCESSTYPE_NOT_SPECIFIED = 0, /* unknown */ + UDF_ACCESSTYPE_PSEUDO_OVERWITE = 0, /* pseudo overwritable, e.g. BD-R's LOW */ + UDF_ACCESSTYPE_READ_ONLY = 1, /* really only readable */ + UDF_ACCESSTYPE_WRITE_ONCE = 2, /* write once and you're done */ + UDF_ACCESSTYPE_REWRITEABLE = 3, /* may need extra work to rewrite */ + UDF_ACCESSTYPE_OVERWRITABLE = 4 /* no limits on rewriting; e.g. harddisc*/ +}; + + +/* Descriptor tag [3/7.2] */ +struct desc_tag { + uint16_t id; + uint16_t descriptor_ver; + uint8_t cksum; + uint8_t reserved; + uint16_t serial_num; + uint16_t desc_crc; + uint16_t desc_crc_len; + uint32_t tag_loc; +} __packed; +#define UDF_DESC_TAG_LENGTH 16 + + +/* Recorded Address [4/7.1] */ +struct lb_addr { /* within partition space */ + uint32_t lb_num; + uint16_t part_num; +} __packed; + + +/* Extent Descriptor [3/7.1] */ +struct extent_ad { + uint32_t len; + uint32_t loc; +} __packed; + + +/* Short Allocation Descriptor [4/14.14.1] */ +struct short_ad { + uint32_t len; + uint32_t lb_num; +} __packed; + + +/* Long Allocation Descriptor [4/14.14.2] */ +struct UDF_ADImp_use { + uint16_t flags; + uint32_t unique_id; +} __packed; +#define UDF_ADIMP_FLAGS_EXTENT_ERASED 1 + + +struct long_ad { + uint32_t len; + struct lb_addr loc; /* within a logical volume mapped partition space !! */ + union { + uint8_t bytes[6]; + struct UDF_ADImp_use im_used; + } impl; +} __packed; +#define longad_uniqueid impl.im_used.unique_id + + +/* Extended Allocation Descriptor [4/14.14.3] ; identifies an extent of allocation descriptors ; also in UDF ? */ +struct ext_ad { + uint32_t ex_len; + uint32_t rec_len; + uint32_t inf_len; + struct lb_addr ex_loc; + uint8_t reserved[2]; +} __packed; + + +/* ICB : Information Control Block; positioning */ +union icb { + struct short_ad s_ad; + struct long_ad l_ad; + struct ext_ad e_ad; +}; + + +/* short/long/ext extent have flags encoded in length */ +#define UDF_EXT_ALLOCATED (0<<30) +#define UDF_EXT_FREED (1<<30) +#define UDF_EXT_ALLOCATED_BUT_NOT_USED (1<<30) +#define UDF_EXT_FREE (2<<30) +#define UDF_EXT_REDIRECT (3<<30) +#define UDF_EXT_FLAGS(len) ((len) & (3<<30)) +#define UDF_EXT_LEN(len) ((len) & ((1<<30)-1)) +#define UDF_EXT_MAXLEN ((1<<30)-1) + + +/* Character set spec [1/7.2.1] */ +struct charspec { + uint8_t type; + uint8_t inf[63]; +} __packed; + + +struct pathcomp { + uint8_t type; + uint8_t l_ci; + uint16_t comp_filever; + uint8_t ident[256]; +} __packed; +#define UDF_PATH_COMP_SIZE 4 +#define UDF_PATH_COMP_RESERVED 0 +#define UDF_PATH_COMP_ROOT 1 +#define UDF_PATH_COMP_MOUNTROOT 2 +#define UDF_PATH_COMP_PARENTDIR 3 +#define UDF_PATH_COMP_CURDIR 4 +#define UDF_PATH_COMP_NAME 5 + + +/* Timestamp [1/7.3] */ +struct timestamp { + uint16_t type_tz; + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; + uint8_t centisec; + uint8_t hund_usec; + uint8_t usec; +} __packed; +#define UDF_TIMESTAMP_SIZE 12 + + +/* Entity Identifier [1/7.4] */ +#define UDF_REGID_ID_SIZE 23 +struct regid { + uint8_t flags; + uint8_t id[UDF_REGID_ID_SIZE]; + uint8_t id_suffix[8]; +} __packed; + + +/* ICB Tag [4/14.6] */ +struct icb_tag { + uint32_t prev_num_dirs; + uint16_t strat_type; + uint8_t strat_param[2]; + uint16_t max_num_entries; + uint8_t reserved; + uint8_t file_type; + struct lb_addr parent_icb; + uint16_t flags; +} __packed; +#define UDF_ICB_TAG_FLAGS_ALLOC_MASK 0x03 +#define UDF_ICB_SHORT_ALLOC 0x00 +#define UDF_ICB_LONG_ALLOC 0x01 +#define UDF_ICB_EXT_ALLOC 0x02 +#define UDF_ICB_INTERN_ALLOC 0x03 + +#define UDF_ICB_TAG_FLAGS_DIRORDERED (1<< 3) +#define UDF_ICB_TAG_FLAGS_NONRELOC (1<< 4) +#define UDF_ICB_TAG_FLAGS_CONTIGUES (1<< 9) +#define UDF_ICB_TAG_FLAGS_MULTIPLEVERS (1<<12) + +#define UDF_ICB_TAG_FLAGS_SETUID (1<< 6) +#define UDF_ICB_TAG_FLAGS_SETGID (1<< 7) +#define UDF_ICB_TAG_FLAGS_STICKY (1<< 8) + +#define UDF_ICB_FILETYPE_UNKNOWN 0 +#define UDF_ICB_FILETYPE_UNALLOCSPACE 1 +#define UDF_ICB_FILETYPE_PARTINTEGRITY 2 +#define UDF_ICB_FILETYPE_INDIRECTENTRY 3 +#define UDF_ICB_FILETYPE_DIRECTORY 4 +#define UDF_ICB_FILETYPE_RANDOMACCESS 5 +#define UDF_ICB_FILETYPE_BLOCKDEVICE 6 +#define UDF_ICB_FILETYPE_CHARDEVICE 7 +#define UDF_ICB_FILETYPE_EXTATTRREC 8 +#define UDF_ICB_FILETYPE_FIFO 9 +#define UDF_ICB_FILETYPE_SOCKET 10 +#define UDF_ICB_FILETYPE_TERM 11 +#define UDF_ICB_FILETYPE_SYMLINK 12 +#define UDF_ICB_FILETYPE_STREAMDIR 13 +#define UDF_ICB_FILETYPE_VAT 248 +#define UDF_ICB_FILETYPE_REALTIME 249 +#define UDF_ICB_FILETYPE_META_MAIN 250 +#define UDF_ICB_FILETYPE_META_MIRROR 251 +#define UDF_ICB_FILETYPE_META_BITMAP 252 + + +/* Anchor Volume Descriptor Pointer [3/10.2] */ +struct anchor_vdp { + struct desc_tag tag; + struct extent_ad main_vds_ex; /* to main volume descriptor set ; 16 sectors min */ + struct extent_ad reserve_vds_ex; /* copy of main volume descriptor set ; 16 sectors min */ +} __packed; + + +/* Volume Descriptor Pointer [3/10.3] */ +struct vol_desc_ptr { + struct desc_tag tag; /* use for extending the volume descriptor space */ + uint32_t vds_number; + struct extent_ad next_vds_ex; /* points to the next block for volume descriptor space */ +} __packed; + + +/* Primary Volume Descriptor [3/10.1] */ +struct pri_vol_desc { + struct desc_tag tag; + uint32_t seq_num; /* MAX prevail */ + uint32_t pvd_num; /* assigned by author; 0 is special as in it may only occur once */ + char vol_id[32]; /* KEY ; main identifier of this disc */ + uint16_t vds_num; /* volume descriptor number; i.e. what volume number is it */ + uint16_t max_vol_seq; /* maximum volume descriptor number known */ + uint16_t ichg_lvl; + uint16_t max_ichg_lvl; + uint32_t charset_list; + uint32_t max_charset_list; + char volset_id[128]; /* KEY ; if part of a multi-disc set or a band of volumes */ + struct charspec desc_charset; /* KEY according to ECMA 167 */ + struct charspec explanatory_charset; + struct extent_ad vol_abstract; + struct extent_ad vol_copyright; + struct regid app_id; + struct timestamp time; + struct regid imp_id; + uint8_t imp_use[64]; + uint32_t prev_vds_loc; /* location of predecessor _lov ? */ + uint16_t flags; /* bit 0 : if set indicates volume set name is meaningful */ + uint8_t reserved[22]; +} __packed; + + +/* UDF specific implementation use part of the implementation use volume descriptor */ +struct udf_lv_info { + struct charspec lvi_charset; + char logvol_id[128]; + + char lvinfo1[36]; + char lvinfo2[36]; + char lvinfo3[36]; + + struct regid impl_id; + uint8_t impl_use[128]; +} __packed; + + +/* Implementation use Volume Descriptor */ +struct impvol_desc { + struct desc_tag tag; + uint32_t seq_num; + struct regid impl_id; + union { + struct udf_lv_info lv_info; + char impl_use[460]; + } _impl_use; +} __packed; + + +/* Logical Volume Descriptor [3/10.6] */ +struct logvol_desc { + struct desc_tag tag; + uint32_t seq_num; /* MAX prevail */ + struct charspec desc_charset; /* KEY */ + char logvol_id[128]; /* KEY */ + uint32_t lb_size; + struct regid domain_id; + union { + struct long_ad fsd_loc; /* to fileset descriptor SEQUENCE */ + uint8_t logvol_content_use[16]; + } _lvd_use; + uint32_t mt_l; /* Partition map length */ + uint32_t n_pm; /* Number of partition maps */ + struct regid imp_id; + uint8_t imp_use[128]; + struct extent_ad integrity_seq_loc; + uint8_t maps[1]; +} __packed; +#define lv_fsd_loc _lvd_use.fsd_loc + +#define UDF_INTEGRITY_OPEN 0 +#define UDF_INTEGRITY_CLOSED 1 + + +#define UDF_PMAP_SIZE 64 + +/* Type 1 Partition Map [3/10.7.2] */ +struct part_map_1 { + uint8_t type; + uint8_t len; + uint16_t vol_seq_num; + uint16_t part_num; +} __packed; + + +/* Type 2 Partition Map [3/10.7.3] */ +struct part_map_2 { + uint8_t type; + uint8_t len; + uint8_t reserved[2]; + struct regid part_id; + uint16_t vol_seq_num; + uint16_t part_num; + uint8_t reserved2[24]; +} __packed; + + +/* Virtual Partition Map [UDF 2.01/2.2.8] */ +struct part_map_virt { + uint8_t type; + uint8_t len; + uint8_t reserved[2]; + struct regid id; + uint16_t vol_seq_num; + uint16_t part_num; + uint8_t reserved1[24]; +} __packed; + + +/* Sparable Partition Map [UDF 2.01/2.2.9] */ +struct part_map_spare { + uint8_t type; + uint8_t len; + uint8_t reserved[2]; + struct regid id; + uint16_t vol_seq_num; + uint16_t part_num; + uint16_t packet_len; + uint8_t n_st; /* Number of redundant sparing tables range 1-4 */ + uint8_t reserved1; + uint32_t st_size; /* size of EACH sparing table */ + uint32_t st_loc[1]; /* locations of sparing tables */ +} __packed; + + +/* Metadata Partition Map [UDF 2.50/2.2.10] */ +struct part_map_meta { + uint8_t type; + uint8_t len; + uint8_t reserved[2]; + struct regid id; + uint16_t vol_seq_num; + uint16_t part_num; + uint32_t meta_file_lbn; /* logical block number for file entry within part_num */ + uint32_t meta_mirror_file_lbn; + uint32_t meta_bitmap_file_lbn; + uint32_t alloc_unit_size; /* allocation unit size in blocks */ + uint16_t alignment_unit_size; /* alignment necessary in blocks */ + uint8_t flags; + uint8_t reserved1[5]; +} __packed; +#define METADATA_DUPLICATED 1 + + +union udf_pmap { + uint8_t data[UDF_PMAP_SIZE]; + struct part_map_1 pm1; + struct part_map_2 pm2; + struct part_map_virt pmv; + struct part_map_spare pms; + struct part_map_meta pmm; +}; + + +/* Sparing Map Entry [UDF 2.01/2.2.11] */ +struct spare_map_entry { + uint32_t org; /* partition relative address */ + uint32_t map; /* absolute disc address (!) can be in partition, but doesn't have to be */ +} __packed; + + +/* Sparing Table [UDF 2.01/2.2.11] */ +struct udf_sparing_table { + struct desc_tag tag; + struct regid id; + uint16_t rt_l; /* Relocation Table len */ + uint8_t reserved[2]; + uint32_t seq_num; + struct spare_map_entry entries[1]; +} __packed; + + +#define UDF_NO_PREV_VAT 0xffffffff +/* UDF 1.50 VAT suffix [UDF 2.2.10 (UDF 1.50 spec)] */ +struct udf_oldvat_tail { + struct regid id; /* "*UDF Virtual Alloc Tbl" */ + uint32_t prev_vat; +} __packed; + + +/* VAT table [UDF 2.0.1/2.2.10] */ +struct udf_vat { + uint16_t header_len; + uint16_t impl_use_len; + char logvol_id[128]; /* newer version of the LVD one */ + uint32_t prev_vat; + uint32_t num_files; + uint32_t num_directories; + uint16_t min_udf_readver; + uint16_t min_udf_writever; + uint16_t max_udf_writever; + uint16_t reserved; + uint8_t data[1]; /* impl.use followed by VAT entries (uint32_t) */ +} __packed; + + +/* Space bitmap descriptor as found in the partition header descriptor */ +struct space_bitmap_desc { + struct desc_tag tag; /* TagId 264 */ + uint32_t num_bits; /* number of bits */ + uint32_t num_bytes; /* bytes that contain it */ + uint8_t data[1]; +} __packed; + + +/* Unalloc space entry as found in the partition header descriptor */ +struct space_entry_desc { + struct desc_tag tag; /* TagId 263 */ + struct icb_tag icbtag; /* type 1 */ + uint32_t l_ad; /* in bytes */ + uint8_t entry[1]; +} __packed; + + +/* Partition header descriptor; in the contents_use of part_desc */ +struct part_hdr_desc { + struct short_ad unalloc_space_table; + struct short_ad unalloc_space_bitmap; + struct short_ad part_integrity_table; /* has to be ZERO for UDF */ + struct short_ad freed_space_table; + struct short_ad freed_space_bitmap; + uint8_t reserved[88]; +} __packed; + + +/* Partition Descriptor [3/10.5] */ +struct part_desc { + struct desc_tag tag; + uint32_t seq_num; /* MAX prevailing */ + uint16_t flags; /* bit 0 : if set the space is allocated */ + uint16_t part_num; /* KEY */ + struct regid contents; + union { + struct part_hdr_desc part_hdr; + uint8_t contents_use[128]; + } _impl_use; + uint32_t access_type; /* R/W, WORM etc. */ + uint32_t start_loc; /* start of partition with given length */ + uint32_t part_len; + struct regid imp_id; + uint8_t imp_use[128]; + uint8_t reserved[156]; +} __packed; +#define pd_part_hdr _impl_use.part_hdr +#define UDF_PART_FLAG_ALLOCATED 1 + + +/* Unallocated Space Descriptor (UDF 2.01/2.2.5) */ +struct unalloc_sp_desc { + struct desc_tag tag; + uint32_t seq_num; /* MAX prevailing */ + uint32_t alloc_desc_num; + struct extent_ad alloc_desc[1]; +} __packed; + + +/* Logical Volume Integrity Descriptor [3/30.10] */ +struct logvolhdr { + uint64_t next_unique_id; + /* rest reserved */ +} __packed; + + +struct udf_logvol_info { + struct regid impl_id; + uint32_t num_files; + uint32_t num_directories; + uint16_t min_udf_readver; + uint16_t min_udf_writever; + uint16_t max_udf_writever; +} __packed; + + +struct logvol_int_desc { + struct desc_tag tag; + struct timestamp time; + uint32_t integrity_type; + struct extent_ad next_extent; + union { + struct logvolhdr logvolhdr; + int8_t reserved[32]; + } _impl_use; + uint32_t num_part; + uint32_t l_iu; + uint32_t tables[1]; /* Freespace table, Sizetable, Implementation use */ +} __packed; +#define lvint_next_unique_id _impl_use.logvolhdr.next_unique_id + + +/* File Set Descriptor [4/14.1] */ +struct fileset_desc { + struct desc_tag tag; + struct timestamp time; + uint16_t ichg_lvl; + uint16_t max_ichg_lvl; + uint32_t charset_list; + uint32_t max_charset_list; + uint32_t fileset_num; /* key! */ + uint32_t fileset_desc_num; + struct charspec logvol_id_charset; + char logvol_id[128]; /* for recovery */ + struct charspec fileset_charset; + char fileset_id[32]; /* Mountpoint !! */ + char copyright_file_id[32]; + char abstract_file_id[32]; + struct long_ad rootdir_icb; /* to rootdir; icb->virtual ? */ + struct regid domain_id; + struct long_ad next_ex; /* to the next fileset_desc extent */ + struct long_ad streamdir_icb; /* streamdir; needed? */ + uint8_t reserved[32]; +} __packed; + + +/* File Identifier Descriptor [4/14.4] */ +struct fileid_desc { + struct desc_tag tag; + uint16_t file_version_num; + uint8_t file_char; + uint8_t l_fi; /* Length of file identifier area */ + struct long_ad icb; + uint16_t l_iu; /* Length of implementation use area */ + uint8_t data[0]; +} __packed; +#define UDF_FID_SIZE 38 +#define UDF_FILE_CHAR_VIS (1 << 0) /* Invisible */ +#define UDF_FILE_CHAR_DIR (1 << 1) /* Directory */ +#define UDF_FILE_CHAR_DEL (1 << 2) /* Deleted */ +#define UDF_FILE_CHAR_PAR (1 << 3) /* Parent Directory */ +#define UDF_FILE_CHAR_META (1 << 4) /* Stream metadata */ + + +/* Extended attributes [4/14.10.1] */ +struct extattrhdr_desc { + struct desc_tag tag; + uint32_t impl_attr_loc; /* offsets within this descriptor */ + uint32_t appl_attr_loc; /* ditto */ +} __packed; +#define UDF_IMPL_ATTR_LOC_NOT_PRESENT 0xffffffff +#define UDF_APPL_ATTR_LOC_NOT_PRESENT 0xffffffff + + +/* Extended attribute entry [4/48.10.2] */ +struct extattr_entry { + uint32_t type; + uint8_t subtype; + uint8_t reserved[3]; + uint32_t a_l; +} __packed; + + +/* Extended attribute entry; type 2048 [4/48.10.8] */ +struct impl_extattr_entry { + struct extattr_entry hdr; + uint32_t iu_l; + struct regid imp_id; + uint8_t data[1]; +} __packed; + + +/* Extended attribute entry; type 65 536 [4/48.10.9] */ +struct appl_extattr_entry { + struct extattr_entry hdr; + uint32_t au_l; + struct regid appl_id; + uint8_t data[1]; +} __packed; + + +/* File Times attribute entry; type 5 or type 6 [4/48.10.5], [4/48.10.6] */ +struct filetimes_extattr_entry { + struct extattr_entry hdr; + uint32_t d_l; /* length of times[] data following */ + uint32_t existence; /* bitmask */ + struct timestamp times[1]; /* in order of ascending bits */ +} __packed; +#define UDF_FILETIMES_ATTR_NO 5 +#define UDF_FILETIMES_FILE_CREATION 1 +#define UDF_FILETIMES_FILE_DELETION 4 +#define UDF_FILETIMES_FILE_EFFECTIVE 8 +#define UDF_FILETIMES_FILE_BACKUPED 16 +#define UDF_FILETIMES_ATTR_SIZE(no) (20 + (no)*sizeof(struct timestamp)) + + +/* Device Specification Extended Attribute [4/4.10.7] */ +struct device_extattr_entry { + struct extattr_entry hdr; + uint32_t iu_l; /* length of implementation use */ + uint32_t major; + uint32_t minor; + uint8_t data[1]; /* UDF: if nonzero length, contain developer ID regid */ +} __packed; +#define UDF_DEVICESPEC_ATTR_NO 12 + + +/* VAT LV extension Extended Attribute [UDF 3.3.4.5.1.3] 1.50 errata */ +struct vatlvext_extattr_entry { + uint64_t unique_id_chk; /* needs to be copy of ICB's */ + uint32_t num_files; + uint32_t num_directories; + char logvol_id[128]; /* replaces logvol name */ +} __packed; + + +/* File Entry [4/14.9] */ +struct file_entry { + struct desc_tag tag; + struct icb_tag icbtag; + uint32_t uid; + uint32_t gid; + uint32_t perm; + uint16_t link_cnt; + uint8_t rec_format; + uint8_t rec_disp_attr; + uint32_t rec_len; + uint64_t inf_len; + uint64_t logblks_rec; + struct timestamp atime; + struct timestamp mtime; + struct timestamp attrtime; + uint32_t ckpoint; + struct long_ad ex_attr_icb; + struct regid imp_id; + uint64_t unique_id; + uint32_t l_ea; /* Length of extended attribute area */ + uint32_t l_ad; /* Length of allocation descriptors */ + uint8_t data[1]; +} __packed; +#define UDF_FENTRY_SIZE 176 +#define UDF_FENTRY_PERM_USER_MASK 0x07 +#define UDF_FENTRY_PERM_GRP_MASK 0xE0 +#define UDF_FENTRY_PERM_OWNER_MASK 0x1C00 + + +/* Extended File Entry [4/48.17] */ +struct extfile_entry { + struct desc_tag tag; + struct icb_tag icbtag; + uint32_t uid; + uint32_t gid; + uint32_t perm; + uint16_t link_cnt; + uint8_t rec_format; + uint8_t rec_disp_attr; + uint32_t rec_len; + uint64_t inf_len; + uint64_t obj_size; + uint64_t logblks_rec; + struct timestamp atime; + struct timestamp mtime; + struct timestamp ctime; + struct timestamp attrtime; + uint32_t ckpoint; + uint32_t reserved1; + struct long_ad ex_attr_icb; + struct long_ad streamdir_icb; + struct regid imp_id; + uint64_t unique_id; + uint32_t l_ea; /* Length of extended attribute area */ + uint32_t l_ad; /* Length of allocation descriptors */ + uint8_t data[1]; +} __packed; +#define UDF_EXTFENTRY_SIZE 216 + + +/* Indirect entry [ecma 48.7] */ +struct indirect_entry { + struct desc_tag tag; + struct icb_tag icbtag; + struct long_ad indirect_icb; +} __packed; + + +/* Allocation extent descriptor [ecma 48.5] */ +struct alloc_ext_entry { + struct desc_tag tag; + uint32_t prev_entry; + uint32_t l_ad; + uint8_t data[1]; +} __packed; + + +union dscrptr { + struct desc_tag tag; + struct anchor_vdp avdp; + struct vol_desc_ptr vdp; + struct pri_vol_desc pvd; + struct logvol_desc lvd; + struct unalloc_sp_desc usd; + struct logvol_int_desc lvid; + struct impvol_desc ivd; + struct part_desc pd; + struct fileset_desc fsd; + struct fileid_desc fid; + struct file_entry fe; + struct extfile_entry efe; + struct extattrhdr_desc eahd; + struct indirect_entry inde; + struct alloc_ext_entry aee; + struct udf_sparing_table spt; + struct space_bitmap_desc sbd; + struct space_entry_desc sed; +}; + + +#endif /* !_FS_UDF_ECMA167_UDF_H_ */ + diff --git a/sys/fs/udf/files.udf b/sys/fs/udf/files.udf new file mode 100644 index 000000000..87fb6676d --- /dev/null +++ b/sys/fs/udf/files.udf @@ -0,0 +1,16 @@ +# $NetBSD: files.udf,v 1.5 2013/07/10 15:10:56 reinoud Exp $ + +deffs UDF + +file fs/udf/udf_osta.c udf +file fs/udf/udf_vfsops.c udf +file fs/udf/udf_vnops.c udf +file fs/udf/udf_subr.c udf +file fs/udf/udf_readwrite.c udf +file fs/udf/udf_strat_bootstrap.c udf +file fs/udf/udf_strat_sequential.c udf +file fs/udf/udf_strat_direct.c udf +file fs/udf/udf_strat_rmw.c udf +file fs/udf/udf_allocation.c udf +file fs/udf/udf_rename.c udf + diff --git a/sys/fs/udf/udf.h b/sys/fs/udf/udf.h new file mode 100644 index 000000000..6848203c7 --- /dev/null +++ b/sys/fs/udf/udf.h @@ -0,0 +1,426 @@ +/* $NetBSD: udf.h,v 1.46 2013/10/18 19:56:55 christos Exp $ */ + +/* + * Copyright (c) 2006, 2008 Reinoud Zandijk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _FS_UDF_UDF_H_ +#define _FS_UDF_UDF_H_ + +#include +#include +#include +#include + +#include "udf_osta.h" +#include "ecma167-udf.h" +#include +#include +#include +#include +#include + +/* debug section */ +extern int udf_verbose; + +/* undefine UDF_COMPLETE_DELETE to need `purge'; but purge is not implemented */ +#define UDF_COMPLETE_DELETE + +/* debug categories */ +#define UDF_DEBUG_VOLUMES 0x0000001 +#define UDF_DEBUG_LOCKING 0x0000002 +#define UDF_DEBUG_NODE 0x0000004 +#define UDF_DEBUG_LOOKUP 0x0000008 +#define UDF_DEBUG_READDIR 0x0000010 +#define UDF_DEBUG_FIDS 0x0000020 +#define UDF_DEBUG_DESCRIPTOR 0x0000040 +#define UDF_DEBUG_TRANSLATE 0x0000080 +#define UDF_DEBUG_STRATEGY 0x0000100 +#define UDF_DEBUG_READ 0x0000200 +#define UDF_DEBUG_WRITE 0x0000400 +#define UDF_DEBUG_CALL 0x0000800 +#define UDF_DEBUG_ATTR 0x0001000 +#define UDF_DEBUG_EXTATTR 0x0002000 +#define UDF_DEBUG_ALLOC 0x0004000 +#define UDF_DEBUG_ADWLK 0x0008000 +#define UDF_DEBUG_DIRHASH 0x0010000 +#define UDF_DEBUG_NOTIMPL 0x0020000 +#define UDF_DEBUG_SHEDULE 0x0040000 +#define UDF_DEBUG_ECCLINE 0x0080000 +#define UDF_DEBUG_SYNC 0x0100000 +#define UDF_DEBUG_PARANOIA 0x0200000 +#define UDF_DEBUG_PARANOIDADWLK 0x0400000 +#define UDF_DEBUG_NODEDUMP 0x0800000 +#define UDF_DEBUG_RESERVE 0x1000000 + +/* initial value of udf_verbose */ +#define UDF_DEBUGGING 0 + +#ifdef UDF_DEBUG +#define DPRINTF(name, arg) { \ + if (udf_verbose & UDF_DEBUG_##name) {\ + printf arg;\ + };\ + } +#define DPRINTFIF(name, cond, arg) { \ + if (udf_verbose & UDF_DEBUG_##name) { \ + if (cond) printf arg;\ + };\ + } +#else +#define DPRINTF(name, arg) {} +#define DPRINTFIF(name, cond, arg) {} +#endif + + +/* constants to identify what kind of identifier we are dealing with */ +#define UDF_REGID_DOMAIN 1 +#define UDF_REGID_UDF 2 +#define UDF_REGID_IMPLEMENTATION 3 +#define UDF_REGID_APPLICATION 4 +#define UDF_REGID_NAME 99 + + +/* DON'T change these: they identify 13thmonkey's UDF implementation */ +#define APP_NAME "*NetBSD UDF" +#define APP_VERSION_MAIN 0 +#define APP_VERSION_SUB 5 +#define IMPL_NAME "*NetBSD kernel UDF" + + +/* Configuration values */ +#define UDF_INODE_HASHBITS 10 +#define UDF_INODE_HASHSIZE (1< are normal */ + +#define UDF_VTOP_TYPE_RAW 0 +#define UDF_VTOP_TYPE_UNKNOWN 0 +#define UDF_VTOP_TYPE_PHYS 1 +#define UDF_VTOP_TYPE_VIRT 2 +#define UDF_VTOP_TYPE_SPARABLE 3 +#define UDF_VTOP_TYPE_META 4 + + +/* allocation strategies */ +#define UDF_ALLOC_INVALID 0 +#define UDF_ALLOC_SEQUENTIAL 1 /* linear on NWA */ +#define UDF_ALLOC_VAT 2 /* VAT handling */ +#define UDF_ALLOC_SPACEMAP 3 /* spacemaps */ +#define UDF_ALLOC_METABITMAP 4 /* metadata bitmap */ +#define UDF_ALLOC_METASEQUENTIAL 5 /* in chunks seq., nodes not seq */ +#define UDF_ALLOC_RELAXEDSEQUENTIAL 6 /* only nodes not seq. */ + + +/* logical volume open/close actions */ +#define UDF_OPEN_SESSION 0x01 /* if needed writeout VRS + VDS */ +#define UDF_CLOSE_SESSION 0x02 /* close session after writing VAT */ +#define UDF_FINALISE_DISC 0x04 /* close session after writing VAT */ +#define UDF_WRITE_VAT 0x08 /* sequential VAT filesystem */ +#define UDF_WRITE_LVINT 0x10 /* write out open lvint */ +#define UDF_WRITE_PART_BITMAPS 0x20 /* write out partition space bitmaps */ +#define UDF_APPENDONLY_LVINT 0x40 /* no shifting, only appending */ +#define UDF_WRITE_METAPART_NODES 0x80 /* write out metadata partition nodes*/ +#define UDFLOGVOL_BITS "\20\1OPEN_SESSION\2CLOSE_SESSION\3FINALISE_DISC" \ + "\4WRITE_VAT\5WRITE_LVINT\6WRITE_PART_BITMAPS" \ + "\7APPENDONLY_LVINT\10WRITE_METAPART_NODES" + +/* logical volume error handling actions */ +#define UDF_UPDATE_TRACKINFO 0x01 /* update trackinfo and re-shedule */ +#define UDF_REMAP_BLOCK 0x02 /* remap the failing block length */ +#define UDFONERROR_BITS "\20\1UPDATE_TRACKINFO\2REMAP_BLOCK" + + +/* readdir cookies */ +#define UDF_DIRCOOKIE_DOT 1 + + +/* malloc pools */ +MALLOC_DECLARE(M_UDFMNT); +MALLOC_DECLARE(M_UDFVOLD); +MALLOC_DECLARE(M_UDFTEMP); + +extern struct pool udf_node_pool; +struct udf_node; +struct udf_strategy; + + +struct udf_lvintq { + uint32_t start; + uint32_t end; + uint32_t pos; + uint32_t wpos; +}; + + +struct udf_bitmap { + uint8_t *blob; /* allocated */ + uint8_t *bits; /* bits themselves */ + uint8_t *pages; /* dirty pages */ + uint32_t max_offset; /* in bits */ + uint32_t data_pos; /* position in data */ + uint32_t metadata_pos; /* .. in metadata */ +}; + + +struct udf_strat_args { + struct udf_mount *ump; + struct udf_node *udf_node; + struct long_ad *icb; + union dscrptr *dscr; + struct buf *nestbuf; + kauth_cred_t cred; + int waitfor; +}; + +struct udf_strategy { + int (*create_logvol_dscr) (struct udf_strat_args *args); + void (*free_logvol_dscr) (struct udf_strat_args *args); + int (*read_logvol_dscr) (struct udf_strat_args *args); + int (*write_logvol_dscr) (struct udf_strat_args *args); + void (*queuebuf) (struct udf_strat_args *args); + void (*discstrat_init) (struct udf_strat_args *args); + void (*discstrat_finish) (struct udf_strat_args *args); +}; + +extern struct udf_strategy udf_strat_bootstrap; +extern struct udf_strategy udf_strat_sequential; +extern struct udf_strategy udf_strat_direct; +extern struct udf_strategy udf_strat_rmw; + + +/* pre cleanup */ +struct udf_mount { + struct mount *vfs_mountp; + struct vnode *devvp; + struct mmc_discinfo discinfo; + struct udf_args mount_args; + + /* format descriptors */ + kmutex_t logvol_mutex; + struct anchor_vdp *anchors[UDF_ANCHORS]; /* anchors to VDS */ + struct pri_vol_desc *primary_vol; /* identification */ + struct logvol_desc *logical_vol; /* main mapping v->p */ + struct unalloc_sp_desc *unallocated; /* free UDF space */ + struct impvol_desc *implementation; /* likely reduntant */ + struct logvol_int_desc *logvol_integrity; /* current integrity */ + struct part_desc *partitions[UDF_PARTITIONS]; /* partitions */ + /* logvol_info is derived; points *into* other structures */ + struct udf_logvol_info *logvol_info; /* integrity descr. */ + + /* fileset and root directories */ + struct fileset_desc *fileset_desc; /* normally one */ + + /* tracing logvol integrity history */ + struct udf_lvintq lvint_trace[UDF_LVDINT_SEGMENTS]; + int lvopen; /* logvol actions */ + int lvclose; /* logvol actions */ + + /* logical to physical translations */ + int vtop[UDF_PMAPS+1]; /* vpartnr trans */ + int vtop_tp[UDF_PMAPS+1]; /* type of trans */ + + /* disc allocation / writing method */ + kmutex_t allocate_mutex; + int lvreadwrite; /* error handling */ + int vtop_alloc[UDF_PMAPS+1]; /* alloc scheme */ + int data_part; + int node_part; + int fids_part; + + /* sequential track info */ + struct mmc_trackinfo data_track; + struct mmc_trackinfo metadata_track; + + /* VAT */ + uint32_t first_possible_vat_location; + uint32_t last_possible_vat_location; + uint32_t vat_entries; + uint32_t vat_offset; /* offset in table */ + uint32_t vat_last_free_lb; /* last free lb_num */ + uint32_t vat_table_len; + uint32_t vat_table_alloc_len; + uint8_t *vat_table; + uint8_t *vat_pages; /* TODO */ + struct udf_node *vat_node; /* system node */ + + /* space bitmaps for physical partitions */ + struct space_bitmap_desc*part_unalloc_dscr[UDF_PARTITIONS]; + struct space_bitmap_desc*part_freed_dscr [UDF_PARTITIONS]; + struct udf_bitmap part_unalloc_bits[UDF_PARTITIONS]; + struct udf_bitmap part_freed_bits [UDF_PARTITIONS]; + + /* sparable */ + uint32_t sparable_packet_size; + uint32_t packet_size; + struct udf_sparing_table*sparing_table; + + /* meta */ + struct udf_node *metadata_node; /* system node */ + struct udf_node *metadatamirror_node; /* system node */ + struct udf_node *metadatabitmap_node; /* system node */ + struct space_bitmap_desc*metadata_unalloc_dscr; + struct udf_bitmap metadata_unalloc_bits; + uint32_t metadata_alloc_unit_size; + uint16_t metadata_alignment_unit_size; + uint8_t metadata_flags; + + /* rb tree for lookup icb to udf_node and sorted list for sync */ + kmutex_t ihash_lock; + kmutex_t get_node_lock; + struct rb_tree udf_node_tree; + + /* syncing */ + int syncing; /* are we syncing? */ + kcondvar_t dirtynodes_cv; /* sleeping on sync */ + + /* late allocation */ + int32_t uncommitted_lbs[UDF_PARTITIONS]; + struct long_ad *la_node_ad_cpy; /* issue buf */ + uint64_t *la_lmapping, *la_pmapping; /* issue buf */ + + /* lists */ + STAILQ_HEAD(udfmntpts, udf_mount) all_udf_mntpnts; + + /* device strategy */ + struct udf_strategy *strategy; + void *strategy_private; +}; + +/* + * UDF node describing a file/directory. + * + * BUGALERT claim node_mutex before reading/writing to prevent inconsistencies ! + */ +struct udf_node { + struct genfs_node i_gnode; /* has to be first */ + struct vnode *vnode; /* vnode associated */ + struct udf_mount *ump; + + kmutex_t node_mutex; + kcondvar_t node_lock; /* sleeping lock */ + char const *lock_fname; + int lock_lineno; + + /* rb_node for fast lookup and fast sequential visiting */ + struct rb_node rbnode; + + /* one of `fe' or `efe' can be set, not both (UDF file entry dscr.) */ + struct file_entry *fe; + struct extfile_entry *efe; + struct alloc_ext_entry *ext[UDF_MAX_ALLOC_EXTENTS]; + int num_extensions; + + /* location found, recording location & hints */ + struct long_ad loc; /* FID/hash loc. */ + struct long_ad write_loc; /* strat 4096 loc */ + int needs_indirect; /* has missing indr. */ + struct long_ad ext_loc[UDF_MAX_ALLOC_EXTENTS]; + + struct dirhash *dir_hash; + + /* misc */ + uint32_t i_flags; /* associated flags */ + struct lockf *lockf; /* lock list */ + uint32_t outstanding_bufs; /* file data */ + uint32_t outstanding_nodedscr; /* node dscr */ + int32_t uncommitted_lbs; /* in UBC */ + + /* references to associated nodes */ + struct udf_node *extattr; + struct udf_node *streamdir; + struct udf_node *my_parent; /* if extended attr. */ +}; + + +/* misc. flags stored in i_flags (XXX needs cleaning up) */ +#define IN_ACCESS 0x0001 /* Inode access time update request */ +#define IN_CHANGE 0x0002 /* Inode change time update request */ +#define IN_UPDATE 0x0004 /* Inode was written to; update mtime*/ +#define IN_MODIFY 0x0008 /* Modification time update request */ +#define IN_MODIFIED 0x0010 /* node has been modified */ +#define IN_ACCESSED 0x0020 /* node has been accessed */ +#define IN_RENAME 0x0040 /* node is being renamed. XXX ?? */ +#define IN_DELETED 0x0080 /* node is unlinked, no FID reference*/ +#define IN_LOCKED 0x0100 /* node is locked by condvar */ +#define IN_SYNCED 0x0200 /* node is being used by sync */ +#define IN_CALLBACK_ULK 0x0400 /* node will be unlocked by callback */ +#define IN_NODE_REBUILD 0x0800 /* node is rebuild */ + + +#define IN_FLAGBITS \ + "\10\1IN_ACCESS\2IN_CHANGE\3IN_UPDATE\4IN_MODIFY\5IN_MODIFIED" \ + "\6IN_ACCESSED\7IN_RENAME\10IN_DELETED\11IN_LOCKED\12IN_SYNCED" \ + "\13IN_CALLBACK_ULK\14IN_NODE_REBUILD" + +#endif /* !_FS_UDF_UDF_H_ */ diff --git a/sys/fs/udf/udf_allocation.c b/sys/fs/udf/udf_allocation.c new file mode 100644 index 000000000..002116932 --- /dev/null +++ b/sys/fs/udf/udf_allocation.c @@ -0,0 +1,3215 @@ +/* $NetBSD: udf_allocation.c,v 1.36 2013/10/30 08:41:38 mrg Exp $ */ + +/* + * Copyright (c) 2006, 2008 Reinoud Zandijk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#ifndef lint +__KERNEL_RCSID(0, "$NetBSD: udf_allocation.c,v 1.36 2013/10/30 08:41:38 mrg Exp $"); +#endif /* not lint */ + + +#if defined(_KERNEL_OPT) +#include "opt_compat_netbsd.h" +#endif + +/* TODO strip */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "udf.h" +#include "udf_subr.h" +#include "udf_bswap.h" + + +#define VTOI(vnode) ((struct udf_node *) vnode->v_data) + +static void udf_record_allocation_in_node(struct udf_mount *ump, + struct buf *buf, uint16_t vpart_num, uint64_t *mapping, + struct long_ad *node_ad_cpy); + +static void udf_collect_free_space_for_vpart(struct udf_mount *ump, + uint16_t vpart_num, uint32_t num_lb); + +static int udf_ads_merge(uint32_t max_len, uint32_t lb_size, struct long_ad *a1, struct long_ad *a2); +static void udf_wipe_adslots(struct udf_node *udf_node); +static void udf_count_alloc_exts(struct udf_node *udf_node); + + +/* --------------------------------------------------------------------- */ + +#if 0 +#if 1 +static void +udf_node_dump(struct udf_node *udf_node) { + struct file_entry *fe; + struct extfile_entry *efe; + struct icb_tag *icbtag; + struct long_ad s_ad; + uint64_t inflen; + uint32_t icbflags, addr_type; + uint32_t len, lb_num; + uint32_t flags; + int part_num; + int lb_size, eof, slot; + + if ((udf_verbose & UDF_DEBUG_NODEDUMP) == 0) + return; + + lb_size = udf_rw32(udf_node->ump->logical_vol->lb_size); + + fe = udf_node->fe; + efe = udf_node->efe; + if (fe) { + icbtag = &fe->icbtag; + inflen = udf_rw64(fe->inf_len); + } else { + icbtag = &efe->icbtag; + inflen = udf_rw64(efe->inf_len); + } + + icbflags = udf_rw16(icbtag->flags); + addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK; + + printf("udf_node_dump %p :\n", udf_node); + + if (addr_type == UDF_ICB_INTERN_ALLOC) { + printf("\tIntern alloc, len = %"PRIu64"\n", inflen); + return; + } + + printf("\tInflen = %"PRIu64"\n", inflen); + printf("\t\t"); + + slot = 0; + for (;;) { + udf_get_adslot(udf_node, slot, &s_ad, &eof); + if (eof) + break; + part_num = udf_rw16(s_ad.loc.part_num); + lb_num = udf_rw32(s_ad.loc.lb_num); + len = udf_rw32(s_ad.len); + flags = UDF_EXT_FLAGS(len); + len = UDF_EXT_LEN(len); + + printf("["); + if (part_num >= 0) + printf("part %d, ", part_num); + printf("lb_num %d, len %d", lb_num, len); + if (flags) + printf(", flags %d", flags>>30); + printf("] "); + + if (flags == UDF_EXT_REDIRECT) { + printf("\n\textent END\n\tallocation extent\n\t\t"); + } + + slot++; + } + printf("\n\tl_ad END\n\n"); +} +#else +#define udf_node_dump(a) +#endif + + +static void +udf_assert_allocated(struct udf_mount *ump, uint16_t vpart_num, + uint32_t lb_num, uint32_t num_lb) +{ + struct udf_bitmap *bitmap; + struct part_desc *pdesc; + uint32_t ptov; + uint32_t bitval; + uint8_t *bpos; + int bit; + int phys_part; + int ok; + + DPRINTF(PARANOIA, ("udf_assert_allocated: check virt lbnum %d " + "part %d + %d sect\n", lb_num, vpart_num, num_lb)); + + /* get partition backing up this vpart_num */ + pdesc = ump->partitions[ump->vtop[vpart_num]]; + + switch (ump->vtop_tp[vpart_num]) { + case UDF_VTOP_TYPE_PHYS : + case UDF_VTOP_TYPE_SPARABLE : + /* free space to freed or unallocated space bitmap */ + ptov = udf_rw32(pdesc->start_loc); + phys_part = ump->vtop[vpart_num]; + + /* use unallocated bitmap */ + bitmap = &ump->part_unalloc_bits[phys_part]; + + /* if no bitmaps are defined, bail out */ + if (bitmap->bits == NULL) + break; + + /* check bits */ + KASSERT(bitmap->bits); + ok = 1; + bpos = bitmap->bits + lb_num/8; + bit = lb_num % 8; + while (num_lb > 0) { + bitval = (1 << bit); + DPRINTF(PARANOIA, ("XXX : check %d, %p, bit %d\n", + lb_num, bpos, bit)); + KASSERT(bitmap->bits + lb_num/8 == bpos); + if (*bpos & bitval) { + printf("\tlb_num %d is NOT marked busy\n", + lb_num); + ok = 0; + } + lb_num++; num_lb--; + bit = (bit + 1) % 8; + if (bit == 0) + bpos++; + } + if (!ok) { + /* KASSERT(0); */ + } + + break; + case UDF_VTOP_TYPE_VIRT : + /* TODO check space */ + KASSERT(num_lb == 1); + break; + case UDF_VTOP_TYPE_META : + /* TODO check space in the metadata bitmap */ + default: + /* not implemented */ + break; + } +} + + +static void +udf_node_sanity_check(struct udf_node *udf_node, + uint64_t *cnt_inflen, uint64_t *cnt_logblksrec) +{ + union dscrptr *dscr; + struct file_entry *fe; + struct extfile_entry *efe; + struct icb_tag *icbtag; + struct long_ad s_ad; + uint64_t inflen, logblksrec; + uint32_t icbflags, addr_type; + uint32_t len, lb_num, l_ea, l_ad, max_l_ad; + uint16_t part_num; + uint8_t *data_pos; + int dscr_size, lb_size, flags, whole_lb; + int i, slot, eof; + +// KASSERT(mutex_owned(&udf_node->ump->allocate_mutex)); + + if (1) + udf_node_dump(udf_node); + + lb_size = udf_rw32(udf_node->ump->logical_vol->lb_size); + + fe = udf_node->fe; + efe = udf_node->efe; + if (fe) { + dscr = (union dscrptr *) fe; + icbtag = &fe->icbtag; + inflen = udf_rw64(fe->inf_len); + dscr_size = sizeof(struct file_entry) -1; + logblksrec = udf_rw64(fe->logblks_rec); + l_ad = udf_rw32(fe->l_ad); + l_ea = udf_rw32(fe->l_ea); + } else { + dscr = (union dscrptr *) efe; + icbtag = &efe->icbtag; + inflen = udf_rw64(efe->inf_len); + dscr_size = sizeof(struct extfile_entry) -1; + logblksrec = udf_rw64(efe->logblks_rec); + l_ad = udf_rw32(efe->l_ad); + l_ea = udf_rw32(efe->l_ea); + } + data_pos = (uint8_t *) dscr + dscr_size + l_ea; + max_l_ad = lb_size - dscr_size - l_ea; + icbflags = udf_rw16(icbtag->flags); + addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK; + + /* check if tail is zero */ + DPRINTF(PARANOIA, ("Sanity check blank tail\n")); + for (i = l_ad; i < max_l_ad; i++) { + if (data_pos[i] != 0) + printf( "sanity_check: violation: node byte %d " + "has value %d\n", i, data_pos[i]); + } + + /* reset counters */ + *cnt_inflen = 0; + *cnt_logblksrec = 0; + + if (addr_type == UDF_ICB_INTERN_ALLOC) { + KASSERT(l_ad <= max_l_ad); + KASSERT(l_ad == inflen); + *cnt_inflen = inflen; + return; + } + + /* start counting */ + whole_lb = 1; + slot = 0; + for (;;) { + udf_get_adslot(udf_node, slot, &s_ad, &eof); + if (eof) + break; + KASSERT(whole_lb == 1); + + part_num = udf_rw16(s_ad.loc.part_num); + lb_num = udf_rw32(s_ad.loc.lb_num); + len = udf_rw32(s_ad.len); + flags = UDF_EXT_FLAGS(len); + len = UDF_EXT_LEN(len); + + if (flags != UDF_EXT_REDIRECT) { + *cnt_inflen += len; + if (flags == UDF_EXT_ALLOCATED) { + *cnt_logblksrec += (len + lb_size -1) / lb_size; + } + } else { + KASSERT(len == lb_size); + } + /* check allocation */ + if (flags == UDF_EXT_ALLOCATED) + udf_assert_allocated(udf_node->ump, part_num, lb_num, + (len + lb_size - 1) / lb_size); + + /* check whole lb */ + whole_lb = ((len % lb_size) == 0); + + slot++; + } + /* rest should be zero (ad_off > l_ad < max_l_ad - adlen) */ + + KASSERT(*cnt_inflen == inflen); + KASSERT(*cnt_logblksrec == logblksrec); + +// KASSERT(mutex_owned(&udf_node->ump->allocate_mutex)); +} +#else +static void +udf_node_sanity_check(struct udf_node *udf_node, + uint64_t *cnt_inflen, uint64_t *cnt_logblksrec) { + struct file_entry *fe; + struct extfile_entry *efe; + uint64_t inflen, logblksrec; + + fe = udf_node->fe; + efe = udf_node->efe; + if (fe) { + inflen = udf_rw64(fe->inf_len); + logblksrec = udf_rw64(fe->logblks_rec); + } else { + inflen = udf_rw64(efe->inf_len); + logblksrec = udf_rw64(efe->logblks_rec); + } + *cnt_logblksrec = logblksrec; + *cnt_inflen = inflen; +} +#endif + +/* --------------------------------------------------------------------- */ + +void +udf_calc_freespace(struct udf_mount *ump, uint64_t *sizeblks, uint64_t *freeblks) +{ + struct logvol_int_desc *lvid; + uint32_t *pos1, *pos2; + int vpart, num_vpart; + + lvid = ump->logvol_integrity; + *freeblks = *sizeblks = 0; + + /* + * Sequentials media report free space directly (CD/DVD/BD-R), for the + * other media we need the logical volume integrity. + * + * We sum all free space up here regardless of type. + */ + + KASSERT(lvid); + num_vpart = udf_rw32(lvid->num_part); + + if (ump->discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) { + /* use track info directly summing if there are 2 open */ + /* XXX assumption at most two tracks open */ + *freeblks = ump->data_track.free_blocks; + if (ump->data_track.tracknr != ump->metadata_track.tracknr) + *freeblks += ump->metadata_track.free_blocks; + *sizeblks = ump->discinfo.last_possible_lba; + } else { + /* free and used space for mountpoint based on logvol integrity */ + for (vpart = 0; vpart < num_vpart; vpart++) { + pos1 = &lvid->tables[0] + vpart; + pos2 = &lvid->tables[0] + num_vpart + vpart; + if (udf_rw32(*pos1) != (uint32_t) -1) { + *freeblks += udf_rw32(*pos1); + *sizeblks += udf_rw32(*pos2); + } + } + } + /* adjust for accounted uncommitted blocks */ + for (vpart = 0; vpart < num_vpart; vpart++) + *freeblks -= ump->uncommitted_lbs[vpart]; + + if (*freeblks > UDF_DISC_SLACK) { + *freeblks -= UDF_DISC_SLACK; + } else { + *freeblks = 0; + } +} + + +static void +udf_calc_vpart_freespace(struct udf_mount *ump, uint16_t vpart_num, uint64_t *freeblks) +{ + struct logvol_int_desc *lvid; + uint32_t *pos1; + + lvid = ump->logvol_integrity; + *freeblks = 0; + + /* + * Sequentials media report free space directly (CD/DVD/BD-R), for the + * other media we need the logical volume integrity. + * + * We sum all free space up here regardless of type. + */ + + KASSERT(lvid); + if (ump->discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) { + /* XXX assumption at most two tracks open */ + if (vpart_num == ump->data_part) { + *freeblks = ump->data_track.free_blocks; + } else { + *freeblks = ump->metadata_track.free_blocks; + } + } else { + /* free and used space for mountpoint based on logvol integrity */ + pos1 = &lvid->tables[0] + vpart_num; + if (udf_rw32(*pos1) != (uint32_t) -1) + *freeblks += udf_rw32(*pos1); + } + + /* adjust for accounted uncommitted blocks */ + if (*freeblks > ump->uncommitted_lbs[vpart_num]) { + *freeblks -= ump->uncommitted_lbs[vpart_num]; + } else { + *freeblks = 0; + } +} + +/* --------------------------------------------------------------------- */ + +int +udf_translate_vtop(struct udf_mount *ump, struct long_ad *icb_loc, + uint32_t *lb_numres, uint32_t *extres) +{ + struct part_desc *pdesc; + struct spare_map_entry *sme; + struct long_ad s_icb_loc; + uint64_t foffset, end_foffset; + uint32_t lb_size, len; + uint32_t lb_num, lb_rel, lb_packet; + uint32_t udf_rw32_lbmap, ext_offset; + uint16_t vpart; + int rel, part, error, eof, slot, flags; + + assert(ump && icb_loc && lb_numres); + + vpart = udf_rw16(icb_loc->loc.part_num); + lb_num = udf_rw32(icb_loc->loc.lb_num); + if (vpart > UDF_VTOP_RAWPART) + return EINVAL; + +translate_again: + part = ump->vtop[vpart]; + pdesc = ump->partitions[part]; + + switch (ump->vtop_tp[vpart]) { + case UDF_VTOP_TYPE_RAW : + /* 1:1 to the end of the device */ + *lb_numres = lb_num; + *extres = INT_MAX; + return 0; + case UDF_VTOP_TYPE_PHYS : + /* transform into its disc logical block */ + if (lb_num > udf_rw32(pdesc->part_len)) + return EINVAL; + *lb_numres = lb_num + udf_rw32(pdesc->start_loc); + + /* extent from here to the end of the partition */ + *extres = udf_rw32(pdesc->part_len) - lb_num; + return 0; + case UDF_VTOP_TYPE_VIRT : + /* only maps one logical block, lookup in VAT */ + if (lb_num >= ump->vat_entries) /* XXX > or >= ? */ + return EINVAL; + + /* lookup in virtual allocation table file */ + mutex_enter(&ump->allocate_mutex); + error = udf_vat_read(ump->vat_node, + (uint8_t *) &udf_rw32_lbmap, 4, + ump->vat_offset + lb_num * 4); + mutex_exit(&ump->allocate_mutex); + + if (error) + return error; + + lb_num = udf_rw32(udf_rw32_lbmap); + + /* transform into its disc logical block */ + if (lb_num > udf_rw32(pdesc->part_len)) + return EINVAL; + *lb_numres = lb_num + udf_rw32(pdesc->start_loc); + + /* just one logical block */ + *extres = 1; + return 0; + case UDF_VTOP_TYPE_SPARABLE : + /* check if the packet containing the lb_num is remapped */ + lb_packet = lb_num / ump->sparable_packet_size; + lb_rel = lb_num % ump->sparable_packet_size; + + for (rel = 0; rel < udf_rw16(ump->sparing_table->rt_l); rel++) { + sme = &ump->sparing_table->entries[rel]; + if (lb_packet == udf_rw32(sme->org)) { + /* NOTE maps to absolute disc logical block! */ + *lb_numres = udf_rw32(sme->map) + lb_rel; + *extres = ump->sparable_packet_size - lb_rel; + return 0; + } + } + + /* transform into its disc logical block */ + if (lb_num > udf_rw32(pdesc->part_len)) + return EINVAL; + *lb_numres = lb_num + udf_rw32(pdesc->start_loc); + + /* rest of block */ + *extres = ump->sparable_packet_size - lb_rel; + return 0; + case UDF_VTOP_TYPE_META : + /* we have to look into the file's allocation descriptors */ + + /* use metadatafile allocation mutex */ + lb_size = udf_rw32(ump->logical_vol->lb_size); + + UDF_LOCK_NODE(ump->metadata_node, 0); + + /* get first overlapping extent */ + foffset = 0; + slot = 0; + for (;;) { + udf_get_adslot(ump->metadata_node, + slot, &s_icb_loc, &eof); + DPRINTF(ADWLK, ("slot %d, eof = %d, flags = %d, " + "len = %d, lb_num = %d, part = %d\n", + slot, eof, + UDF_EXT_FLAGS(udf_rw32(s_icb_loc.len)), + UDF_EXT_LEN(udf_rw32(s_icb_loc.len)), + udf_rw32(s_icb_loc.loc.lb_num), + udf_rw16(s_icb_loc.loc.part_num))); + if (eof) { + DPRINTF(TRANSLATE, + ("Meta partition translation " + "failed: can't seek location\n")); + UDF_UNLOCK_NODE(ump->metadata_node, 0); + return EINVAL; + } + len = udf_rw32(s_icb_loc.len); + flags = UDF_EXT_FLAGS(len); + len = UDF_EXT_LEN(len); + + if (flags == UDF_EXT_REDIRECT) { + slot++; + continue; + } + + end_foffset = foffset + len; + + if (end_foffset > (uint64_t) lb_num * lb_size) + break; /* found */ + foffset = end_foffset; + slot++; + } + /* found overlapping slot */ + ext_offset = lb_num * lb_size - foffset; + + /* process extent offset */ + lb_num = udf_rw32(s_icb_loc.loc.lb_num); + vpart = udf_rw16(s_icb_loc.loc.part_num); + lb_num += (ext_offset + lb_size -1) / lb_size; + ext_offset = 0; + + UDF_UNLOCK_NODE(ump->metadata_node, 0); + if (flags != UDF_EXT_ALLOCATED) { + DPRINTF(TRANSLATE, ("Metadata partition translation " + "failed: not allocated\n")); + return EINVAL; + } + + /* + * vpart and lb_num are updated, translate again since we + * might be mapped on sparable media + */ + goto translate_again; + default: + printf("UDF vtop translation scheme %d unimplemented yet\n", + ump->vtop_tp[vpart]); + } + + return EINVAL; +} + + +/* XXX provisional primitive braindead version */ +/* TODO use ext_res */ +void +udf_translate_vtop_list(struct udf_mount *ump, uint32_t sectors, + uint16_t vpart_num, uint64_t *lmapping, uint64_t *pmapping) +{ + struct long_ad loc; + uint32_t lb_numres, ext_res; + int sector; + + for (sector = 0; sector < sectors; sector++) { + memset(&loc, 0, sizeof(struct long_ad)); + loc.loc.part_num = udf_rw16(vpart_num); + loc.loc.lb_num = udf_rw32(*lmapping); + udf_translate_vtop(ump, &loc, &lb_numres, &ext_res); + *pmapping = lb_numres; + lmapping++; pmapping++; + } +} + + +/* --------------------------------------------------------------------- */ + +/* + * Translate an extent (in logical_blocks) into logical block numbers; used + * for read and write operations. DOESNT't check extents. + */ + +int +udf_translate_file_extent(struct udf_node *udf_node, + uint32_t from, uint32_t num_lb, + uint64_t *map) +{ + struct udf_mount *ump; + struct icb_tag *icbtag; + struct long_ad t_ad, s_ad; + uint64_t transsec; + uint64_t foffset, end_foffset; + uint32_t transsec32; + uint32_t lb_size; + uint32_t ext_offset; + uint32_t lb_num, len; + uint32_t overlap, translen; + uint16_t vpart_num; + int eof, error, flags; + int slot, addr_type, icbflags; + + if (!udf_node) + return ENOENT; + + KASSERT(num_lb > 0); + + UDF_LOCK_NODE(udf_node, 0); + + /* initialise derivative vars */ + ump = udf_node->ump; + lb_size = udf_rw32(ump->logical_vol->lb_size); + + if (udf_node->fe) { + icbtag = &udf_node->fe->icbtag; + } else { + icbtag = &udf_node->efe->icbtag; + } + icbflags = udf_rw16(icbtag->flags); + addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK; + + /* do the work */ + if (addr_type == UDF_ICB_INTERN_ALLOC) { + *map = UDF_TRANS_INTERN; + UDF_UNLOCK_NODE(udf_node, 0); + return 0; + } + + /* find first overlapping extent */ + foffset = 0; + slot = 0; + for (;;) { + udf_get_adslot(udf_node, slot, &s_ad, &eof); + DPRINTF(ADWLK, ("slot %d, eof = %d, flags = %d, len = %d, " + "lb_num = %d, part = %d\n", slot, eof, + UDF_EXT_FLAGS(udf_rw32(s_ad.len)), + UDF_EXT_LEN(udf_rw32(s_ad.len)), + udf_rw32(s_ad.loc.lb_num), + udf_rw16(s_ad.loc.part_num))); + if (eof) { + DPRINTF(TRANSLATE, + ("Translate file extent " + "failed: can't seek location\n")); + UDF_UNLOCK_NODE(udf_node, 0); + return EINVAL; + } + len = udf_rw32(s_ad.len); + flags = UDF_EXT_FLAGS(len); + len = UDF_EXT_LEN(len); + lb_num = udf_rw32(s_ad.loc.lb_num); + + if (flags == UDF_EXT_REDIRECT) { + slot++; + continue; + } + + end_foffset = foffset + len; + + if (end_foffset > (uint64_t) from * lb_size) + break; /* found */ + foffset = end_foffset; + slot++; + } + /* found overlapping slot */ + ext_offset = (uint64_t) from * lb_size - foffset; + + for (;;) { + udf_get_adslot(udf_node, slot, &s_ad, &eof); + DPRINTF(ADWLK, ("slot %d, eof = %d, flags = %d, len = %d, " + "lb_num = %d, part = %d\n", slot, eof, + UDF_EXT_FLAGS(udf_rw32(s_ad.len)), + UDF_EXT_LEN(udf_rw32(s_ad.len)), + udf_rw32(s_ad.loc.lb_num), + udf_rw16(s_ad.loc.part_num))); + if (eof) { + DPRINTF(TRANSLATE, + ("Translate file extent " + "failed: past eof\n")); + UDF_UNLOCK_NODE(udf_node, 0); + return EINVAL; + } + + len = udf_rw32(s_ad.len); + flags = UDF_EXT_FLAGS(len); + len = UDF_EXT_LEN(len); + + lb_num = udf_rw32(s_ad.loc.lb_num); + vpart_num = udf_rw16(s_ad.loc.part_num); + + end_foffset = foffset + len; + + /* process extent, don't forget to advance on ext_offset! */ + lb_num += (ext_offset + lb_size -1) / lb_size; + overlap = (len - ext_offset + lb_size -1) / lb_size; + ext_offset = 0; + + /* + * note that the while(){} is nessisary for the extent that + * the udf_translate_vtop() returns doens't have to span the + * whole extent. + */ + + overlap = MIN(overlap, num_lb); + while (overlap && (flags != UDF_EXT_REDIRECT)) { + switch (flags) { + case UDF_EXT_FREE : + case UDF_EXT_ALLOCATED_BUT_NOT_USED : + transsec = UDF_TRANS_ZERO; + translen = overlap; + while (overlap && num_lb && translen) { + *map++ = transsec; + lb_num++; + overlap--; num_lb--; translen--; + } + break; + case UDF_EXT_ALLOCATED : + t_ad.loc.lb_num = udf_rw32(lb_num); + t_ad.loc.part_num = udf_rw16(vpart_num); + error = udf_translate_vtop(ump, + &t_ad, &transsec32, &translen); + transsec = transsec32; + if (error) { + UDF_UNLOCK_NODE(udf_node, 0); + return error; + } + while (overlap && num_lb && translen) { + *map++ = transsec; + lb_num++; transsec++; + overlap--; num_lb--; translen--; + } + break; + default: + DPRINTF(TRANSLATE, + ("Translate file extent " + "failed: bad flags %x\n", flags)); + UDF_UNLOCK_NODE(udf_node, 0); + return EINVAL; + } + } + if (num_lb == 0) + break; + + if (flags != UDF_EXT_REDIRECT) + foffset = end_foffset; + slot++; + } + UDF_UNLOCK_NODE(udf_node, 0); + + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int +udf_search_free_vatloc(struct udf_mount *ump, uint32_t *lbnumres) +{ + uint32_t lb_size, lb_num, lb_map, udf_rw32_lbmap; + uint8_t *blob; + int entry, chunk, found, error; + + KASSERT(ump); + KASSERT(ump->logical_vol); + + lb_size = udf_rw32(ump->logical_vol->lb_size); + blob = malloc(lb_size, M_UDFTEMP, M_WAITOK); + + /* TODO static allocation of search chunk */ + + lb_num = MIN(ump->vat_entries, ump->vat_last_free_lb); + found = 0; + error = 0; + entry = 0; + do { + chunk = MIN(lb_size, (ump->vat_entries - lb_num) * 4); + if (chunk <= 0) + break; + /* load in chunk */ + error = udf_vat_read(ump->vat_node, blob, chunk, + ump->vat_offset + lb_num * 4); + + if (error) + break; + + /* search this chunk */ + for (entry=0; entry < chunk /4; entry++, lb_num++) { + udf_rw32_lbmap = *((uint32_t *) (blob + entry * 4)); + lb_map = udf_rw32(udf_rw32_lbmap); + if (lb_map == 0xffffffff) { + found = 1; + break; + } + } + } while (!found); + if (error) { + printf("udf_search_free_vatloc: error reading in vat chunk " + "(lb %d, size %d)\n", lb_num, chunk); + } + + if (!found) { + /* extend VAT */ + DPRINTF(WRITE, ("udf_search_free_vatloc: extending\n")); + lb_num = ump->vat_entries; + ump->vat_entries++; + } + + /* mark entry with initialiser just in case */ + lb_map = udf_rw32(0xfffffffe); + udf_vat_write(ump->vat_node, (uint8_t *) &lb_map, 4, + ump->vat_offset + lb_num *4); + ump->vat_last_free_lb = lb_num; + + free(blob, M_UDFTEMP); + *lbnumres = lb_num; + return 0; +} + + +static void +udf_bitmap_allocate(struct udf_bitmap *bitmap, int ismetadata, + uint32_t *num_lb, uint64_t *lmappos) +{ + uint32_t offset, lb_num, bit; + int32_t diff; + uint8_t *bpos; + int pass; + + if (!ismetadata) { + /* heuristic to keep the two pointers not too close */ + diff = bitmap->data_pos - bitmap->metadata_pos; + if ((diff >= 0) && (diff < 1024)) + bitmap->data_pos = bitmap->metadata_pos + 1024; + } + offset = ismetadata ? bitmap->metadata_pos : bitmap->data_pos; + offset &= ~7; + for (pass = 0; pass < 2; pass++) { + if (offset >= bitmap->max_offset) + offset = 0; + + while (offset < bitmap->max_offset) { + if (*num_lb == 0) + break; + + /* use first bit not set */ + bpos = bitmap->bits + offset/8; + bit = ffs(*bpos); /* returns 0 or 1..8 */ + if (bit == 0) { + offset += 8; + continue; + } + + /* check for ffs overshoot */ + if (offset + bit-1 >= bitmap->max_offset) { + offset = bitmap->max_offset; + break; + } + + DPRINTF(PARANOIA, ("XXX : allocate %d, %p, bit %d\n", + offset + bit -1, bpos, bit-1)); + *bpos &= ~(1 << (bit-1)); + lb_num = offset + bit-1; + *lmappos++ = lb_num; + *num_lb = *num_lb - 1; + // offset = (offset & ~7); + } + } + + if (ismetadata) { + bitmap->metadata_pos = offset; + } else { + bitmap->data_pos = offset; + } +} + + +static void +udf_bitmap_free(struct udf_bitmap *bitmap, uint32_t lb_num, uint32_t num_lb) +{ + uint32_t offset; + uint32_t bit, bitval; + uint8_t *bpos; + + offset = lb_num; + + /* starter bits */ + bpos = bitmap->bits + offset/8; + bit = offset % 8; + while ((bit != 0) && (num_lb > 0)) { + bitval = (1 << bit); + KASSERT((*bpos & bitval) == 0); + DPRINTF(PARANOIA, ("XXX : free %d, %p, %d\n", + offset, bpos, bit)); + *bpos |= bitval; + offset++; num_lb--; + bit = (bit + 1) % 8; + } + if (num_lb == 0) + return; + + /* whole bytes */ + KASSERT(bit == 0); + bpos = bitmap->bits + offset / 8; + while (num_lb >= 8) { + KASSERT((*bpos == 0)); + DPRINTF(PARANOIA, ("XXX : free %d + 8, %p\n", offset, bpos)); + *bpos = 255; + offset += 8; num_lb -= 8; + bpos++; + } + + /* stop bits */ + KASSERT(num_lb < 8); + bit = 0; + while (num_lb > 0) { + bitval = (1 << bit); + KASSERT((*bpos & bitval) == 0); + DPRINTF(PARANOIA, ("XXX : free %d, %p, %d\n", + offset, bpos, bit)); + *bpos |= bitval; + offset++; num_lb--; + bit = (bit + 1) % 8; + } +} + + +static uint32_t +udf_bitmap_check_trunc_free(struct udf_bitmap *bitmap, uint32_t to_trunc) +{ + uint32_t seq_free, offset; + uint8_t *bpos; + uint8_t bit, bitval; + + DPRINTF(RESERVE, ("\ttrying to trunc %d bits from bitmap\n", to_trunc)); + offset = bitmap->max_offset - to_trunc; + + /* starter bits (if any) */ + bpos = bitmap->bits + offset/8; + bit = offset % 8; + seq_free = 0; + while (to_trunc > 0) { + seq_free++; + bitval = (1 << bit); + if (!(*bpos & bitval)) + seq_free = 0; + offset++; to_trunc--; + bit++; + if (bit == 8) { + bpos++; + bit = 0; + } + } + + DPRINTF(RESERVE, ("\tfound %d sequential free bits in bitmap\n", seq_free)); + return seq_free; +} + +/* --------------------------------------------------------------------- */ + +/* + * We check for overall disc space with a margin to prevent critical + * conditions. If disc space is low we try to force a sync() to improve our + * estimates. When confronted with meta-data partition size shortage we know + * we have to check if it can be extended and we need to extend it when + * needed. + * + * A 2nd strategy we could use when disc space is getting low on a disc + * formatted with a meta-data partition is to see if there are sparse areas in + * the meta-data partition and free blocks there for extra data. + */ + +void +udf_do_reserve_space(struct udf_mount *ump, struct udf_node *udf_node, + uint16_t vpart_num, uint32_t num_lb) +{ + ump->uncommitted_lbs[vpart_num] += num_lb; + if (udf_node) + udf_node->uncommitted_lbs += num_lb; +} + + +void +udf_do_unreserve_space(struct udf_mount *ump, struct udf_node *udf_node, + uint16_t vpart_num, uint32_t num_lb) +{ + ump->uncommitted_lbs[vpart_num] -= num_lb; + if (ump->uncommitted_lbs[vpart_num] < 0) { + DPRINTF(RESERVE, ("UDF: underflow on partition reservation, " + "part %d: %d\n", vpart_num, + ump->uncommitted_lbs[vpart_num])); + ump->uncommitted_lbs[vpart_num] = 0; + } + if (udf_node) { + udf_node->uncommitted_lbs -= num_lb; + if (udf_node->uncommitted_lbs < 0) { + DPRINTF(RESERVE, ("UDF: underflow of node " + "reservation : %d\n", + udf_node->uncommitted_lbs)); + udf_node->uncommitted_lbs = 0; + } + } +} + + +int +udf_reserve_space(struct udf_mount *ump, struct udf_node *udf_node, + int udf_c_type, uint16_t vpart_num, uint32_t num_lb, int can_fail) +{ + uint64_t freeblks; + uint64_t slack; + int i, error; + + slack = 0; + if (can_fail) + slack = UDF_DISC_SLACK; + + error = 0; + mutex_enter(&ump->allocate_mutex); + + /* check if there is enough space available */ + for (i = 0; i < 3; i++) { /* XXX arbitrary number */ + udf_calc_vpart_freespace(ump, vpart_num, &freeblks); + if (num_lb + slack < freeblks) + break; + /* issue SYNC */ + DPRINTF(RESERVE, ("udf_reserve_space: issuing sync\n")); + mutex_exit(&ump->allocate_mutex); + udf_do_sync(ump, FSCRED, 0); + mutex_enter(&mntvnode_lock); + /* 1/8 second wait */ + cv_timedwait(&ump->dirtynodes_cv, &mntvnode_lock, + hz/8); + mutex_exit(&mntvnode_lock); + mutex_enter(&ump->allocate_mutex); + } + + /* check if there is enough space available now */ + udf_calc_vpart_freespace(ump, vpart_num, &freeblks); + if (num_lb + slack >= freeblks) { + DPRINTF(RESERVE, ("udf_reserve_space: try to redistribute " + "partition space\n")); + DPRINTF(RESERVE, ("\tvpart %d, type %d is full\n", + vpart_num, ump->vtop_alloc[vpart_num])); + /* Try to redistribute space if possible */ + udf_collect_free_space_for_vpart(ump, vpart_num, num_lb + slack); + } + + /* check if there is enough space available now */ + udf_calc_vpart_freespace(ump, vpart_num, &freeblks); + if (num_lb + slack <= freeblks) { + udf_do_reserve_space(ump, udf_node, vpart_num, num_lb); + } else { + DPRINTF(RESERVE, ("udf_reserve_space: out of disc space\n")); + error = ENOSPC; + } + + mutex_exit(&ump->allocate_mutex); + return error; +} + + +void +udf_cleanup_reservation(struct udf_node *udf_node) +{ + struct udf_mount *ump = udf_node->ump; + int vpart_num; + + mutex_enter(&ump->allocate_mutex); + + /* compensate for overlapping blocks */ + DPRINTF(RESERVE, ("UDF: overlapped %d blocks in count\n", udf_node->uncommitted_lbs)); + + vpart_num = udf_get_record_vpart(ump, udf_get_c_type(udf_node)); + udf_do_unreserve_space(ump, udf_node, vpart_num, udf_node->uncommitted_lbs); + + DPRINTF(RESERVE, ("\ttotal now %d\n", ump->uncommitted_lbs[vpart_num])); + + /* sanity */ + if (ump->uncommitted_lbs[vpart_num] < 0) + ump->uncommitted_lbs[vpart_num] = 0; + + mutex_exit(&ump->allocate_mutex); +} + +/* --------------------------------------------------------------------- */ + +/* + * Allocate an extent of given length on given virt. partition. It doesn't + * have to be one stretch. + */ + +int +udf_allocate_space(struct udf_mount *ump, struct udf_node *udf_node, + int udf_c_type, uint16_t vpart_num, uint32_t num_lb, uint64_t *lmapping) +{ + struct mmc_trackinfo *alloc_track, *other_track; + struct udf_bitmap *bitmap; + struct part_desc *pdesc; + struct logvol_int_desc *lvid; + uint64_t *lmappos; + uint32_t ptov, lb_num, *freepos, free_lbs; + int lb_size __diagused, alloc_num_lb; + int alloc_type, error; + int is_node; + + DPRINTF(CALL, ("udf_allocate_space(ctype %d, vpart %d, num_lb %d\n", + udf_c_type, vpart_num, num_lb)); + mutex_enter(&ump->allocate_mutex); + + lb_size = udf_rw32(ump->logical_vol->lb_size); + KASSERT(lb_size == ump->discinfo.sector_size); + + alloc_type = ump->vtop_alloc[vpart_num]; + is_node = (udf_c_type == UDF_C_NODE); + + lmappos = lmapping; + error = 0; + switch (alloc_type) { + case UDF_ALLOC_VAT : + /* search empty slot in VAT file */ + KASSERT(num_lb == 1); + error = udf_search_free_vatloc(ump, &lb_num); + if (!error) { + *lmappos = lb_num; + + /* reserve on the backing sequential partition since + * that partition is credited back later */ + udf_do_reserve_space(ump, udf_node, + ump->vtop[vpart_num], num_lb); + } + break; + case UDF_ALLOC_SEQUENTIAL : + /* sequential allocation on recordable media */ + /* get partition backing up this vpart_num_num */ + pdesc = ump->partitions[ump->vtop[vpart_num]]; + + /* calculate offset from physical base partition */ + ptov = udf_rw32(pdesc->start_loc); + + /* get our track descriptors */ + if (vpart_num == ump->node_part) { + alloc_track = &ump->metadata_track; + other_track = &ump->data_track; + } else { + alloc_track = &ump->data_track; + other_track = &ump->metadata_track; + } + + /* allocate */ + for (lb_num = 0; lb_num < num_lb; lb_num++) { + *lmappos++ = alloc_track->next_writable - ptov; + alloc_track->next_writable++; + alloc_track->free_blocks--; + } + + /* keep other track up-to-date */ + if (alloc_track->tracknr == other_track->tracknr) + memcpy(other_track, alloc_track, + sizeof(struct mmc_trackinfo)); + break; + case UDF_ALLOC_SPACEMAP : + /* try to allocate on unallocated bits */ + alloc_num_lb = num_lb; + bitmap = &ump->part_unalloc_bits[vpart_num]; + udf_bitmap_allocate(bitmap, is_node, &alloc_num_lb, lmappos); + ump->lvclose |= UDF_WRITE_PART_BITMAPS; + + /* have we allocated all? */ + if (alloc_num_lb) { + /* TODO convert freed to unalloc and try again */ + /* free allocated piece for now */ + lmappos = lmapping; + for (lb_num=0; lb_num < num_lb-alloc_num_lb; lb_num++) { + udf_bitmap_free(bitmap, *lmappos++, 1); + } + error = ENOSPC; + } + if (!error) { + /* adjust freecount */ + lvid = ump->logvol_integrity; + freepos = &lvid->tables[0] + vpart_num; + free_lbs = udf_rw32(*freepos); + *freepos = udf_rw32(free_lbs - num_lb); + } + break; + case UDF_ALLOC_METABITMAP : /* UDF 2.50, 2.60 BluRay-RE */ + /* allocate on metadata unallocated bits */ + alloc_num_lb = num_lb; + bitmap = &ump->metadata_unalloc_bits; + udf_bitmap_allocate(bitmap, is_node, &alloc_num_lb, lmappos); + ump->lvclose |= UDF_WRITE_PART_BITMAPS; + + /* have we allocated all? */ + if (alloc_num_lb) { + /* YIKES! TODO we need to extend the metadata partition */ + /* free allocated piece for now */ + lmappos = lmapping; + for (lb_num=0; lb_num < num_lb-alloc_num_lb; lb_num++) { + udf_bitmap_free(bitmap, *lmappos++, 1); + } + error = ENOSPC; + } + if (!error) { + /* adjust freecount */ + lvid = ump->logvol_integrity; + freepos = &lvid->tables[0] + vpart_num; + free_lbs = udf_rw32(*freepos); + *freepos = udf_rw32(free_lbs - num_lb); + } + break; + case UDF_ALLOC_METASEQUENTIAL : /* UDF 2.60 BluRay-R */ + case UDF_ALLOC_RELAXEDSEQUENTIAL : /* UDF 2.50/~meta BluRay-R */ + printf("ALERT: udf_allocate_space : allocation %d " + "not implemented yet!\n", alloc_type); + /* TODO implement, doesn't have to be contiguous */ + error = ENOSPC; + break; + } + + if (!error) { + /* credit our partition since we have committed the space */ + udf_do_unreserve_space(ump, udf_node, vpart_num, num_lb); + } + +#ifdef DEBUG + if (udf_verbose & UDF_DEBUG_ALLOC) { + lmappos = lmapping; + printf("udf_allocate_space, allocated logical lba :\n"); + for (lb_num = 0; lb_num < num_lb; lb_num++) { + printf("%s %"PRIu64, (lb_num > 0)?",":"", + *lmappos++); + } + printf("\n"); + } +#endif + mutex_exit(&ump->allocate_mutex); + + return error; +} + +/* --------------------------------------------------------------------- */ + +void +udf_free_allocated_space(struct udf_mount *ump, uint32_t lb_num, + uint16_t vpart_num, uint32_t num_lb) +{ + struct udf_bitmap *bitmap; + struct logvol_int_desc *lvid; + uint32_t lb_map, udf_rw32_lbmap; + uint32_t *freepos, free_lbs; + int phys_part; + int error __diagused; + + DPRINTF(ALLOC, ("udf_free_allocated_space: freeing virt lbnum %d " + "part %d + %d sect\n", lb_num, vpart_num, num_lb)); + + /* no use freeing zero length */ + if (num_lb == 0) + return; + + mutex_enter(&ump->allocate_mutex); + + switch (ump->vtop_tp[vpart_num]) { + case UDF_VTOP_TYPE_PHYS : + case UDF_VTOP_TYPE_SPARABLE : + /* free space to freed or unallocated space bitmap */ + phys_part = ump->vtop[vpart_num]; + + /* first try freed space bitmap */ + bitmap = &ump->part_freed_bits[phys_part]; + + /* if not defined, use unallocated bitmap */ + if (bitmap->bits == NULL) + bitmap = &ump->part_unalloc_bits[phys_part]; + + /* if no bitmaps are defined, bail out; XXX OK? */ + if (bitmap->bits == NULL) + break; + + /* free bits if its defined */ + KASSERT(bitmap->bits); + ump->lvclose |= UDF_WRITE_PART_BITMAPS; + udf_bitmap_free(bitmap, lb_num, num_lb); + + /* adjust freecount */ + lvid = ump->logvol_integrity; + freepos = &lvid->tables[0] + vpart_num; + free_lbs = udf_rw32(*freepos); + *freepos = udf_rw32(free_lbs + num_lb); + break; + case UDF_VTOP_TYPE_VIRT : + /* free this VAT entry */ + KASSERT(num_lb == 1); + + lb_map = 0xffffffff; + udf_rw32_lbmap = udf_rw32(lb_map); + error = udf_vat_write(ump->vat_node, + (uint8_t *) &udf_rw32_lbmap, 4, + ump->vat_offset + lb_num * 4); + KASSERT(error == 0); + ump->vat_last_free_lb = MIN(ump->vat_last_free_lb, lb_num); + break; + case UDF_VTOP_TYPE_META : + /* free space in the metadata bitmap */ + bitmap = &ump->metadata_unalloc_bits; + KASSERT(bitmap->bits); + + ump->lvclose |= UDF_WRITE_PART_BITMAPS; + udf_bitmap_free(bitmap, lb_num, num_lb); + + /* adjust freecount */ + lvid = ump->logvol_integrity; + freepos = &lvid->tables[0] + vpart_num; + free_lbs = udf_rw32(*freepos); + *freepos = udf_rw32(free_lbs + num_lb); + break; + default: + printf("ALERT: udf_free_allocated_space : allocation %d " + "not implemented yet!\n", ump->vtop_tp[vpart_num]); + break; + } + + mutex_exit(&ump->allocate_mutex); +} + +/* --------------------------------------------------------------------- */ + +/* + * Special function to synchronise the metadatamirror file when they change on + * resizing. When the metadatafile is actually duplicated, this action is a + * no-op since they describe different extents on the disc. + */ + +void +udf_synchronise_metadatamirror_node(struct udf_mount *ump) +{ + struct udf_node *meta_node, *metamirror_node; + struct long_ad s_ad; + uint32_t len, flags; + int slot, cpy_slot; + int error, eof; + + if (ump->metadata_flags & METADATA_DUPLICATED) + return; + + meta_node = ump->metadata_node; + metamirror_node = ump->metadatamirror_node; + + /* 1) wipe mirror node */ + udf_wipe_adslots(metamirror_node); + + /* 2) copy all node descriptors from the meta_node */ + slot = 0; + cpy_slot = 0; + for (;;) { + udf_get_adslot(meta_node, slot, &s_ad, &eof); + if (eof) + break; + len = udf_rw32(s_ad.len); + flags = UDF_EXT_FLAGS(len); + len = UDF_EXT_LEN(len); + + if (flags == UDF_EXT_REDIRECT) { + slot++; + continue; + } + + error = udf_append_adslot(metamirror_node, &cpy_slot, &s_ad); + if (error) { + /* WTF, this shouldn't happen, what to do now? */ + panic("udf_synchronise_metadatamirror_node failed!"); + } + cpy_slot++; + slot++; + } + + /* 3) adjust metamirror_node size */ + if (meta_node->fe) { + KASSERT(metamirror_node->fe); + metamirror_node->fe->inf_len = meta_node->fe->inf_len; + } else { + KASSERT(meta_node->efe); + KASSERT(metamirror_node->efe); + metamirror_node->efe->inf_len = meta_node->efe->inf_len; + metamirror_node->efe->obj_size = meta_node->efe->obj_size; + } + + /* for sanity */ + udf_count_alloc_exts(metamirror_node); +} + +/* --------------------------------------------------------------------- */ + +/* + * When faced with an out of space but there is still space available on other + * partitions, try to redistribute the space. This is only defined for media + * using Metadata partitions. + * + * There are two formats to deal with. Either its a `normal' metadata + * partition and we can move blocks between a metadata bitmap and its + * companion data spacemap OR its a UDF 2.60 formatted BluRay-R disc with POW + * and a metadata partition. + */ + +/* implementation limit: ump->datapart is the companion partition */ +static uint32_t +udf_trunc_metadatapart(struct udf_mount *ump, uint32_t num_lb) +{ + struct udf_node *bitmap_node; + struct udf_bitmap *bitmap; + struct space_bitmap_desc *sbd, *new_sbd; + struct logvol_int_desc *lvid; + uint64_t inf_len; + uint64_t meta_free_lbs, data_free_lbs, to_trunc; + uint32_t *freepos, *sizepos; + uint32_t unit, lb_size; + uint16_t meta_vpart_num, data_vpart_num, num_vpart; + int err __diagused; + + unit = ump->metadata_alloc_unit_size; + lb_size = udf_rw32(ump->logical_vol->lb_size); + lvid = ump->logvol_integrity; + + /* XXX + * + * the following checks will fail for BD-R UDF 2.60! but they are + * read-only for now anyway! Its even doubtfull if it is to be allowed + * for these discs. + */ + + /* lookup vpart for metadata partition */ + meta_vpart_num = ump->node_part; + KASSERT(ump->vtop_alloc[meta_vpart_num] == UDF_ALLOC_METABITMAP); + + /* lookup vpart for data partition */ + data_vpart_num = ump->data_part; + KASSERT(ump->vtop_alloc[data_vpart_num] == UDF_ALLOC_SPACEMAP); + + udf_calc_vpart_freespace(ump, data_vpart_num, &data_free_lbs); + udf_calc_vpart_freespace(ump, meta_vpart_num, &meta_free_lbs); + + DPRINTF(RESERVE, ("\tfree space on data partition %"PRIu64" blks\n", data_free_lbs)); + DPRINTF(RESERVE, ("\tfree space on metadata partition %"PRIu64" blks\n", meta_free_lbs)); + + /* give away some of the free meta space, in unit block sizes */ + to_trunc = meta_free_lbs/4; /* give out a quarter */ + to_trunc = MAX(to_trunc, num_lb); + to_trunc = unit * ((to_trunc + unit-1) / unit); /* round up */ + + /* scale down if needed and bail out when out of space */ + if (to_trunc >= meta_free_lbs) + return num_lb; + + /* check extent of bits marked free at the end of the map */ + bitmap = &ump->metadata_unalloc_bits; + to_trunc = udf_bitmap_check_trunc_free(bitmap, to_trunc); + to_trunc = unit * (to_trunc / unit); /* round down again */ + if (to_trunc == 0) + return num_lb; + + DPRINTF(RESERVE, ("\ttruncating %"PRIu64" lbs from the metadata bitmap\n", + to_trunc)); + + /* get length of the metadata bitmap node file */ + bitmap_node = ump->metadatabitmap_node; + if (bitmap_node->fe) { + inf_len = udf_rw64(bitmap_node->fe->inf_len); + } else { + KASSERT(bitmap_node->efe); + inf_len = udf_rw64(bitmap_node->efe->inf_len); + } + inf_len -= to_trunc/8; + + /* as per [UDF 2.60/2.2.13.6] : */ + /* 1) update the SBD in the metadata bitmap file */ + sbd = (struct space_bitmap_desc *) bitmap->blob; + sbd->num_bits = udf_rw32(udf_rw32(sbd->num_bits) - to_trunc); + sbd->num_bytes = udf_rw32(udf_rw32(sbd->num_bytes) - to_trunc/8); + bitmap->max_offset = udf_rw32(sbd->num_bits); + + num_vpart = udf_rw32(lvid->num_part); + freepos = &lvid->tables[0] + meta_vpart_num; + sizepos = &lvid->tables[0] + num_vpart + meta_vpart_num; + *freepos = udf_rw32(*freepos) - to_trunc; + *sizepos = udf_rw32(*sizepos) - to_trunc; + + /* realloc bitmap for better memory usage */ + new_sbd = realloc(sbd, inf_len, M_UDFVOLD, + M_CANFAIL | M_WAITOK); + if (new_sbd) { + /* update pointers */ + ump->metadata_unalloc_dscr = new_sbd; + bitmap->blob = (uint8_t *) new_sbd; + } + ump->lvclose |= UDF_WRITE_PART_BITMAPS; + + /* + * The truncated space is secured now and can't be allocated anymore. + * Release the allocate mutex so we can shrink the nodes the normal + * way. + */ + mutex_exit(&ump->allocate_mutex); + + /* 2) trunc the metadata bitmap information file, freeing blocks */ + err = udf_shrink_node(bitmap_node, inf_len); + KASSERT(err == 0); + + /* 3) trunc the metadata file and mirror file, freeing blocks */ + inf_len = (uint64_t) udf_rw32(sbd->num_bits) * lb_size; /* [4/14.12.4] */ + err = udf_shrink_node(ump->metadata_node, inf_len); + KASSERT(err == 0); + if (ump->metadatamirror_node) { + if (ump->metadata_flags & METADATA_DUPLICATED) { + err = udf_shrink_node(ump->metadatamirror_node, inf_len); + } else { + /* extents will be copied on writeout */ + } + KASSERT(err == 0); + } + ump->lvclose |= UDF_WRITE_METAPART_NODES; + + /* relock before exit */ + mutex_enter(&ump->allocate_mutex); + + if (to_trunc > num_lb) + return 0; + return num_lb - to_trunc; +} + + +static void +udf_sparsify_metadatapart(struct udf_mount *ump, uint32_t num_lb) +{ + /* NOT IMPLEMENTED, fail */ +} + + +static void +udf_collect_free_space_for_vpart(struct udf_mount *ump, + uint16_t vpart_num, uint32_t num_lb) +{ + /* allocate mutex is helt */ + + /* only defined for metadata partitions */ + if (ump->vtop_tp[ump->node_part] != UDF_VTOP_TYPE_META) { + DPRINTF(RESERVE, ("\tcan't grow/shrink; no metadata partitioning\n")); + return; + } + + /* UDF 2.60 BD-R+POW? */ + if (ump->vtop_alloc[ump->node_part] == UDF_ALLOC_METASEQUENTIAL) { + DPRINTF(RESERVE, ("\tUDF 2.60 BD-R+POW track grow not implemented yet\n")); + return; + } + + if (ump->vtop_tp[vpart_num] == UDF_VTOP_TYPE_META) { + /* try to grow the meta partition */ + DPRINTF(RESERVE, ("\ttrying to grow the meta partition\n")); + /* as per [UDF 2.60/2.2.13.5] : extend bitmap and metadata file(s) */ + DPRINTF(NOTIMPL, ("\tgrowing meta partition not implemented yet\n")); + } else { + /* try to shrink the metadata partition */ + DPRINTF(RESERVE, ("\ttrying to shrink the meta partition\n")); + /* as per [UDF 2.60/2.2.13.6] : either trunc or make sparse */ + num_lb = udf_trunc_metadatapart(ump, num_lb); + if (num_lb) + udf_sparsify_metadatapart(ump, num_lb); + } + + /* allocate mutex should still be helt */ +} + +/* --------------------------------------------------------------------- */ + +/* + * Allocate a buf on disc for direct write out. The space doesn't have to be + * contiguous as the caller takes care of this. + */ + +void +udf_late_allocate_buf(struct udf_mount *ump, struct buf *buf, + uint64_t *lmapping, struct long_ad *node_ad_cpy, uint16_t *vpart_nump) +{ + struct udf_node *udf_node = VTOI(buf->b_vp); + int lb_size, udf_c_type; + int vpart_num, num_lb; + int error, s; + + /* + * for each sector in the buf, allocate a sector on disc and record + * its position in the provided mapping array. + * + * If its userdata or FIDs, record its location in its node. + */ + + lb_size = udf_rw32(ump->logical_vol->lb_size); + num_lb = (buf->b_bcount + lb_size -1) / lb_size; + udf_c_type = buf->b_udf_c_type; + + KASSERT(lb_size == ump->discinfo.sector_size); + + /* select partition to record the buffer on */ + vpart_num = *vpart_nump = udf_get_record_vpart(ump, udf_c_type); + + if (udf_c_type == UDF_C_NODE) { + /* if not VAT, its allready allocated */ + if (ump->vtop_alloc[ump->node_part] != UDF_ALLOC_VAT) + return; + + /* allocate on its backing sequential partition */ + vpart_num = ump->data_part; + } + + /* XXX can this still happen? */ + /* do allocation on the selected partition */ + error = udf_allocate_space(ump, udf_node, udf_c_type, + vpart_num, num_lb, lmapping); + if (error) { + /* + * ARGH! we haven't done our accounting right! it should + * allways succeed. + */ + panic("UDF disc allocation accounting gone wrong"); + } + + /* If its userdata or FIDs, record its allocation in its node. */ + if ((udf_c_type == UDF_C_USERDATA) || + (udf_c_type == UDF_C_FIDS) || + (udf_c_type == UDF_C_METADATA_SBM)) + { + udf_record_allocation_in_node(ump, buf, vpart_num, lmapping, + node_ad_cpy); + /* decrement our outstanding bufs counter */ + s = splbio(); + udf_node->outstanding_bufs--; + splx(s); + } +} + +/* --------------------------------------------------------------------- */ + +/* + * Try to merge a1 with the new piece a2. udf_ads_merge returns error when not + * possible (anymore); a2 returns the rest piece. + */ + +static int +udf_ads_merge(uint32_t max_len, uint32_t lb_size, struct long_ad *a1, struct long_ad *a2) +{ + uint32_t merge_len; + uint32_t a1_len, a2_len; + uint32_t a1_flags, a2_flags; + uint32_t a1_lbnum, a2_lbnum; + uint16_t a1_part, a2_part; + + a1_flags = UDF_EXT_FLAGS(udf_rw32(a1->len)); + a1_len = UDF_EXT_LEN(udf_rw32(a1->len)); + a1_lbnum = udf_rw32(a1->loc.lb_num); + a1_part = udf_rw16(a1->loc.part_num); + + a2_flags = UDF_EXT_FLAGS(udf_rw32(a2->len)); + a2_len = UDF_EXT_LEN(udf_rw32(a2->len)); + a2_lbnum = udf_rw32(a2->loc.lb_num); + a2_part = udf_rw16(a2->loc.part_num); + + /* defines same space */ + if (a1_flags != a2_flags) + return 1; + + if (a1_flags != UDF_EXT_FREE) { + /* the same partition */ + if (a1_part != a2_part) + return 1; + + /* a2 is successor of a1 */ + if (a1_lbnum * lb_size + a1_len != a2_lbnum * lb_size) + return 1; + } + + /* merge as most from a2 if possible */ + merge_len = MIN(a2_len, max_len - a1_len); + a1_len += merge_len; + a2_len -= merge_len; + a2_lbnum += merge_len/lb_size; + + a1->len = udf_rw32(a1_len | a1_flags); + a2->len = udf_rw32(a2_len | a2_flags); + a2->loc.lb_num = udf_rw32(a2_lbnum); + + if (a2_len > 0) + return 1; + + /* there is space over to merge */ + return 0; +} + +/* --------------------------------------------------------------------- */ + +static void +udf_wipe_adslots(struct udf_node *udf_node) +{ + struct file_entry *fe; + struct extfile_entry *efe; + struct alloc_ext_entry *ext; + uint32_t lb_size, dscr_size, l_ea, max_l_ad, crclen; + uint8_t *data_pos; + int extnr; + + lb_size = udf_rw32(udf_node->ump->logical_vol->lb_size); + + fe = udf_node->fe; + efe = udf_node->efe; + if (fe) { + dscr_size = sizeof(struct file_entry) -1; + l_ea = udf_rw32(fe->l_ea); + data_pos = (uint8_t *) fe + dscr_size + l_ea; + } else { + dscr_size = sizeof(struct extfile_entry) -1; + l_ea = udf_rw32(efe->l_ea); + data_pos = (uint8_t *) efe + dscr_size + l_ea; + } + max_l_ad = lb_size - dscr_size - l_ea; + + /* wipe fe/efe */ + memset(data_pos, 0, max_l_ad); + crclen = dscr_size - UDF_DESC_TAG_LENGTH + l_ea; + if (fe) { + fe->l_ad = udf_rw32(0); + fe->logblks_rec = udf_rw64(0); + fe->tag.desc_crc_len = udf_rw16(crclen); + } else { + efe->l_ad = udf_rw32(0); + efe->logblks_rec = udf_rw64(0); + efe->tag.desc_crc_len = udf_rw16(crclen); + } + + /* wipe all allocation extent entries */ + for (extnr = 0; extnr < udf_node->num_extensions; extnr++) { + ext = udf_node->ext[extnr]; + dscr_size = sizeof(struct alloc_ext_entry) -1; + data_pos = (uint8_t *) ext->data; + max_l_ad = lb_size - dscr_size; + memset(data_pos, 0, max_l_ad); + ext->l_ad = udf_rw32(0); + + crclen = dscr_size - UDF_DESC_TAG_LENGTH; + ext->tag.desc_crc_len = udf_rw16(crclen); + } + udf_node->i_flags |= IN_NODE_REBUILD; +} + +/* --------------------------------------------------------------------- */ + +void +udf_get_adslot(struct udf_node *udf_node, int slot, struct long_ad *icb, + int *eof) { + struct file_entry *fe; + struct extfile_entry *efe; + struct alloc_ext_entry *ext; + struct icb_tag *icbtag; + struct short_ad *short_ad; + struct long_ad *long_ad, l_icb; + uint32_t offset; + uint32_t dscr_size, l_ea, l_ad, flags; + uint8_t *data_pos; + int icbflags, addr_type, adlen, extnr; + + fe = udf_node->fe; + efe = udf_node->efe; + if (fe) { + icbtag = &fe->icbtag; + dscr_size = sizeof(struct file_entry) -1; + l_ea = udf_rw32(fe->l_ea); + l_ad = udf_rw32(fe->l_ad); + data_pos = (uint8_t *) fe + dscr_size + l_ea; + } else { + icbtag = &efe->icbtag; + dscr_size = sizeof(struct extfile_entry) -1; + l_ea = udf_rw32(efe->l_ea); + l_ad = udf_rw32(efe->l_ad); + data_pos = (uint8_t *) efe + dscr_size + l_ea; + } + + icbflags = udf_rw16(icbtag->flags); + addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK; + + /* just in case we're called on an intern, its EOF */ + if (addr_type == UDF_ICB_INTERN_ALLOC) { + memset(icb, 0, sizeof(struct long_ad)); + *eof = 1; + return; + } + + adlen = 0; + if (addr_type == UDF_ICB_SHORT_ALLOC) { + adlen = sizeof(struct short_ad); + } else if (addr_type == UDF_ICB_LONG_ALLOC) { + adlen = sizeof(struct long_ad); + } + + /* if offset too big, we go to the allocation extensions */ + offset = slot * adlen; + extnr = -1; + while (offset >= l_ad) { + /* check if our last entry is a redirect */ + if (addr_type == UDF_ICB_SHORT_ALLOC) { + short_ad = (struct short_ad *) (data_pos + l_ad-adlen); + l_icb.len = short_ad->len; + l_icb.loc.part_num = udf_node->loc.loc.part_num; + l_icb.loc.lb_num = short_ad->lb_num; + } else { + KASSERT(addr_type == UDF_ICB_LONG_ALLOC); + long_ad = (struct long_ad *) (data_pos + l_ad-adlen); + l_icb = *long_ad; + } + flags = UDF_EXT_FLAGS(udf_rw32(l_icb.len)); + if (flags != UDF_EXT_REDIRECT) { + l_ad = 0; /* force EOF */ + break; + } + + /* advance to next extent */ + extnr++; + if (extnr >= udf_node->num_extensions) { + l_ad = 0; /* force EOF */ + break; + } + offset = offset - l_ad; + ext = udf_node->ext[extnr]; + dscr_size = sizeof(struct alloc_ext_entry) -1; + l_ad = udf_rw32(ext->l_ad); + data_pos = (uint8_t *) ext + dscr_size; + } + + /* XXX l_ad == 0 should be enough to check */ + *eof = (offset >= l_ad) || (l_ad == 0); + if (*eof) { + DPRINTF(PARANOIDADWLK, ("returning EOF, extnr %d, offset %d, " + "l_ad %d\n", extnr, offset, l_ad)); + memset(icb, 0, sizeof(struct long_ad)); + return; + } + + /* get the element */ + if (addr_type == UDF_ICB_SHORT_ALLOC) { + short_ad = (struct short_ad *) (data_pos + offset); + icb->len = short_ad->len; + icb->loc.part_num = udf_node->loc.loc.part_num; + icb->loc.lb_num = short_ad->lb_num; + } else if (addr_type == UDF_ICB_LONG_ALLOC) { + long_ad = (struct long_ad *) (data_pos + offset); + *icb = *long_ad; + } + DPRINTF(PARANOIDADWLK, ("returning element : v %d, lb %d, len %d, " + "flags %d\n", icb->loc.part_num, icb->loc.lb_num, + UDF_EXT_LEN(icb->len), UDF_EXT_FLAGS(icb->len))); +} + +/* --------------------------------------------------------------------- */ + +int +udf_append_adslot(struct udf_node *udf_node, int *slot, struct long_ad *icb) { + struct udf_mount *ump = udf_node->ump; + union dscrptr *dscr, *extdscr; + struct file_entry *fe; + struct extfile_entry *efe; + struct alloc_ext_entry *ext; + struct icb_tag *icbtag; + struct short_ad *short_ad; + struct long_ad *long_ad, o_icb, l_icb; + uint64_t logblks_rec, *logblks_rec_p; + uint64_t lmapping; + uint32_t offset, rest, len, lb_num; + uint32_t lb_size, dscr_size, l_ea, l_ad, *l_ad_p, max_l_ad, crclen; + uint32_t flags; + uint16_t vpart_num; + uint8_t *data_pos; + int icbflags, addr_type, adlen, extnr; + int error; + + lb_size = udf_rw32(ump->logical_vol->lb_size); + vpart_num = udf_rw16(udf_node->loc.loc.part_num); + + /* determine what descriptor we are in */ + fe = udf_node->fe; + efe = udf_node->efe; + if (fe) { + icbtag = &fe->icbtag; + dscr = (union dscrptr *) fe; + dscr_size = sizeof(struct file_entry) -1; + + l_ea = udf_rw32(fe->l_ea); + l_ad_p = &fe->l_ad; + logblks_rec_p = &fe->logblks_rec; + } else { + icbtag = &efe->icbtag; + dscr = (union dscrptr *) efe; + dscr_size = sizeof(struct extfile_entry) -1; + + l_ea = udf_rw32(efe->l_ea); + l_ad_p = &efe->l_ad; + logblks_rec_p = &efe->logblks_rec; + } + data_pos = (uint8_t *) dscr + dscr_size + l_ea; + max_l_ad = lb_size - dscr_size - l_ea; + + icbflags = udf_rw16(icbtag->flags); + addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK; + + /* just in case we're called on an intern, its EOF */ + if (addr_type == UDF_ICB_INTERN_ALLOC) { + panic("udf_append_adslot on UDF_ICB_INTERN_ALLOC\n"); + } + + adlen = 0; + if (addr_type == UDF_ICB_SHORT_ALLOC) { + adlen = sizeof(struct short_ad); + } else if (addr_type == UDF_ICB_LONG_ALLOC) { + adlen = sizeof(struct long_ad); + } + + /* clean up given long_ad since it can be a synthesized one */ + flags = UDF_EXT_FLAGS(udf_rw32(icb->len)); + if (flags == UDF_EXT_FREE) { + icb->loc.part_num = udf_rw16(0); + icb->loc.lb_num = udf_rw32(0); + } + + /* if offset too big, we go to the allocation extensions */ + l_ad = udf_rw32(*l_ad_p); + offset = (*slot) * adlen; + extnr = -1; + while (offset >= l_ad) { + /* check if our last entry is a redirect */ + if (addr_type == UDF_ICB_SHORT_ALLOC) { + short_ad = (struct short_ad *) (data_pos + l_ad-adlen); + l_icb.len = short_ad->len; + l_icb.loc.part_num = udf_node->loc.loc.part_num; + l_icb.loc.lb_num = short_ad->lb_num; + } else { + KASSERT(addr_type == UDF_ICB_LONG_ALLOC); + long_ad = (struct long_ad *) (data_pos + l_ad-adlen); + l_icb = *long_ad; + } + flags = UDF_EXT_FLAGS(udf_rw32(l_icb.len)); + if (flags != UDF_EXT_REDIRECT) { + /* only one past the last one is adressable */ + break; + } + + /* advance to next extent */ + extnr++; + KASSERT(extnr < udf_node->num_extensions); + offset = offset - l_ad; + + ext = udf_node->ext[extnr]; + dscr = (union dscrptr *) ext; + dscr_size = sizeof(struct alloc_ext_entry) -1; + max_l_ad = lb_size - dscr_size; + l_ad_p = &ext->l_ad; + l_ad = udf_rw32(*l_ad_p); + data_pos = (uint8_t *) ext + dscr_size; + } + DPRINTF(PARANOIDADWLK, ("append, ext %d, offset %d, l_ad %d\n", + extnr, offset, udf_rw32(*l_ad_p))); + KASSERT(l_ad == udf_rw32(*l_ad_p)); + + /* offset is offset within the current (E)FE/AED */ + l_ad = udf_rw32(*l_ad_p); + crclen = udf_rw16(dscr->tag.desc_crc_len); + logblks_rec = udf_rw64(*logblks_rec_p); + + /* overwriting old piece? */ + if (offset < l_ad) { + /* overwrite entry; compensate for the old element */ + if (addr_type == UDF_ICB_SHORT_ALLOC) { + short_ad = (struct short_ad *) (data_pos + offset); + o_icb.len = short_ad->len; + o_icb.loc.part_num = udf_rw16(0); /* ignore */ + o_icb.loc.lb_num = short_ad->lb_num; + } else if (addr_type == UDF_ICB_LONG_ALLOC) { + long_ad = (struct long_ad *) (data_pos + offset); + o_icb = *long_ad; + } else { + panic("Invalid address type in udf_append_adslot\n"); + } + + len = udf_rw32(o_icb.len); + if (UDF_EXT_FLAGS(len) == UDF_EXT_ALLOCATED) { + /* adjust counts */ + len = UDF_EXT_LEN(len); + logblks_rec -= (len + lb_size -1) / lb_size; + } + } + + /* check if we're not appending a redirection */ + flags = UDF_EXT_FLAGS(udf_rw32(icb->len)); + KASSERT(flags != UDF_EXT_REDIRECT); + + /* round down available space */ + rest = adlen * ((max_l_ad - offset) / adlen); + if (rest <= adlen) { + /* have to append aed, see if we already have a spare one */ + extnr++; + ext = udf_node->ext[extnr]; + l_icb = udf_node->ext_loc[extnr]; + if (ext == NULL) { + DPRINTF(ALLOC,("adding allocation extent %d\n", extnr)); + + error = udf_reserve_space(ump, NULL, UDF_C_NODE, + vpart_num, 1, /* can fail */ false); + if (error) { + printf("UDF: couldn't reserve space for AED!\n"); + return error; + } + error = udf_allocate_space(ump, NULL, UDF_C_NODE, + vpart_num, 1, &lmapping); + lb_num = lmapping; + if (error) + panic("UDF: couldn't allocate AED!\n"); + + /* initialise pointer to location */ + memset(&l_icb, 0, sizeof(struct long_ad)); + l_icb.len = udf_rw32(lb_size | UDF_EXT_REDIRECT); + l_icb.loc.lb_num = udf_rw32(lb_num); + l_icb.loc.part_num = udf_rw16(vpart_num); + + /* create new aed descriptor */ + udf_create_logvol_dscr(ump, udf_node, &l_icb, &extdscr); + ext = &extdscr->aee; + + udf_inittag(ump, &ext->tag, TAGID_ALLOCEXTENT, lb_num); + dscr_size = sizeof(struct alloc_ext_entry) -1; + max_l_ad = lb_size - dscr_size; + memset(ext->data, 0, max_l_ad); + ext->l_ad = udf_rw32(0); + ext->tag.desc_crc_len = + udf_rw16(dscr_size - UDF_DESC_TAG_LENGTH); + + /* declare aed */ + udf_node->num_extensions++; + udf_node->ext_loc[extnr] = l_icb; + udf_node->ext[extnr] = ext; + } + /* add redirect and adjust l_ad and crclen for old descr */ + if (addr_type == UDF_ICB_SHORT_ALLOC) { + short_ad = (struct short_ad *) (data_pos + offset); + short_ad->len = l_icb.len; + short_ad->lb_num = l_icb.loc.lb_num; + } else if (addr_type == UDF_ICB_LONG_ALLOC) { + long_ad = (struct long_ad *) (data_pos + offset); + *long_ad = l_icb; + } + l_ad += adlen; + crclen += adlen; + dscr->tag.desc_crc_len = udf_rw16(crclen); + *l_ad_p = udf_rw32(l_ad); + + /* advance to the new extension */ + KASSERT(ext != NULL); + dscr = (union dscrptr *) ext; + dscr_size = sizeof(struct alloc_ext_entry) -1; + max_l_ad = lb_size - dscr_size; + data_pos = (uint8_t *) dscr + dscr_size; + + l_ad_p = &ext->l_ad; + l_ad = udf_rw32(*l_ad_p); + crclen = udf_rw16(dscr->tag.desc_crc_len); + offset = 0; + + /* adjust callees slot count for link insert */ + *slot += 1; + } + + /* write out the element */ + DPRINTF(PARANOIDADWLK, ("adding element : %p : v %d, lb %d, " + "len %d, flags %d\n", data_pos + offset, + icb->loc.part_num, icb->loc.lb_num, + UDF_EXT_LEN(icb->len), UDF_EXT_FLAGS(icb->len))); + if (addr_type == UDF_ICB_SHORT_ALLOC) { + short_ad = (struct short_ad *) (data_pos + offset); + short_ad->len = icb->len; + short_ad->lb_num = icb->loc.lb_num; + } else if (addr_type == UDF_ICB_LONG_ALLOC) { + long_ad = (struct long_ad *) (data_pos + offset); + *long_ad = *icb; + } + + /* adjust logblks recorded count */ + len = udf_rw32(icb->len); + flags = UDF_EXT_FLAGS(len); + if (flags == UDF_EXT_ALLOCATED) + logblks_rec += (UDF_EXT_LEN(len) + lb_size -1) / lb_size; + *logblks_rec_p = udf_rw64(logblks_rec); + + /* adjust l_ad and crclen when needed */ + if (offset >= l_ad) { + l_ad += adlen; + crclen += adlen; + dscr->tag.desc_crc_len = udf_rw16(crclen); + *l_ad_p = udf_rw32(l_ad); + } + + return 0; +} + +/* --------------------------------------------------------------------- */ + +static void +udf_count_alloc_exts(struct udf_node *udf_node) +{ + struct long_ad s_ad; + uint32_t lb_num, len, flags; + uint16_t vpart_num; + int slot, eof; + int num_extents, extnr; + + if (udf_node->num_extensions == 0) + return; + + /* count number of allocation extents in use */ + num_extents = 0; + slot = 0; + for (;;) { + udf_get_adslot(udf_node, slot, &s_ad, &eof); + if (eof) + break; + len = udf_rw32(s_ad.len); + flags = UDF_EXT_FLAGS(len); + + if (flags == UDF_EXT_REDIRECT) + num_extents++; + + slot++; + } + + DPRINTF(ALLOC, ("udf_count_alloc_ext counted %d live extents\n", + num_extents)); + + /* XXX choice: we could delay freeing them on node writeout */ + /* free excess entries */ + extnr = num_extents; + for (;extnr < udf_node->num_extensions; extnr++) { + DPRINTF(ALLOC, ("freeing alloc ext %d\n", extnr)); + /* free dscriptor */ + s_ad = udf_node->ext_loc[extnr]; + udf_free_logvol_dscr(udf_node->ump, &s_ad, + udf_node->ext[extnr]); + udf_node->ext[extnr] = NULL; + + /* free disc space */ + lb_num = udf_rw32(s_ad.loc.lb_num); + vpart_num = udf_rw16(s_ad.loc.part_num); + udf_free_allocated_space(udf_node->ump, lb_num, vpart_num, 1); + + memset(&udf_node->ext_loc[extnr], 0, sizeof(struct long_ad)); + } + + /* set our new number of allocation extents */ + udf_node->num_extensions = num_extents; +} + + +/* --------------------------------------------------------------------- */ + +/* + * Adjust the node's allocation descriptors to reflect the new mapping; do + * take note that we might glue to existing allocation descriptors. + * + * XXX Note there can only be one allocation being recorded/mount; maybe + * explicit allocation in shedule thread? + */ + +static void +udf_record_allocation_in_node(struct udf_mount *ump, struct buf *buf, + uint16_t vpart_num, uint64_t *mapping, struct long_ad *node_ad_cpy) +{ + struct vnode *vp = buf->b_vp; + struct udf_node *udf_node = VTOI(vp); + struct file_entry *fe; + struct extfile_entry *efe; + struct icb_tag *icbtag; + struct long_ad s_ad, c_ad; + uint64_t inflen, from, till; + uint64_t foffset, end_foffset, restart_foffset; + uint64_t orig_inflen, orig_lbrec, new_inflen, new_lbrec; + uint32_t max_len; + uint32_t num_lb, len, flags, lb_num; + uint32_t run_start; + uint32_t slot_offset, replace_len, replace; + int addr_type, icbflags; +// int udf_c_type = buf->b_udf_c_type; + int lb_size, run_length, eof; + int slot, cpy_slot, cpy_slots, restart_slot; + int error; + + DPRINTF(ALLOC, ("udf_record_allocation_in_node\n")); + +#if 0 + /* XXX disable sanity check for now */ + /* sanity check ... should be panic ? */ + if ((udf_c_type != UDF_C_USERDATA) && (udf_c_type != UDF_C_FIDS)) + return; +#endif + + lb_size = udf_rw32(udf_node->ump->logical_vol->lb_size); + max_len = ((UDF_EXT_MAXLEN / lb_size) * lb_size); + + /* do the job */ + UDF_LOCK_NODE(udf_node, 0); /* XXX can deadlock ? */ + udf_node_sanity_check(udf_node, &orig_inflen, &orig_lbrec); + + fe = udf_node->fe; + efe = udf_node->efe; + if (fe) { + icbtag = &fe->icbtag; + inflen = udf_rw64(fe->inf_len); + } else { + icbtag = &efe->icbtag; + inflen = udf_rw64(efe->inf_len); + } + + /* do check if `till' is not past file information length */ + from = buf->b_lblkno * lb_size; + till = MIN(inflen, from + buf->b_resid); + + num_lb = (till - from + lb_size -1) / lb_size; + + DPRINTF(ALLOC, ("record allocation from %"PRIu64" + %d\n", from, buf->b_bcount)); + + icbflags = udf_rw16(icbtag->flags); + addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK; + + if (addr_type == UDF_ICB_INTERN_ALLOC) { + /* nothing to do */ + /* XXX clean up rest of node? just in case? */ + UDF_UNLOCK_NODE(udf_node, 0); + return; + } + + slot = 0; + cpy_slot = 0; + foffset = 0; + + /* 1) copy till first overlap piece to the rewrite buffer */ + for (;;) { + udf_get_adslot(udf_node, slot, &s_ad, &eof); + if (eof) { + DPRINTF(WRITE, + ("Record allocation in node " + "failed: encountered EOF\n")); + UDF_UNLOCK_NODE(udf_node, 0); + buf->b_error = EINVAL; + return; + } + len = udf_rw32(s_ad.len); + flags = UDF_EXT_FLAGS(len); + len = UDF_EXT_LEN(len); + + if (flags == UDF_EXT_REDIRECT) { + slot++; + continue; + } + + end_foffset = foffset + len; + if (end_foffset > from) + break; /* found */ + + node_ad_cpy[cpy_slot++] = s_ad; + + DPRINTF(ALLOC, ("\t1: vp %d, lb %d, len %d, flags %d " + "-> stack\n", + udf_rw16(s_ad.loc.part_num), + udf_rw32(s_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(s_ad.len)), + UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30)); + + foffset = end_foffset; + slot++; + } + restart_slot = slot; + restart_foffset = foffset; + + /* 2) trunc overlapping slot at overlap and copy it */ + slot_offset = from - foffset; + if (slot_offset > 0) { + DPRINTF(ALLOC, ("\tslot_offset = %d, flags = %d (%d)\n", + slot_offset, flags >> 30, flags)); + + s_ad.len = udf_rw32(slot_offset | flags); + node_ad_cpy[cpy_slot++] = s_ad; + + DPRINTF(ALLOC, ("\t2: vp %d, lb %d, len %d, flags %d " + "-> stack\n", + udf_rw16(s_ad.loc.part_num), + udf_rw32(s_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(s_ad.len)), + UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30)); + } + foffset += slot_offset; + + /* 3) insert new mappings */ + memset(&s_ad, 0, sizeof(struct long_ad)); + lb_num = 0; + for (lb_num = 0; lb_num < num_lb; lb_num++) { + run_start = mapping[lb_num]; + run_length = 1; + while (lb_num < num_lb-1) { + if (mapping[lb_num+1] != mapping[lb_num]+1) + if (mapping[lb_num+1] != mapping[lb_num]) + break; + run_length++; + lb_num++; + } + /* insert slot for this mapping */ + len = run_length * lb_size; + + /* bounds checking */ + if (foffset + len > till) + len = till - foffset; + KASSERT(foffset + len <= inflen); + + s_ad.len = udf_rw32(len | UDF_EXT_ALLOCATED); + s_ad.loc.part_num = udf_rw16(vpart_num); + s_ad.loc.lb_num = udf_rw32(run_start); + + foffset += len; + + /* paranoia */ + if (len == 0) { + DPRINTF(WRITE, + ("Record allocation in node " + "failed: insert failed\n")); + UDF_UNLOCK_NODE(udf_node, 0); + buf->b_error = EINVAL; + return; + } + node_ad_cpy[cpy_slot++] = s_ad; + + DPRINTF(ALLOC, ("\t3: insert new mapping vp %d lb %d, len %d, " + "flags %d -> stack\n", + udf_rw16(s_ad.loc.part_num), udf_rw32(s_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(s_ad.len)), + UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30)); + } + + /* 4) pop replaced length */ + slot = restart_slot; + foffset = restart_foffset; + + replace_len = till - foffset; /* total amount of bytes to pop */ + slot_offset = from - foffset; /* offset in first encounted slot */ + KASSERT((slot_offset % lb_size) == 0); + + for (;;) { + udf_get_adslot(udf_node, slot, &s_ad, &eof); + if (eof) + break; + + len = udf_rw32(s_ad.len); + flags = UDF_EXT_FLAGS(len); + len = UDF_EXT_LEN(len); + lb_num = udf_rw32(s_ad.loc.lb_num); + + if (flags == UDF_EXT_REDIRECT) { + slot++; + continue; + } + + DPRINTF(ALLOC, ("\t4i: got slot %d, slot_offset %d, " + "replace_len %d, " + "vp %d, lb %d, len %d, flags %d\n", + slot, slot_offset, replace_len, + udf_rw16(s_ad.loc.part_num), + udf_rw32(s_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(s_ad.len)), + UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30)); + + /* adjust for slot offset */ + if (slot_offset) { + DPRINTF(ALLOC, ("\t4s: skipping %d\n", slot_offset)); + lb_num += slot_offset / lb_size; + len -= slot_offset; + foffset += slot_offset; + replace_len -= slot_offset; + + /* mark adjusted */ + slot_offset = 0; + } + + /* advance for (the rest of) this slot */ + replace = MIN(len, replace_len); + DPRINTF(ALLOC, ("\t4d: replacing %d\n", replace)); + + /* advance for this slot */ + if (replace) { + /* note: dont round DOWN on num_lb since we then + * forget the last partial one */ + num_lb = (replace + lb_size - 1) / lb_size; + if (flags != UDF_EXT_FREE) { + udf_free_allocated_space(ump, lb_num, + udf_rw16(s_ad.loc.part_num), num_lb); + } + lb_num += num_lb; + len -= replace; + foffset += replace; + replace_len -= replace; + } + + /* do we have a slot tail ? */ + if (len) { + KASSERT(foffset % lb_size == 0); + + /* we arrived at our point, push remainder */ + s_ad.len = udf_rw32(len | flags); + s_ad.loc.lb_num = udf_rw32(lb_num); + if (flags == UDF_EXT_FREE) + s_ad.loc.lb_num = udf_rw32(0); + node_ad_cpy[cpy_slot++] = s_ad; + foffset += len; + slot++; + + DPRINTF(ALLOC, ("\t4: vp %d, lb %d, len %d, flags %d " + "-> stack\n", + udf_rw16(s_ad.loc.part_num), + udf_rw32(s_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(s_ad.len)), + UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30)); + break; + } + + slot++; + } + + /* 5) copy remainder */ + for (;;) { + udf_get_adslot(udf_node, slot, &s_ad, &eof); + if (eof) + break; + + len = udf_rw32(s_ad.len); + flags = UDF_EXT_FLAGS(len); + len = UDF_EXT_LEN(len); + + if (flags == UDF_EXT_REDIRECT) { + slot++; + continue; + } + + node_ad_cpy[cpy_slot++] = s_ad; + + DPRINTF(ALLOC, ("\t5: insert new mapping " + "vp %d lb %d, len %d, flags %d " + "-> stack\n", + udf_rw16(s_ad.loc.part_num), + udf_rw32(s_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(s_ad.len)), + UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30)); + + slot++; + } + + /* 6) reset node descriptors */ + udf_wipe_adslots(udf_node); + + /* 7) copy back extents; merge when possible. Recounting on the fly */ + cpy_slots = cpy_slot; + + c_ad = node_ad_cpy[0]; + slot = 0; + DPRINTF(ALLOC, ("\t7s: stack -> got mapping vp %d " + "lb %d, len %d, flags %d\n", + udf_rw16(c_ad.loc.part_num), + udf_rw32(c_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(c_ad.len)), + UDF_EXT_FLAGS(udf_rw32(c_ad.len)) >> 30)); + + for (cpy_slot = 1; cpy_slot < cpy_slots; cpy_slot++) { + s_ad = node_ad_cpy[cpy_slot]; + + DPRINTF(ALLOC, ("\t7i: stack -> got mapping vp %d " + "lb %d, len %d, flags %d\n", + udf_rw16(s_ad.loc.part_num), + udf_rw32(s_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(s_ad.len)), + UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30)); + + /* see if we can merge */ + if (udf_ads_merge(max_len, lb_size, &c_ad, &s_ad)) { + /* not mergable (anymore) */ + DPRINTF(ALLOC, ("\t7: appending vp %d lb %d, " + "len %d, flags %d\n", + udf_rw16(c_ad.loc.part_num), + udf_rw32(c_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(c_ad.len)), + UDF_EXT_FLAGS(udf_rw32(c_ad.len)) >> 30)); + + error = udf_append_adslot(udf_node, &slot, &c_ad); + if (error) { + buf->b_error = error; + goto out; + } + c_ad = s_ad; + slot++; + } + } + + /* 8) push rest slot (if any) */ + if (UDF_EXT_LEN(c_ad.len) > 0) { + DPRINTF(ALLOC, ("\t8: last append vp %d lb %d, " + "len %d, flags %d\n", + udf_rw16(c_ad.loc.part_num), + udf_rw32(c_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(c_ad.len)), + UDF_EXT_FLAGS(udf_rw32(c_ad.len)) >> 30)); + + error = udf_append_adslot(udf_node, &slot, &c_ad); + if (error) { + buf->b_error = error; + goto out; + } + } + +out: + udf_count_alloc_exts(udf_node); + + /* the node's descriptors should now be sane */ + udf_node_sanity_check(udf_node, &new_inflen, &new_lbrec); + UDF_UNLOCK_NODE(udf_node, 0); + + KASSERT(orig_inflen == new_inflen); + KASSERT(new_lbrec >= orig_lbrec); + + return; +} + +/* --------------------------------------------------------------------- */ + +int +udf_grow_node(struct udf_node *udf_node, uint64_t new_size) +{ + struct vnode *vp = udf_node->vnode; + struct udf_mount *ump = udf_node->ump; + struct file_entry *fe; + struct extfile_entry *efe; + struct icb_tag *icbtag; + struct long_ad c_ad, s_ad; + uint64_t size_diff, old_size, inflen, objsize, chunk, append_len; + uint64_t foffset, end_foffset; + uint64_t orig_inflen, orig_lbrec, new_inflen, new_lbrec; + uint32_t lb_size, unit_size, dscr_size, crclen, lastblock_grow; + uint32_t icbflags, len, flags, max_len; + uint32_t max_l_ad, l_ad, l_ea; + uint16_t my_part, dst_part; + uint8_t *evacuated_data; + int addr_type; + int slot; + int eof, error; + + DPRINTF(ALLOC, ("udf_grow_node\n")); + + UDF_LOCK_NODE(udf_node, 0); + udf_node_sanity_check(udf_node, &orig_inflen, &orig_lbrec); + + lb_size = udf_rw32(ump->logical_vol->lb_size); + + /* max_len in unit's IFF its a metadata node or metadata mirror node */ + unit_size = lb_size; + if ((udf_node == ump->metadata_node) || (udf_node == ump->metadatamirror_node)) + unit_size = ump->metadata_alloc_unit_size * lb_size; + max_len = ((UDF_EXT_MAXLEN / unit_size) * unit_size); + + fe = udf_node->fe; + efe = udf_node->efe; + if (fe) { + icbtag = &fe->icbtag; + inflen = udf_rw64(fe->inf_len); + objsize = inflen; + dscr_size = sizeof(struct file_entry) -1; + l_ea = udf_rw32(fe->l_ea); + l_ad = udf_rw32(fe->l_ad); + } else { + icbtag = &efe->icbtag; + inflen = udf_rw64(efe->inf_len); + objsize = udf_rw64(efe->obj_size); + dscr_size = sizeof(struct extfile_entry) -1; + l_ea = udf_rw32(efe->l_ea); + l_ad = udf_rw32(efe->l_ad); + } + max_l_ad = lb_size - dscr_size - l_ea; + + icbflags = udf_rw16(icbtag->flags); + addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK; + + old_size = inflen; + size_diff = new_size - old_size; + + DPRINTF(ALLOC, ("\tfrom %"PRIu64" to %"PRIu64"\n", old_size, new_size)); + + evacuated_data = NULL; + if (addr_type == UDF_ICB_INTERN_ALLOC) { + if (l_ad + size_diff <= max_l_ad) { + /* only reflect size change directly in the node */ + inflen += size_diff; + objsize += size_diff; + l_ad += size_diff; + crclen = dscr_size - UDF_DESC_TAG_LENGTH + l_ea + l_ad; + if (fe) { + fe->inf_len = udf_rw64(inflen); + fe->l_ad = udf_rw32(l_ad); + fe->tag.desc_crc_len = udf_rw16(crclen); + } else { + efe->inf_len = udf_rw64(inflen); + efe->obj_size = udf_rw64(objsize); + efe->l_ad = udf_rw32(l_ad); + efe->tag.desc_crc_len = udf_rw16(crclen); + } + error = 0; + + /* set new size for uvm */ + uvm_vnp_setwritesize(vp, new_size); + uvm_vnp_setsize(vp, new_size); + +#if 0 + /* zero append space in buffer */ + ubc_zerorange(&vp->v_uobj, old_size, + new_size - old_size, UBC_UNMAP_FLAG(vp)); +#endif + + udf_node_sanity_check(udf_node, &new_inflen, &new_lbrec); + + /* unlock */ + UDF_UNLOCK_NODE(udf_node, 0); + + KASSERT(new_inflen == orig_inflen + size_diff); + KASSERT(new_lbrec == orig_lbrec); + KASSERT(new_lbrec == 0); + return 0; + } + + DPRINTF(ALLOC, ("\tCONVERT from internal\n")); + + if (old_size > 0) { + /* allocate some space and copy in the stuff to keep */ + evacuated_data = malloc(lb_size, M_UDFTEMP, M_WAITOK); + memset(evacuated_data, 0, lb_size); + + /* node is locked, so safe to exit mutex */ + UDF_UNLOCK_NODE(udf_node, 0); + + /* read in using the `normal' vn_rdwr() */ + error = vn_rdwr(UIO_READ, udf_node->vnode, + evacuated_data, old_size, 0, + UIO_SYSSPACE, IO_ALTSEMANTICS | IO_NODELOCKED, + FSCRED, NULL, NULL); + + /* enter again */ + UDF_LOCK_NODE(udf_node, 0); + } + + /* convert to a normal alloc and select type */ + my_part = udf_rw16(udf_node->loc.loc.part_num); + dst_part = udf_get_record_vpart(ump, udf_get_c_type(udf_node)); + addr_type = UDF_ICB_SHORT_ALLOC; + if (dst_part != my_part) + addr_type = UDF_ICB_LONG_ALLOC; + + icbflags &= ~UDF_ICB_TAG_FLAGS_ALLOC_MASK; + icbflags |= addr_type; + icbtag->flags = udf_rw16(icbflags); + + /* wipe old descriptor space */ + udf_wipe_adslots(udf_node); + + memset(&c_ad, 0, sizeof(struct long_ad)); + c_ad.len = udf_rw32(old_size | UDF_EXT_FREE); + c_ad.loc.part_num = udf_rw16(0); /* not relevant */ + c_ad.loc.lb_num = udf_rw32(0); /* not relevant */ + + slot = 0; + } else { + /* goto the last entry (if any) */ + slot = 0; + foffset = 0; + memset(&c_ad, 0, sizeof(struct long_ad)); + for (;;) { + udf_get_adslot(udf_node, slot, &c_ad, &eof); + if (eof) + break; + + len = udf_rw32(c_ad.len); + flags = UDF_EXT_FLAGS(len); + len = UDF_EXT_LEN(len); + + end_foffset = foffset + len; + if (flags != UDF_EXT_REDIRECT) + foffset = end_foffset; + + slot++; + } + /* at end of adslots */ + + /* special case if the old size was zero, then there is no last slot */ + if (old_size == 0) { + c_ad.len = udf_rw32(0 | UDF_EXT_FREE); + c_ad.loc.part_num = udf_rw16(0); /* not relevant */ + c_ad.loc.lb_num = udf_rw32(0); /* not relevant */ + } else { + /* refetch last slot */ + slot--; + udf_get_adslot(udf_node, slot, &c_ad, &eof); + } + } + + /* + * If the length of the last slot is not a multiple of lb_size, adjust + * length so that it is; don't forget to adjust `append_len'! relevant for + * extending existing files + */ + len = udf_rw32(c_ad.len); + flags = UDF_EXT_FLAGS(len); + len = UDF_EXT_LEN(len); + + lastblock_grow = 0; + if (len % lb_size > 0) { + lastblock_grow = lb_size - (len % lb_size); + lastblock_grow = MIN(size_diff, lastblock_grow); + len += lastblock_grow; + c_ad.len = udf_rw32(len | flags); + + /* TODO zero appened space in buffer! */ + /* using ubc_zerorange(&vp->v_uobj, old_size, */ + /* new_size - old_size, UBC_UNMAP_FLAG(vp)); ? */ + } + memset(&s_ad, 0, sizeof(struct long_ad)); + + /* size_diff can be bigger than allowed, so grow in chunks */ + append_len = size_diff - lastblock_grow; + while (append_len > 0) { + chunk = MIN(append_len, max_len); + s_ad.len = udf_rw32(chunk | UDF_EXT_FREE); + s_ad.loc.part_num = udf_rw16(0); + s_ad.loc.lb_num = udf_rw32(0); + + if (udf_ads_merge(max_len, lb_size, &c_ad, &s_ad)) { + /* not mergable (anymore) */ + error = udf_append_adslot(udf_node, &slot, &c_ad); + if (error) + goto errorout; + slot++; + c_ad = s_ad; + memset(&s_ad, 0, sizeof(struct long_ad)); + } + append_len -= chunk; + } + + /* if there is a rest piece in the accumulator, append it */ + if (UDF_EXT_LEN(udf_rw32(c_ad.len)) > 0) { + error = udf_append_adslot(udf_node, &slot, &c_ad); + if (error) + goto errorout; + slot++; + } + + /* if there is a rest piece that didn't fit, append it */ + if (UDF_EXT_LEN(udf_rw32(s_ad.len)) > 0) { + error = udf_append_adslot(udf_node, &slot, &s_ad); + if (error) + goto errorout; + slot++; + } + + inflen += size_diff; + objsize += size_diff; + if (fe) { + fe->inf_len = udf_rw64(inflen); + } else { + efe->inf_len = udf_rw64(inflen); + efe->obj_size = udf_rw64(objsize); + } + error = 0; + + if (evacuated_data) { + /* set new write size for uvm */ + uvm_vnp_setwritesize(vp, old_size); + + /* write out evacuated data */ + error = vn_rdwr(UIO_WRITE, udf_node->vnode, + evacuated_data, old_size, 0, + UIO_SYSSPACE, IO_ALTSEMANTICS | IO_NODELOCKED, + FSCRED, NULL, NULL); + uvm_vnp_setsize(vp, old_size); + } + +errorout: + if (evacuated_data) + free(evacuated_data, M_UDFTEMP); + + udf_count_alloc_exts(udf_node); + + udf_node_sanity_check(udf_node, &new_inflen, &new_lbrec); + UDF_UNLOCK_NODE(udf_node, 0); + + KASSERT(new_inflen == orig_inflen + size_diff); + KASSERT(new_lbrec == orig_lbrec); + + return error; +} + +/* --------------------------------------------------------------------- */ + +int +udf_shrink_node(struct udf_node *udf_node, uint64_t new_size) +{ + struct vnode *vp = udf_node->vnode; + struct udf_mount *ump = udf_node->ump; + struct file_entry *fe; + struct extfile_entry *efe; + struct icb_tag *icbtag; + struct long_ad c_ad, s_ad, *node_ad_cpy; + uint64_t size_diff, old_size, inflen, objsize; + uint64_t foffset, end_foffset; + uint64_t orig_inflen, orig_lbrec, new_inflen, new_lbrec; + uint32_t lb_size, unit_size, dscr_size, crclen; + uint32_t slot_offset, slot_offset_lb; + uint32_t len, flags, max_len; + uint32_t num_lb, lb_num; + uint32_t max_l_ad, l_ad, l_ea; + uint16_t vpart_num; + uint8_t *data_pos; + int icbflags, addr_type; + int slot, cpy_slot, cpy_slots; + int eof, error; + + DPRINTF(ALLOC, ("udf_shrink_node\n")); + + UDF_LOCK_NODE(udf_node, 0); + udf_node_sanity_check(udf_node, &orig_inflen, &orig_lbrec); + + lb_size = udf_rw32(ump->logical_vol->lb_size); + + /* max_len in unit's IFF its a metadata node or metadata mirror node */ + unit_size = lb_size; + if ((udf_node == ump->metadata_node) || (udf_node == ump->metadatamirror_node)) + unit_size = ump->metadata_alloc_unit_size * lb_size; + max_len = ((UDF_EXT_MAXLEN / unit_size) * unit_size); + + /* do the work */ + fe = udf_node->fe; + efe = udf_node->efe; + if (fe) { + icbtag = &fe->icbtag; + inflen = udf_rw64(fe->inf_len); + objsize = inflen; + dscr_size = sizeof(struct file_entry) -1; + l_ea = udf_rw32(fe->l_ea); + l_ad = udf_rw32(fe->l_ad); + data_pos = (uint8_t *) fe + dscr_size + l_ea; + } else { + icbtag = &efe->icbtag; + inflen = udf_rw64(efe->inf_len); + objsize = udf_rw64(efe->obj_size); + dscr_size = sizeof(struct extfile_entry) -1; + l_ea = udf_rw32(efe->l_ea); + l_ad = udf_rw32(efe->l_ad); + data_pos = (uint8_t *) efe + dscr_size + l_ea; + } + max_l_ad = lb_size - dscr_size - l_ea; + + icbflags = udf_rw16(icbtag->flags); + addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK; + + old_size = inflen; + size_diff = old_size - new_size; + + DPRINTF(ALLOC, ("\tfrom %"PRIu64" to %"PRIu64"\n", old_size, new_size)); + + /* shrink the node to its new size */ + if (addr_type == UDF_ICB_INTERN_ALLOC) { + /* only reflect size change directly in the node */ + KASSERT(new_size <= max_l_ad); + inflen -= size_diff; + objsize -= size_diff; + l_ad -= size_diff; + crclen = dscr_size - UDF_DESC_TAG_LENGTH + l_ea + l_ad; + if (fe) { + fe->inf_len = udf_rw64(inflen); + fe->l_ad = udf_rw32(l_ad); + fe->tag.desc_crc_len = udf_rw16(crclen); + } else { + efe->inf_len = udf_rw64(inflen); + efe->obj_size = udf_rw64(objsize); + efe->l_ad = udf_rw32(l_ad); + efe->tag.desc_crc_len = udf_rw16(crclen); + } + error = 0; + + /* clear the space in the descriptor */ + KASSERT(old_size > new_size); + memset(data_pos + new_size, 0, old_size - new_size); + + /* TODO zero appened space in buffer! */ + /* using ubc_zerorange(&vp->v_uobj, old_size, */ + /* old_size - new_size, UBC_UNMAP_FLAG(vp)); ? */ + + /* set new size for uvm */ + uvm_vnp_setsize(vp, new_size); + + udf_node_sanity_check(udf_node, &new_inflen, &new_lbrec); + UDF_UNLOCK_NODE(udf_node, 0); + + KASSERT(new_inflen == orig_inflen - size_diff); + KASSERT(new_lbrec == orig_lbrec); + KASSERT(new_lbrec == 0); + + return 0; + } + + /* setup node cleanup extents copy space */ + node_ad_cpy = malloc(lb_size * UDF_MAX_ALLOC_EXTENTS, + M_UDFMNT, M_WAITOK); + memset(node_ad_cpy, 0, lb_size * UDF_MAX_ALLOC_EXTENTS); + + /* + * Shrink the node by releasing the allocations and truncate the last + * allocation to the new size. If the new size fits into the + * allocation descriptor itself, transform it into an + * UDF_ICB_INTERN_ALLOC. + */ + slot = 0; + cpy_slot = 0; + foffset = 0; + + /* 1) copy till first overlap piece to the rewrite buffer */ + for (;;) { + udf_get_adslot(udf_node, slot, &s_ad, &eof); + if (eof) { + DPRINTF(WRITE, + ("Shrink node failed: " + "encountered EOF\n")); + error = EINVAL; + goto errorout; /* panic? */ + } + len = udf_rw32(s_ad.len); + flags = UDF_EXT_FLAGS(len); + len = UDF_EXT_LEN(len); + + if (flags == UDF_EXT_REDIRECT) { + slot++; + continue; + } + + end_foffset = foffset + len; + if (end_foffset > new_size) + break; /* found */ + + node_ad_cpy[cpy_slot++] = s_ad; + + DPRINTF(ALLOC, ("\t1: vp %d, lb %d, len %d, flags %d " + "-> stack\n", + udf_rw16(s_ad.loc.part_num), + udf_rw32(s_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(s_ad.len)), + UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30)); + + foffset = end_foffset; + slot++; + } + slot_offset = new_size - foffset; + + /* 2) trunc overlapping slot at overlap and copy it */ + if (slot_offset > 0) { + lb_num = udf_rw32(s_ad.loc.lb_num); + vpart_num = udf_rw16(s_ad.loc.part_num); + + if (flags == UDF_EXT_ALLOCATED) { + /* calculate extent in lb, and offset in lb */ + num_lb = (len + lb_size -1) / lb_size; + slot_offset_lb = (slot_offset + lb_size -1) / lb_size; + + /* adjust our slot */ + lb_num += slot_offset_lb; + num_lb -= slot_offset_lb; + + udf_free_allocated_space(ump, lb_num, vpart_num, num_lb); + } + + s_ad.len = udf_rw32(slot_offset | flags); + node_ad_cpy[cpy_slot++] = s_ad; + slot++; + + DPRINTF(ALLOC, ("\t2: vp %d, lb %d, len %d, flags %d " + "-> stack\n", + udf_rw16(s_ad.loc.part_num), + udf_rw32(s_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(s_ad.len)), + UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30)); + } + + /* 3) delete remainder */ + for (;;) { + udf_get_adslot(udf_node, slot, &s_ad, &eof); + if (eof) + break; + + len = udf_rw32(s_ad.len); + flags = UDF_EXT_FLAGS(len); + len = UDF_EXT_LEN(len); + + if (flags == UDF_EXT_REDIRECT) { + slot++; + continue; + } + + DPRINTF(ALLOC, ("\t3: delete remainder " + "vp %d lb %d, len %d, flags %d\n", + udf_rw16(s_ad.loc.part_num), + udf_rw32(s_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(s_ad.len)), + UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30)); + + if (flags == UDF_EXT_ALLOCATED) { + lb_num = udf_rw32(s_ad.loc.lb_num); + vpart_num = udf_rw16(s_ad.loc.part_num); + num_lb = (len + lb_size - 1) / lb_size; + + udf_free_allocated_space(ump, lb_num, vpart_num, + num_lb); + } + + slot++; + } + + /* 4) if it will fit into the descriptor then convert */ + if (new_size < max_l_ad) { + /* + * resque/evacuate old piece by reading it in, and convert it + * to internal alloc. + */ + if (new_size == 0) { + /* XXX/TODO only for zero sizing now */ + udf_wipe_adslots(udf_node); + + icbflags &= ~UDF_ICB_TAG_FLAGS_ALLOC_MASK; + icbflags |= UDF_ICB_INTERN_ALLOC; + icbtag->flags = udf_rw16(icbflags); + + inflen -= size_diff; KASSERT(inflen == 0); + objsize -= size_diff; + l_ad = new_size; + crclen = dscr_size - UDF_DESC_TAG_LENGTH + l_ea + l_ad; + if (fe) { + fe->inf_len = udf_rw64(inflen); + fe->l_ad = udf_rw32(l_ad); + fe->tag.desc_crc_len = udf_rw16(crclen); + } else { + efe->inf_len = udf_rw64(inflen); + efe->obj_size = udf_rw64(objsize); + efe->l_ad = udf_rw32(l_ad); + efe->tag.desc_crc_len = udf_rw16(crclen); + } + /* eventually copy in evacuated piece */ + /* set new size for uvm */ + uvm_vnp_setsize(vp, new_size); + + free(node_ad_cpy, M_UDFMNT); + udf_node_sanity_check(udf_node, &new_inflen, &new_lbrec); + + UDF_UNLOCK_NODE(udf_node, 0); + + KASSERT(new_inflen == orig_inflen - size_diff); + KASSERT(new_inflen == 0); + KASSERT(new_lbrec == 0); + + return 0; + } + + printf("UDF_SHRINK_NODE: could convert to internal alloc!\n"); + } + + /* 5) reset node descriptors */ + udf_wipe_adslots(udf_node); + + /* 6) copy back extents; merge when possible. Recounting on the fly */ + cpy_slots = cpy_slot; + + c_ad = node_ad_cpy[0]; + slot = 0; + for (cpy_slot = 1; cpy_slot < cpy_slots; cpy_slot++) { + s_ad = node_ad_cpy[cpy_slot]; + + DPRINTF(ALLOC, ("\t6: stack -> got mapping vp %d " + "lb %d, len %d, flags %d\n", + udf_rw16(s_ad.loc.part_num), + udf_rw32(s_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(s_ad.len)), + UDF_EXT_FLAGS(udf_rw32(s_ad.len)) >> 30)); + + /* see if we can merge */ + if (udf_ads_merge(max_len, lb_size, &c_ad, &s_ad)) { + /* not mergable (anymore) */ + DPRINTF(ALLOC, ("\t6: appending vp %d lb %d, " + "len %d, flags %d\n", + udf_rw16(c_ad.loc.part_num), + udf_rw32(c_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(c_ad.len)), + UDF_EXT_FLAGS(udf_rw32(c_ad.len)) >> 30)); + + error = udf_append_adslot(udf_node, &slot, &c_ad); + if (error) + goto errorout; /* panic? */ + c_ad = s_ad; + slot++; + } + } + + /* 7) push rest slot (if any) */ + if (UDF_EXT_LEN(c_ad.len) > 0) { + DPRINTF(ALLOC, ("\t7: last append vp %d lb %d, " + "len %d, flags %d\n", + udf_rw16(c_ad.loc.part_num), + udf_rw32(c_ad.loc.lb_num), + UDF_EXT_LEN(udf_rw32(c_ad.len)), + UDF_EXT_FLAGS(udf_rw32(c_ad.len)) >> 30)); + + error = udf_append_adslot(udf_node, &slot, &c_ad); + if (error) + goto errorout; /* panic? */ + ; + } + + inflen -= size_diff; + objsize -= size_diff; + if (fe) { + fe->inf_len = udf_rw64(inflen); + } else { + efe->inf_len = udf_rw64(inflen); + efe->obj_size = udf_rw64(objsize); + } + error = 0; + + /* set new size for uvm */ + uvm_vnp_setsize(vp, new_size); + +errorout: + free(node_ad_cpy, M_UDFMNT); + + udf_count_alloc_exts(udf_node); + + udf_node_sanity_check(udf_node, &new_inflen, &new_lbrec); + UDF_UNLOCK_NODE(udf_node, 0); + + KASSERT(new_inflen == orig_inflen - size_diff); + + return error; +} + diff --git a/sys/fs/udf/udf_bswap.h b/sys/fs/udf/udf_bswap.h new file mode 100644 index 000000000..9a481cfcc --- /dev/null +++ b/sys/fs/udf/udf_bswap.h @@ -0,0 +1,76 @@ +/* $NetBSD: udf_bswap.h,v 1.8 2009/10/22 21:50:01 bouyer Exp $ */ + +/* + * Copyright (c) 1998 Manuel Bouyer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * adapted for UDF by Reinoud Zandijk + * + */ + +#ifndef _FS_UDF_UDF_BSWAP_H_ +#define _FS_UDF_UDF_BSWAP_H_ + +#include +#include +#include + +/* rest only relevant for big endian machines */ +#if (BYTE_ORDER == BIG_ENDIAN) + +/* inlines for access to swapped data */ +static __inline uint16_t udf_rw16(uint16_t); +static __inline uint32_t udf_rw32(uint32_t); +static __inline uint64_t udf_rw64(uint64_t); + + +static __inline uint16_t +udf_rw16(uint16_t a) +{ + return bswap16(a); +} + + +static __inline uint32_t +udf_rw32(uint32_t a) +{ + return bswap32(a); +} + + +static __inline uint64_t +udf_rw64(uint64_t a) +{ + return bswap64(a); +} + +#else + +#define udf_rw16(a) ((uint16_t)(a)) +#define udf_rw32(a) ((uint32_t)(a)) +#define udf_rw64(a) ((uint64_t)(a)) + +#endif + + +#endif /* !_FS_UDF_UDF_BSWAP_H_ */ + diff --git a/sys/fs/udf/udf_mount.h b/sys/fs/udf/udf_mount.h new file mode 100644 index 000000000..bf02be7dd --- /dev/null +++ b/sys/fs/udf/udf_mount.h @@ -0,0 +1,70 @@ +/* $NetBSD: udf_mount.h,v 1.3 2006/02/02 15:52:23 reinoud Exp $ */ + +/* + * Copyright (c) 2006 Reinoud Zandijk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the + * NetBSD Project. See http://www.NetBSD.org/ for + * information about NetBSD. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#ifndef _FS_UDF_UDF_MOUNT_H_ +#define _FS_UDF_UDF_MOUNT_H_ + +/* + * Arguments to mount UDF filingsystem. + */ + +#define UDFMNT_VERSION 1 +struct udf_args { + uint32_t version; /* version of this structure */ + char *fspec; /* mount specifier */ + int32_t sessionnr; /* session specifier, rel of abs */ + uint32_t udfmflags; /* mount options */ + int32_t gmtoff; /* offset from UTC in seconds */ + + uid_t anon_uid; /* mapping of anonymous files uid */ + gid_t anon_gid; /* mapping of anonymous files gid */ + uid_t nobody_uid; /* nobody:nobody will map to -1:-1 */ + gid_t nobody_gid; /* nobody:nobody will map to -1:-1 */ + + uint32_t sector_size; /* for mounting dumps/files */ + + /* extendable */ + uint8_t reserved[32]; +}; + + +/* udf mount options */ + +#define UDFMNT_CLOSESESSION 0x00000001 /* close session on dismount */ +#define UDFMNT_BITS "\20\1CLOSESESSION" + +#endif /* !_FS_UDF_UDF_MOUNT_H_ */ + diff --git a/sys/fs/udf/udf_osta.c b/sys/fs/udf/udf_osta.c new file mode 100644 index 000000000..3b1a28293 --- /dev/null +++ b/sys/fs/udf/udf_osta.c @@ -0,0 +1,514 @@ +/* $NetBSD: udf_osta.c,v 1.10 2013/08/05 17:02:54 joerg Exp $ */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: udf_osta.c,v 1.10 2013/08/05 17:02:54 joerg Exp $"); + +/* + * Various routines from the OSTA 2.01 specs. Copyrights are included with + * each code segment. Slight whitespace modifications have been made for + * formatting purposes. Typos/bugs have been fixed. + * + */ + +#include "udf_osta.h" + +#ifndef _KERNEL +#include +#endif + +/*****************************************************************************/ +/*********************************************************************** + * OSTA compliant Unicode compression, uncompression routines. + * Copyright 1995 Micro Design International, Inc. + * Written by Jason M. Rinn. + * Micro Design International gives permission for the free use of the + * following source code. + */ + +/*********************************************************************** + * Takes an OSTA CS0 compressed unicode name, and converts + * it to Unicode. + * The Unicode output will be in the byte order + * that the local compiler uses for 16-bit values. + * NOTE: This routine only performs error checking on the compID. + * It is up to the user to ensure that the unicode buffer is large + * enough, and that the compressed unicode name is correct. + * + * RETURN VALUE + * + * The number of unicode characters which were uncompressed. + * A -1 is returned if the compression ID is invalid. + */ +int +udf_UncompressUnicode( + int numberOfBytes, /* (Input) number of bytes read from media. */ + byte *UDFCompressed, /* (Input) bytes read from media. */ + unicode_t *unicode) /* (Output) uncompressed unicode characters. */ +{ + unsigned int compID; + int returnValue, unicodeIndex, byteIndex; + + /* Use UDFCompressed to store current byte being read. */ + compID = UDFCompressed[0]; + + /* First check for valid compID. */ + if (compID != 8 && compID != 16) { + returnValue = -1; + } else { + unicodeIndex = 0; + byteIndex = 1; + + /* Loop through all the bytes. */ + while (byteIndex < numberOfBytes) { + if (compID == 16) { + /* Move the first byte to the high bits of the + * unicode char. + */ + unicode[unicodeIndex] = + UDFCompressed[byteIndex++] << 8; + } else { + unicode[unicodeIndex] = 0; + } + if (byteIndex < numberOfBytes) { + /*Then the next byte to the low bits. */ + unicode[unicodeIndex] |= + UDFCompressed[byteIndex++]; + } + unicodeIndex++; + } + returnValue = unicodeIndex; + } + return(returnValue); +} + +/*********************************************************************** + * DESCRIPTION: + * Takes a string of unicode wide characters and returns an OSTA CS0 + * compressed unicode string. The unicode MUST be in the byte order of + * the compiler in order to obtain correct results. Returns an error + * if the compression ID is invalid. + * + * NOTE: This routine assumes the implementation already knows, by + * the local environment, how many bits are appropriate and + * therefore does no checking to test if the input characters fit + * into that number of bits or not. + * + * RETURN VALUE + * + * The total number of bytes in the compressed OSTA CS0 string, + * including the compression ID. + * A -1 is returned if the compression ID is invalid. + */ +int +udf_CompressUnicode( + int numberOfChars, /* (Input) number of unicode characters. */ + int compID, /* (Input) compression ID to be used. */ + unicode_t *unicode, /* (Input) unicode characters to compress. */ + byte *UDFCompressed) /* (Output) compressed string, as bytes. */ +{ + int byteIndex, unicodeIndex; + + if (compID != 8 && compID != 16) { + byteIndex = -1; /* Unsupported compression ID ! */ + } else { + /* Place compression code in first byte. */ + UDFCompressed[0] = compID; + + byteIndex = 1; + unicodeIndex = 0; + while (unicodeIndex < numberOfChars) { + if (compID == 16) { + /* First, place the high bits of the char + * into the byte stream. + */ + UDFCompressed[byteIndex++] = + (unicode[unicodeIndex] & 0xFF00) >> 8; + } + /*Then place the low bits into the stream. */ + UDFCompressed[byteIndex++] = + unicode[unicodeIndex] & 0x00FF; + unicodeIndex++; + } + } + return(byteIndex); +} + +/*****************************************************************************/ +/* + * CRC 010041 + */ +static unsigned short crc_table[256] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, + 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, + 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, + 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, + 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, + 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, + 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, + 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, + 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, + 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, + 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, + 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, + 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, + 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, + 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, + 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, + 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, + 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, + 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, + 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 +}; + +unsigned short +udf_cksum(unsigned char *s, int n) +{ + unsigned short crc=0; + + while (n-- > 0) + crc = crc_table[(crc>>8 ^ *s++) & 0xff] ^ (crc<<8); + return crc; +} + +/* UNICODE Checksum */ +unsigned short +udf_unicode_cksum(unsigned short *s, int n) +{ + unsigned short crc=0; + + while (n-- > 0) { + /* Take high order byte first--corresponds to a big endian + * byte stream. + */ + crc = crc_table[(crc>>8 ^ (*s>>8)) & 0xff] ^ (crc<<8); + crc = crc_table[(crc>>8 ^ (*s++ & 0xff)) & 0xff] ^ (crc<<8); + } + return crc; +} + + +/* + * Calculates a 16-bit checksum of the Implementation Use + * Extended Attribute header or Application Use Extended Attribute + * header. The fields AttributeType through ImplementationIdentifier + * (or ApplicationIdentifier) inclusively represent the + * data covered by the checksum (48 bytes). + * + */ +uint16_t udf_ea_cksum(uint8_t *data) { + uint16_t checksum = 0; + int count; + + for (count = 0; count < 48; count++) { + checksum += *data++; + } + + return checksum; +} + + +#ifdef MAIN +unsigned char bytes[] = { 0x70, 0x6A, 0x77 }; + +main(void) +{ + unsigned short x; + x = cksum(bytes, sizeof bytes); + printf("checksum: calculated=%4.4x, correct=%4.4x\en", x, 0x3299); + exit(0); +} +#endif + +/*****************************************************************************/ +/* #ifdef NEEDS_ISPRINT */ +/*********************************************************************** + * OSTA UDF compliant file name translation routine for OS/2, + * Windows 95, Windows NT, Macintosh and UNIX. + * Copyright 1995 Micro Design International, Inc. + * Written by Jason M. Rinn. + * Micro Design International gives permission for the free use of the + * following source code. + */ + +/*********************************************************************** + * To use these routines with different operating systems. + * + * OS/2 + * Define OS2 + * Define MAXLEN = 254 + * + * Windows 95 + * Define WIN_95 + * Define MAXLEN = 255 + * + * Windows NT + * Define WIN_NT + * Define MAXLEN = 255 + * + * Macintosh: + * Define MAC. + * Define MAXLEN = 31. + * + * UNIX + * Define UNIX. + * Define MAXLEN as specified by unix version. + */ + +#define ILLEGAL_CHAR_MARK 0x005F +#define CRC_MARK 0x0023 +#define EXT_SIZE 5 +#define PERIOD 0x002E +#define SPACE 0x0020 + +/*** PROTOTYPES ***/ +int IsIllegal(unicode_t ch); + +/* Define a function or macro which determines if a Unicode character is + * printable under your implementation. + */ + + +/* #include */ +static int UnicodeIsPrint(unicode_t ch) { + return (ch >=' ') && (ch != 127); +} + + +int UnicodeLength(unicode_t *string) { + int length; + length = 0; + while (*string++) length++; + + return length; +} + + +#ifdef _KERNEL +static int isprint(int c) { + return (c >= ' ') && (c != 127); +} +#endif + + +/*********************************************************************** + * Translates a long file name to one using a MAXLEN and an illegal + * char set in accord with the OSTA requirements. Assumes the name has + * already been translated to Unicode. + * + * RETURN VALUE + * + * Number of unicode characters in translated name. + */ +int UDFTransName( + unicode_t *newName, /* (Output)Translated name. Must be of length + * MAXLEN */ + unicode_t *udfName, /* (Input) Name from UDF volume.*/ + int udfLen) /* (Input) Length of UDF Name. */ +{ + int Index, newIndex = 0, needsCRC = false; /* index is shadowed */ + int extIndex = 0, newExtIndex = 0, hasExt = false; +#if defined OS2 || defined WIN_95 || defined WIN_NT + int trailIndex = 0; +#endif + unsigned short valueCRC; + unicode_t current; + const char hexChar[] = "0123456789ABCDEF"; + + for (Index = 0; Index < udfLen; Index++) { + current = udfName[Index]; + + if (IsIllegal(current) || !UnicodeIsPrint(current)) { + needsCRC = true; + /* Replace Illegal and non-displayable chars with + * underscore. + */ + current = ILLEGAL_CHAR_MARK; + /* Skip any other illegal or non-displayable + * characters. + */ + while(Index+1 < udfLen && (IsIllegal(udfName[Index+1]) + || !UnicodeIsPrint(udfName[Index+1]))) { + Index++; + } + } + + /* Record position of extension, if one is found. */ + if (current == PERIOD && (udfLen - Index -1) <= EXT_SIZE) { + if (udfLen == Index + 1) { + /* A trailing period is NOT an extension. */ + hasExt = false; + } else { + hasExt = true; + extIndex = Index; + newExtIndex = newIndex; + } + } + +#if defined OS2 || defined WIN_95 || defined WIN_NT + /* Record position of last char which is NOT period or space. */ + else if (current != PERIOD && current != SPACE) { + trailIndex = newIndex; + } +#endif + + if (newIndex < MAXLEN) { + newName[newIndex++] = current; + } else { + needsCRC = true; + } + } + +#if defined OS2 || defined WIN_95 || defined WIN_NT + /* For OS2, 95 & NT, truncate any trailing periods and\or spaces. */ + if (trailIndex != newIndex - 1) { + newIndex = trailIndex + 1; + needsCRC = true; + hasExt = false; /* Trailing period does not make an + * extension. */ + } +#endif + + if (needsCRC) { + unicode_t ext[EXT_SIZE]; + int localExtIndex = 0; + if (hasExt) { + int maxFilenameLen; + /* Translate extension, and store it in ext. */ + for(Index = 0; Index maxFilenameLen) { + newIndex = maxFilenameLen; + } else { + newIndex = newExtIndex; + } + } else if (newIndex > MAXLEN - 5) { + /*If no extension, make sure to leave room for CRC. */ + newIndex = MAXLEN - 5; + } + newName[newIndex++] = CRC_MARK; /* Add mark for CRC. */ + + /*Calculate CRC from original filename from FileIdentifier. */ + valueCRC = udf_unicode_cksum(udfName, udfLen); + /* Convert 16-bits of CRC to hex characters. */ + newName[newIndex++] = hexChar[(valueCRC & 0xf000) >> 12]; + newName[newIndex++] = hexChar[(valueCRC & 0x0f00) >> 8]; + newName[newIndex++] = hexChar[(valueCRC & 0x00f0) >> 4]; + newName[newIndex++] = hexChar[(valueCRC & 0x000f)]; + + /* Place a translated extension at end, if found. */ + if (hasExt) { + newName[newIndex++] = PERIOD; + for (Index = 0;Index < localExtIndex ;Index++ ) { + newName[newIndex++] = ext[Index]; + } + } + } + return(newIndex); +} + +#if defined OS2 || defined WIN_95 || defined WIN_NT +/*********************************************************************** + * Decides if a Unicode character matches one of a list + * of ASCII characters. + * Used by OS2 version of IsIllegal for readability, since all of the + * illegal characters above 0x0020 are in the ASCII subset of Unicode. + * Works very similarly to the standard C function strchr(). + * + * RETURN VALUE + * + * Non-zero if the Unicode character is in the given ASCII string. + */ +int UnicodeInString( + unsigned char *string, /* (Input) String to search through. */ + unicode_t ch) /* (Input) Unicode char to search for. */ +{ + int found = false; + while (*string != '\0' && found == false) { + /* These types should compare, since both are unsigned + * numbers. */ + if (*string == ch) { + found = true; + } + string++; + } + return(found); +} +#endif /* OS2 */ + +/*********************************************************************** + * Decides whether the given character is illegal for a given OS. + * + * RETURN VALUE + * + * Non-zero if char is illegal. + */ +int IsIllegal(unicode_t ch) +{ +#ifdef MAC + /* Only illegal character on the MAC is the colon. */ + if (ch == 0x003A) { + return(1); + } else { + return(0); + } + +#elif defined UNIX + /* Illegal UNIX characters are NULL and slash. */ + if (ch == 0x0000 || ch == 0x002F) { + return(1); + } else { + return(0); + } + +#elif defined OS2 || defined WIN_95 || defined WIN_NT + /* Illegal char's for OS/2 according to WARP toolkit. */ + if (ch < 0x0020 || UnicodeInString("\\/:*?\"<>|", ch)) { + return(1); + } else { + return(0); + } +#endif +} +/* #endif*/ /* NEEDS_ISPRINT */ + diff --git a/sys/fs/udf/udf_osta.h b/sys/fs/udf/udf_osta.h new file mode 100644 index 000000000..4d166b74f --- /dev/null +++ b/sys/fs/udf/udf_osta.h @@ -0,0 +1,44 @@ +/* $NetBSD: udf_osta.h,v 1.4 2008/05/14 16:49:48 reinoud Exp $ */ + +/* + * Prototypes for the OSTA functions + */ + + +#ifndef _FS_UDF_OSTA_H_ +#define _FS_UDF_OSTA_H_ + +#include + +#ifndef _KERNEL +#include +#endif + +#ifndef UNIX +#define UNIX +#endif + +#ifndef MAXLEN +#define MAXLEN 255 +#endif + + +/*********************************************************************** + * The following two typedef's are to remove compiler dependancies. + * byte needs to be unsigned 8-bit, and unicode_t needs to be + * unsigned 16-bit. + */ +typedef uint16_t unicode_t; +typedef uint8_t byte; + + +int udf_UncompressUnicode(int, byte *, unicode_t *); +int udf_CompressUnicode(int, int, unicode_t *, byte *); +unsigned short udf_cksum(unsigned char *, int); +unsigned short udf_unicode_cksum(unsigned short *, int); +uint16_t udf_ea_cksum(uint8_t *data); +int UDFTransName(unicode_t *, unicode_t *, int); +int UnicodeLength(unicode_t *string); + + +#endif /* _FS_UDF_OSTA_H_ */ diff --git a/sys/fs/udf/udf_readwrite.c b/sys/fs/udf/udf_readwrite.c new file mode 100644 index 000000000..57c4ee2d0 --- /dev/null +++ b/sys/fs/udf/udf_readwrite.c @@ -0,0 +1,723 @@ +/* $NetBSD: udf_readwrite.c,v 1.11 2011/06/12 03:35:55 rmind Exp $ */ + +/* + * Copyright (c) 2007, 2008 Reinoud Zandijk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#ifndef lint +__KERNEL_RCSID(0, "$NetBSD: udf_readwrite.c,v 1.11 2011/06/12 03:35:55 rmind Exp $"); +#endif /* not lint */ + + +#if defined(_KERNEL_OPT) +#include "opt_compat_netbsd.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "udf.h" +#include "udf_subr.h" +#include "udf_bswap.h" + + +#define VTOI(vnode) ((struct udf_node *) vnode->v_data) + +/* --------------------------------------------------------------------- */ + +void +udf_fixup_fid_block(uint8_t *blob, int lb_size, + int rfix_pos, int max_rfix_pos, uint32_t lb_num) +{ + struct fileid_desc *fid; + uint8_t *fid_pos; + int fid_len, found; + + /* needs to be word aligned */ + KASSERT(rfix_pos % 4 == 0); + + /* first resync with the FID stream !!! */ + found = 0; + while (rfix_pos + sizeof(struct desc_tag) <= max_rfix_pos) { + fid_pos = blob + rfix_pos; + fid = (struct fileid_desc *) fid_pos; + if (udf_rw16(fid->tag.id) == TAGID_FID) { + if (udf_check_tag((union dscrptr *) fid) == 0) + found = 1; + } + if (found) + break; + /* try next location; can only be 4 bytes aligned */ + rfix_pos += 4; + } + + /* walk over the fids */ + fid_pos = blob + rfix_pos; + while (rfix_pos + sizeof(struct desc_tag) <= max_rfix_pos) { + fid = (struct fileid_desc *) fid_pos; + if (udf_rw16(fid->tag.id) != TAGID_FID) { + /* end of FID stream; end of directory or currupted */ + break; + } + + /* update sector number and recalculate checkum */ + fid->tag.tag_loc = udf_rw32(lb_num); + udf_validate_tag_sum((union dscrptr *) fid); + + /* if the FID crosses the memory, we're done! */ + if (rfix_pos + UDF_FID_SIZE >= max_rfix_pos) + break; + + fid_len = udf_fidsize(fid); + fid_pos += fid_len; + rfix_pos += fid_len; + } +} + + +void +udf_fixup_internal_extattr(uint8_t *blob, uint32_t lb_num) +{ + struct desc_tag *tag; + struct file_entry *fe; + struct extfile_entry *efe; + struct extattrhdr_desc *eahdr; + int l_ea; + + /* get information from fe/efe */ + tag = (struct desc_tag *) blob; + switch (udf_rw16(tag->id)) { + case TAGID_FENTRY : + fe = (struct file_entry *) blob; + l_ea = udf_rw32(fe->l_ea); + eahdr = (struct extattrhdr_desc *) fe->data; + break; + case TAGID_EXTFENTRY : + efe = (struct extfile_entry *) blob; + l_ea = udf_rw32(efe->l_ea); + eahdr = (struct extattrhdr_desc *) efe->data; + break; + case TAGID_INDIRECTENTRY : + case TAGID_ALLOCEXTENT : + case TAGID_EXTATTR_HDR : + return; + default: + panic("%s: passed bad tag\n", __func__); + } + + /* something recorded here? (why am i called?) */ + if (l_ea == 0) + return; + +#if 0 + /* check extended attribute tag */ + /* TODO XXX what to do when we encounter an error here? */ + error = udf_check_tag(eahdr); + if (error) + return; /* for now */ + if (udf_rw16(eahdr->tag.id) != TAGID_EXTATTR_HDR) + return; /* for now */ + error = udf_check_tag_payload(eahdr, sizeof(struct extattrhdr_desc)); + if (error) + return; /* for now */ +#endif + + DPRINTF(EXTATTR, ("node fixup: found %d bytes of extended attributes\n", + l_ea)); + + /* fixup eahdr tag */ + eahdr->tag.tag_loc = udf_rw32(lb_num); + udf_validate_tag_and_crc_sums((union dscrptr *) eahdr); +} + + +void +udf_fixup_node_internals(struct udf_mount *ump, uint8_t *blob, int udf_c_type) +{ + struct desc_tag *tag, *sbm_tag; + struct file_entry *fe; + struct extfile_entry *efe; + struct alloc_ext_entry *ext; + uint32_t lb_size, lb_num; + uint32_t intern_pos, max_intern_pos; + int icbflags, addr_type, file_type, intern, has_fids, has_sbm, l_ea; + + lb_size = udf_rw32(ump->logical_vol->lb_size); + /* if its not a node we're done */ + if (udf_c_type != UDF_C_NODE) + return; + + /* NOTE this could also be done in write_internal */ + /* start of a descriptor */ + l_ea = 0; + has_fids = 0; + has_sbm = 0; + intern = 0; + file_type = 0; + max_intern_pos = intern_pos = lb_num = 0; /* shut up gcc! */ + + tag = (struct desc_tag *) blob; + switch (udf_rw16(tag->id)) { + case TAGID_FENTRY : + fe = (struct file_entry *) tag; + l_ea = udf_rw32(fe->l_ea); + icbflags = udf_rw16(fe->icbtag.flags); + addr_type = (icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK); + file_type = fe->icbtag.file_type; + intern = (addr_type == UDF_ICB_INTERN_ALLOC); + intern_pos = UDF_FENTRY_SIZE + l_ea; + max_intern_pos = intern_pos + udf_rw64(fe->inf_len); + lb_num = udf_rw32(fe->tag.tag_loc); + break; + case TAGID_EXTFENTRY : + efe = (struct extfile_entry *) tag; + l_ea = udf_rw32(efe->l_ea); + icbflags = udf_rw16(efe->icbtag.flags); + addr_type = (icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK); + file_type = efe->icbtag.file_type; + intern = (addr_type == UDF_ICB_INTERN_ALLOC); + intern_pos = UDF_EXTFENTRY_SIZE + l_ea; + max_intern_pos = intern_pos + udf_rw64(efe->inf_len); + lb_num = udf_rw32(efe->tag.tag_loc); + break; + case TAGID_INDIRECTENTRY : + case TAGID_EXTATTR_HDR : + break; + case TAGID_ALLOCEXTENT : + /* force crclen to 8 for UDF version < 2.01 */ + ext = (struct alloc_ext_entry *) tag; + if (udf_rw16(ump->logvol_info->min_udf_readver) <= 0x200) + ext->tag.desc_crc_len = udf_rw16(8); + break; + default: + panic("%s: passed bad tag\n", __func__); + break; + } + + /* determine what to fix if its internally recorded */ + if (intern) { + has_fids = (file_type == UDF_ICB_FILETYPE_DIRECTORY) || + (file_type == UDF_ICB_FILETYPE_STREAMDIR); + has_sbm = (file_type == UDF_ICB_FILETYPE_META_BITMAP); + } + + /* fixup internal extended attributes if present */ + if (l_ea) + udf_fixup_internal_extattr(blob, lb_num); + + /* fixup fids lb numbers */ + if (has_fids) + udf_fixup_fid_block(blob, lb_size, intern_pos, + max_intern_pos, lb_num); + + /* fixup space bitmap descriptor */ + if (has_sbm) { + sbm_tag = (struct desc_tag *) (blob + intern_pos); + sbm_tag->tag_loc = tag->tag_loc; + udf_validate_tag_and_crc_sums((uint8_t *) sbm_tag); + } + + udf_validate_tag_and_crc_sums(blob); +} + +/* --------------------------------------------------------------------- */ + +/* + * Set of generic descriptor readers and writers and their helper functions. + * Descriptors inside `logical space' i.e. inside logically mapped partitions + * can never be longer than one logical sector. + * + * NOTE that these functions *can* be used by the sheduler backends to read + * node descriptors too. + * + * For reading, the size of allocated piece is returned in multiple of sector + * size due to udf_calc_udf_malloc_size(). + */ + + +/* SYNC reading of n blocks from specified sector */ +int +udf_read_phys_sectors(struct udf_mount *ump, int what, void *blob, + uint32_t start, uint32_t sectors) +{ + struct buf *buf, *nestbuf; + uint32_t buf_offset; + off_t lblkno, rblkno; + int sector_size = ump->discinfo.sector_size; + int blks = sector_size / DEV_BSIZE; + int piece; + int error; + + DPRINTF(READ, ("udf_intbreadn() : sectors = %d, sector_size = %d\n", + sectors, sector_size)); + buf = getiobuf(ump->devvp, true); + buf->b_flags = B_READ; + buf->b_cflags = BC_BUSY; /* needed? */ + buf->b_iodone = NULL; + buf->b_data = blob; + buf->b_bcount = sectors * sector_size; + buf->b_resid = buf->b_bcount; + buf->b_bufsize = buf->b_bcount; + buf->b_private = NULL; /* not needed yet */ + BIO_SETPRIO(buf, BPRIO_DEFAULT); + buf->b_lblkno = buf->b_blkno = buf->b_rawblkno = start * blks; + buf->b_proc = NULL; + + error = 0; + buf_offset = 0; + rblkno = start; + lblkno = 0; + while ((sectors > 0) && (error == 0)) { + piece = MIN(MAXPHYS/sector_size, sectors); + DPRINTF(READ, ("read in %d + %d\n", (uint32_t) rblkno, piece)); + + nestbuf = getiobuf(NULL, true); + nestiobuf_setup(buf, nestbuf, buf_offset, piece * sector_size); + /* nestbuf is B_ASYNC */ + + /* identify this nestbuf */ + nestbuf->b_lblkno = lblkno; + + /* CD shedules on raw blkno */ + nestbuf->b_blkno = rblkno * blks; + nestbuf->b_proc = NULL; + nestbuf->b_rawblkno = rblkno * blks; + nestbuf->b_udf_c_type = what; + + udf_discstrat_queuebuf(ump, nestbuf); + + lblkno += piece; + rblkno += piece; + buf_offset += piece * sector_size; + sectors -= piece; + } + error = biowait(buf); + putiobuf(buf); + + return error; +} + + +/* synchronous generic descriptor read */ +int +udf_read_phys_dscr(struct udf_mount *ump, uint32_t sector, + struct malloc_type *mtype, union dscrptr **dstp) +{ + union dscrptr *dst, *new_dst; + uint8_t *pos; + int sectors, dscrlen; + int i, error, sector_size; + + sector_size = ump->discinfo.sector_size; + + *dstp = dst = NULL; + dscrlen = sector_size; + + /* read initial piece */ + dst = malloc(sector_size, mtype, M_WAITOK); + error = udf_read_phys_sectors(ump, UDF_C_DSCR, dst, sector, 1); + DPRINTFIF(DESCRIPTOR, error, ("read error (%d)\n", error)); + + if (!error) { + /* check if its a valid tag */ + error = udf_check_tag(dst); + if (error) { + /* check if its an empty block */ + pos = (uint8_t *) dst; + for (i = 0; i < sector_size; i++, pos++) { + if (*pos) break; + } + if (i == sector_size) { + /* return no error but with no dscrptr */ + /* dispose first block */ + free(dst, mtype); + return 0; + } + } + /* calculate descriptor size */ + dscrlen = udf_tagsize(dst, sector_size); + } + DPRINTFIF(DESCRIPTOR, error, ("bad tag checksum\n")); + + if (!error && (dscrlen > sector_size)) { + DPRINTF(DESCRIPTOR, ("multi block descriptor read\n")); + /* + * Read the rest of descriptor. Since it is only used at mount + * time its overdone to define and use a specific udf_intbreadn + * for this alone. + */ + + new_dst = realloc(dst, dscrlen, mtype, M_WAITOK); + if (new_dst == NULL) { + free(dst, mtype); + return ENOMEM; + } + dst = new_dst; + + sectors = (dscrlen + sector_size -1) / sector_size; + DPRINTF(DESCRIPTOR, ("dscrlen = %d (%d blk)\n", dscrlen, sectors)); + + pos = (uint8_t *) dst + sector_size; + error = udf_read_phys_sectors(ump, UDF_C_DSCR, pos, + sector + 1, sectors-1); + + DPRINTFIF(DESCRIPTOR, error, ("read error on multi (%d)\n", + error)); + } + if (!error) { + error = udf_check_tag_payload(dst, dscrlen); + DPRINTFIF(DESCRIPTOR, error, ("bad payload check sum\n")); + } + if (error && dst) { + free(dst, mtype); + dst = NULL; + } + *dstp = dst; + + return error; +} + + +static void +udf_write_phys_buf(struct udf_mount *ump, int what, struct buf *buf) +{ + struct buf *nestbuf; + uint32_t buf_offset; + off_t lblkno, rblkno; + int sector_size = ump->discinfo.sector_size; + int blks = sector_size / DEV_BSIZE; + uint32_t sectors; + int piece; + int error; + + sectors = buf->b_bcount / sector_size; + DPRINTF(WRITE, ("udf_intbwriten() : sectors = %d, sector_size = %d\n", + sectors, sector_size)); + + /* don't forget to increase pending count for the bwrite itself */ +/* panic("NO WRITING\n"); */ + if (buf->b_vp) { + mutex_enter(buf->b_vp->v_interlock); + buf->b_vp->v_numoutput++; + mutex_exit(buf->b_vp->v_interlock); + } + + error = 0; + buf_offset = 0; + rblkno = buf->b_blkno / blks; + lblkno = 0; + while ((sectors > 0) && (error == 0)) { + piece = MIN(MAXPHYS/sector_size, sectors); + DPRINTF(WRITE, ("write out %d + %d\n", + (uint32_t) rblkno, piece)); + + nestbuf = getiobuf(NULL, true); + nestiobuf_setup(buf, nestbuf, buf_offset, piece * sector_size); + /* nestbuf is B_ASYNC */ + + /* identify this nestbuf */ + nestbuf->b_lblkno = lblkno; + + /* CD shedules on raw blkno */ + nestbuf->b_blkno = rblkno * blks; + nestbuf->b_proc = NULL; + nestbuf->b_rawblkno = rblkno * blks; + nestbuf->b_udf_c_type = what; + + udf_discstrat_queuebuf(ump, nestbuf); + + lblkno += piece; + rblkno += piece; + buf_offset += piece * sector_size; + sectors -= piece; + } +} + + +/* SYNC writing of n blocks from specified sector */ +int +udf_write_phys_sectors(struct udf_mount *ump, int what, void *blob, + uint32_t start, uint32_t sectors) +{ + struct vnode *vp; + struct buf *buf; + int sector_size = ump->discinfo.sector_size; + int blks = sector_size / DEV_BSIZE; + int error; + + /* get transfer buffer */ + vp = ump->devvp; + buf = getiobuf(vp, true); + buf->b_flags = B_WRITE; + buf->b_cflags = BC_BUSY; /* needed? */ + buf->b_iodone = NULL; + buf->b_data = blob; + buf->b_bcount = sectors * sector_size; + buf->b_resid = buf->b_bcount; + buf->b_bufsize = buf->b_bcount; + buf->b_private = NULL; /* not needed yet */ + BIO_SETPRIO(buf, BPRIO_DEFAULT); + buf->b_lblkno = buf->b_blkno = buf->b_rawblkno = start * blks; + buf->b_proc = NULL; + + /* do the write, wait and return error */ + udf_write_phys_buf(ump, what, buf); + error = biowait(buf); + putiobuf(buf); + + return error; +} + + +/* synchronous generic descriptor write */ +int +udf_write_phys_dscr_sync(struct udf_mount *ump, struct udf_node *udf_node, int what, + union dscrptr *dscr, uint32_t sector, uint32_t logsector) +{ + struct vnode *vp; + struct buf *buf; + int sector_size = ump->discinfo.sector_size; + int blks = sector_size / DEV_BSIZE; + int dscrlen; + int error; + + /* set sector number in the descriptor and validate */ + dscr->tag.tag_loc = udf_rw32(logsector); + udf_validate_tag_and_crc_sums(dscr); + + /* calculate descriptor size */ + dscrlen = udf_tagsize(dscr, sector_size); + + /* get transfer buffer */ + vp = udf_node ? udf_node->vnode : ump->devvp; + buf = getiobuf(vp, true); + buf->b_flags = B_WRITE; + buf->b_cflags = BC_BUSY; /* needed? */ + buf->b_iodone = NULL; + buf->b_data = (void *) dscr; + buf->b_bcount = dscrlen; + buf->b_resid = buf->b_bcount; + buf->b_bufsize = buf->b_bcount; + buf->b_private = NULL; /* not needed yet */ + BIO_SETPRIO(buf, BPRIO_DEFAULT); + buf->b_lblkno = buf->b_blkno = buf->b_rawblkno = sector * blks; + buf->b_proc = NULL; + + /* do the write, wait and return error */ + udf_write_phys_buf(ump, what, buf); + error = biowait(buf); + putiobuf(buf); + + return error; +} + + +/* asynchronous generic descriptor write */ +int +udf_write_phys_dscr_async(struct udf_mount *ump, struct udf_node *udf_node, + int what, union dscrptr *dscr, + uint32_t sector, uint32_t logsector, + void (*dscrwr_callback)(struct buf *)) +{ + struct vnode *vp; + struct buf *buf; + int dscrlen; + int sector_size = ump->discinfo.sector_size; + int blks = sector_size / DEV_BSIZE; + + KASSERT(dscrwr_callback); + DPRINTF(NODE, ("udf_write_phys_dscr_async() called\n")); + + /* set sector number in the descriptor and validate */ + dscr->tag.tag_loc = udf_rw32(logsector); + udf_validate_tag_and_crc_sums(dscr); + + /* calculate descriptor size */ + dscrlen = udf_tagsize(dscr, sector_size); + + /* get transfer buffer */ + vp = udf_node ? udf_node->vnode : ump->devvp; + buf = getiobuf(vp, true); + buf->b_flags = B_WRITE; // | B_ASYNC; + buf->b_cflags = BC_BUSY; + buf->b_iodone = dscrwr_callback; + buf->b_data = dscr; + buf->b_bcount = dscrlen; + buf->b_resid = buf->b_bcount; + buf->b_bufsize = buf->b_bcount; + buf->b_private = NULL; /* not needed yet */ + BIO_SETPRIO(buf, BPRIO_DEFAULT); + buf->b_lblkno = buf->b_blkno = buf->b_rawblkno = sector * blks; + buf->b_proc = NULL; + + /* do the write and return no error */ + udf_write_phys_buf(ump, what, buf); + return 0; +} + +/* --------------------------------------------------------------------- */ + +/* disc strategy dispatchers */ + +int +udf_create_logvol_dscr(struct udf_mount *ump, struct udf_node *udf_node, struct long_ad *icb, + union dscrptr **dscrptr) +{ + struct udf_strategy *strategy = ump->strategy; + struct udf_strat_args args; + int error; + + KASSERT(strategy); + args.ump = ump; + args.udf_node = udf_node; + args.icb = icb; + args.dscr = NULL; + + error = (strategy->create_logvol_dscr)(&args); + *dscrptr = args.dscr; + + return error; +} + + +void +udf_free_logvol_dscr(struct udf_mount *ump, struct long_ad *icb, + void *dscr) +{ + struct udf_strategy *strategy = ump->strategy; + struct udf_strat_args args; + + KASSERT(strategy); + args.ump = ump; + args.icb = icb; + args.dscr = dscr; + + (strategy->free_logvol_dscr)(&args); +} + + +int +udf_read_logvol_dscr(struct udf_mount *ump, struct long_ad *icb, + union dscrptr **dscrptr) +{ + struct udf_strategy *strategy = ump->strategy; + struct udf_strat_args args; + int error; + + KASSERT(strategy); + args.ump = ump; + args.icb = icb; + args.dscr = NULL; + + error = (strategy->read_logvol_dscr)(&args); + *dscrptr = args.dscr; + + return error; +} + + +int +udf_write_logvol_dscr(struct udf_node *udf_node, union dscrptr *dscr, + struct long_ad *icb, int waitfor) +{ + struct udf_strategy *strategy = udf_node->ump->strategy; + struct udf_strat_args args; + int error; + + KASSERT(strategy); + args.ump = udf_node->ump; + args.udf_node = udf_node; + args.icb = icb; + args.dscr = dscr; + args.waitfor = waitfor; + + error = (strategy->write_logvol_dscr)(&args); + return error; +} + + +void +udf_discstrat_queuebuf(struct udf_mount *ump, struct buf *nestbuf) +{ + struct udf_strategy *strategy = ump->strategy; + struct udf_strat_args args; + + KASSERT(strategy); + args.ump = ump; + args.nestbuf = nestbuf; + + (strategy->queuebuf)(&args); +} + + +void +udf_discstrat_init(struct udf_mount *ump) +{ + struct udf_strategy *strategy = ump->strategy; + struct udf_strat_args args; + + KASSERT(strategy); + args.ump = ump; + (strategy->discstrat_init)(&args); +} + + +void udf_discstrat_finish(struct udf_mount *ump) +{ + struct udf_strategy *strategy = ump->strategy; + struct udf_strat_args args; + + /* strategy might not have been set, so ignore if not set */ + if (strategy) { + args.ump = ump; + (strategy->discstrat_finish)(&args); + } +} + +/* --------------------------------------------------------------------- */ + diff --git a/sys/fs/udf/udf_rename.c b/sys/fs/udf/udf_rename.c new file mode 100644 index 000000000..1aa6bb246 --- /dev/null +++ b/sys/fs/udf/udf_rename.c @@ -0,0 +1,669 @@ +/* $NetBSD: udf_rename.c,v 1.10 2013/07/16 10:49:36 reinoud Exp $ */ + +/* + * Copyright (c) 2013 Reinoud Zandijk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Comments and trivial code from the reference implementation in tmpfs. + */ + +#include +__KERNEL_RCSID(0, "$NetBSD: udf_rename.c,v 1.10 2013/07/16 10:49:36 reinoud Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "udf.h" +#include "udf_subr.h" +#include "udf_bswap.h" + + +/* forwards */ +static int udf_sane_rename( struct vnode *, struct componentname *, + struct vnode *, struct componentname *, + kauth_cred_t, bool); +static bool udf_rmdired_p(struct vnode *); +static int udf_gro_lock_directory(struct mount *, struct vnode *); + +static const struct genfs_rename_ops udf_genfs_rename_ops; + + +#define VTOI(vnode) ((struct udf_node *) (vnode)->v_data) + + +/* + * udf_sane_rename: The hairiest vop, with the saner API. + * + * Arguments: + * + * . fdvp (from directory vnode), + * . fcnp (from component name), + * . tdvp (to directory vnode), + * . tcnp (to component name), + * . cred (credentials structure), and + * . posixly_correct (flag for behaviour if target & source link same file). + * + * fdvp and tdvp may be the same, and must be referenced and unlocked. + */ +static int +udf_sane_rename( struct vnode *fdvp, struct componentname *fcnp, + struct vnode *tdvp, struct componentname *tcnp, + kauth_cred_t cred, bool posixly_correct) +{ + DPRINTF(CALL, ("udf_sane_rename '%s' -> '%s'\n", + fcnp->cn_nameptr, tcnp->cn_nameptr)); + return genfs_sane_rename(&udf_genfs_rename_ops, + fdvp, fcnp, NULL, tdvp, tcnp, NULL, + cred, posixly_correct); +} + + +/* + * udf_rename: the hairiest vop, with the insanest API. Pass to + * genfs_insane_rename immediately. + */ +int +udf_rename(void *v) +{ + struct vop_rename_args /* { + struct vnode *a_fdvp; + struct vnode *a_fvp; + struct componentname *a_fcnp; + struct vnode *a_tdvp; + struct vnode *a_tvp; + struct componentname *a_tcnp; + } */ *ap = v; + DPRINTF(CALL, ("udf_rename called\n")); + return genfs_insane_rename(ap, &udf_sane_rename); +} + + +/* + * udf_gro_directory_empty_p: return true if the directory vp is empty. dvp is + * its parent. + * + * vp and dvp must be locked and referenced. + */ +static bool +udf_gro_directory_empty_p(struct mount *mp, kauth_cred_t cred, + struct vnode *vp, struct vnode *dvp) +{ + struct udf_node *udf_node = VTOI(vp); + int error, isempty; + + KASSERT(mp != NULL); + KASSERT(vp != NULL); + KASSERT(dvp != NULL); + KASSERT(vp != dvp); + KASSERT(vp->v_mount == mp); + KASSERT(dvp->v_mount == mp); + KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); + KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); + + DPRINTF(CALL, ("udf_gro_directory_empty_p called\n")); + /* make sure our `leaf' node's hash is populated */ + dirhash_get(&udf_node->dir_hash); + error = udf_dirhash_fill(udf_node); + if (error) { + dirhash_put(udf_node->dir_hash); + /* VERY unlikely, answer its not empty */ + return 0; + } + + /* check to see if the directory is empty */ + isempty = dirhash_dir_isempty(udf_node->dir_hash); + dirhash_put(udf_node->dir_hash); + + return isempty; +} + + +/* + * udf_gro_rename_check_possible: check whether a rename is possible + * independent of credentials. + */ +static int +udf_gro_rename_check_possible(struct mount *mp, + struct vnode *fdvp, struct vnode *fvp, + struct vnode *tdvp, struct vnode *tvp) +{ + (void)mp; + KASSERT(mp != NULL); + KASSERT(fdvp != NULL); + KASSERT(fvp != NULL); + KASSERT(tdvp != NULL); + KASSERT(fdvp != fvp); + KASSERT(fdvp != tvp); + KASSERT(tdvp != fvp); + KASSERT(tdvp != tvp); + KASSERT(fvp != tvp); + KASSERT(fdvp->v_type == VDIR); + KASSERT(tdvp->v_type == VDIR); + KASSERT(fdvp->v_mount == mp); + KASSERT(fvp->v_mount == mp); + KASSERT(tdvp->v_mount == mp); + KASSERT((tvp == NULL) || (tvp->v_mount == mp)); + KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE); + KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE); + KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE); + KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE)); + + DPRINTF(CALL, ("udf_gro_rename_check_possible called\n")); + + /* flags not implemented since they are not defined (yet) in UDF */ + return 0; +} + + +/* + * udf_gro_rename_check_permitted: check whether a rename is permitted given + * our credentials. + */ +static int +udf_gro_rename_check_permitted(struct mount *mp, kauth_cred_t cred, + struct vnode *fdvp, struct vnode *fvp, + struct vnode *tdvp, struct vnode *tvp) +{ + struct udf_node *fdir_node = VTOI(fdvp); + struct udf_node *tdir_node = VTOI(tdvp); + struct udf_node *f_node = VTOI(fvp); + struct udf_node *t_node = (tvp? VTOI(tvp): NULL); + mode_t fdmode, tdmode; + uid_t fduid, tduid, fuid, tuid; + gid_t gdummy; + + (void)mp; + KASSERT(mp != NULL); + KASSERT(fdvp != NULL); + KASSERT(fvp != NULL); + KASSERT(tdvp != NULL); + KASSERT(fdvp != fvp); + KASSERT(fdvp != tvp); + KASSERT(tdvp != fvp); + KASSERT(tdvp != tvp); + KASSERT(fvp != tvp); + KASSERT(fdvp->v_type == VDIR); + KASSERT(tdvp->v_type == VDIR); + KASSERT(fdvp->v_mount == mp); + KASSERT(fvp->v_mount == mp); + KASSERT(tdvp->v_mount == mp); + KASSERT((tvp == NULL) || (tvp->v_mount == mp)); + KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE); + KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE); + KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE); + KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE)); + + DPRINTF(CALL, ("udf_gro_rename_check_permitted called\n")); + fdmode = udf_getaccessmode(fdir_node); + tdmode = udf_getaccessmode(tdir_node); + + udf_getownership(fdir_node, &fduid, &gdummy); + udf_getownership(tdir_node, &tduid, &gdummy); + udf_getownership(f_node, &fuid, &gdummy); + + tuid = 0; + if (t_node) + udf_getownership(t_node, &tuid, &gdummy); + + return genfs_ufslike_rename_check_permitted(cred, + fdvp, fdmode, fduid, + fvp, fuid, + tdvp, tdmode, tduid, + tvp, tuid); +} + + +/* + * udf_gro_remove_check_possible: check whether a remove is possible + * independent of credentials. + * + * XXX could check for special attributes? + */ +static int +udf_gro_remove_check_possible(struct mount *mp, + struct vnode *dvp, struct vnode *vp) +{ + (void)mp; + KASSERT(mp != NULL); + KASSERT(dvp != NULL); + KASSERT(vp != NULL); + KASSERT(dvp != vp); + KASSERT(dvp->v_type == VDIR); + KASSERT(vp->v_type != VDIR); + KASSERT(dvp->v_mount == mp); + KASSERT(vp->v_mount == mp); + KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); + KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); + + DPRINTF(CALL, ("udf_gro_remove_check_possible called\n")); + + /* flags not implemented since they are not defined (yet) in UDF */ + return 0; +} + + +/* + * udf_gro_remove_check_permitted: check whether a remove is permitted given + * our credentials. + */ +static int +udf_gro_remove_check_permitted(struct mount *mp, kauth_cred_t cred, + struct vnode *dvp, struct vnode *vp) +{ + struct udf_node *dir_node = VTOI(dvp); + struct udf_node *udf_node = VTOI(vp); + mode_t dmode; + uid_t duid, uid; + gid_t gdummy; + + (void)mp; + KASSERT(mp != NULL); + KASSERT(dvp != NULL); + KASSERT(vp != NULL); + KASSERT(dvp != vp); + KASSERT(dvp->v_type == VDIR); + KASSERT(vp->v_type != VDIR); + KASSERT(dvp->v_mount == mp); + KASSERT(vp->v_mount == mp); + KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); + KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); + + DPRINTF(CALL, ("udf_gro_remove_check_permitted called\n")); + dmode = udf_getaccessmode(dir_node); + + udf_getownership(dir_node, &duid, &gdummy); + udf_getownership(udf_node, &uid, &gdummy); + + return genfs_ufslike_remove_check_permitted(cred, + dvp, dmode, duid, + vp, uid); +} + + +/* + * udf_gro_rename: actually perform the rename operation. + */ +static int +udf_gro_rename(struct mount *mp, kauth_cred_t cred, + struct vnode *fdvp, struct componentname *fcnp, + void *fde, struct vnode *fvp, + struct vnode *tdvp, struct componentname *tcnp, + void *tde, struct vnode *tvp) +{ + struct udf_node *fnode, *fdnode, *tnode, *tdnode; + struct vattr fvap; + int error; + + (void)cred; + KASSERT(mp != NULL); + KASSERT(fdvp != NULL); + KASSERT(fcnp != NULL); + KASSERT(fvp != NULL); + KASSERT(tdvp != NULL); + KASSERT(tcnp != NULL); + KASSERT(fdvp != fvp); + KASSERT(fdvp != tvp); + KASSERT(tdvp != fvp); + KASSERT(tdvp != tvp); + KASSERT(fvp != tvp); + KASSERT(fdvp->v_mount == mp); + KASSERT(fvp->v_mount == mp); + KASSERT(tdvp->v_mount == mp); + KASSERT((tvp == NULL) || (tvp->v_mount == mp)); + KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE); + KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE); + KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE); + KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE)); + + DPRINTF(CALL, ("udf_gro_rename called\n")); + DPRINTF(NODE, ("udf_gro_rename called : %s -> %s\n", + fcnp->cn_nameptr, tcnp->cn_nameptr)); + + fnode = VTOI(fvp); + fdnode = VTOI(fdvp); + tnode = (tvp == NULL) ? NULL : VTOI(tvp); + tdnode = VTOI(tdvp); + + /* get attribute information */ + error = VOP_GETATTR(fvp, &fvap, NULL); + if (error) + return error; + + /* remove existing entry if present */ + if (tvp) + udf_dir_detach(tdnode->ump, tdnode, tnode, tcnp); + + /* create new directory entry for the node */ + error = udf_dir_attach(tdnode->ump, tdnode, fnode, &fvap, tcnp); + if (error) + return error; + + /* unlink old directory entry for the node, if failing, unattach new */ + error = udf_dir_detach(tdnode->ump, fdnode, fnode, fcnp); + if (error) + goto rollback_attach; + + if ((fdnode != tdnode) && (fvp->v_type == VDIR)) { + /* update fnode's '..' entry */ + error = udf_dir_update_rootentry(fnode->ump, fnode, tdnode); + if (error) + goto rollback; + } + + VN_KNOTE(fvp, NOTE_RENAME); + genfs_rename_cache_purge(fdvp, fvp, tdvp, tvp); + return 0; + +rollback: + /* 'try' to recover from this situation */ + udf_dir_attach(tdnode->ump, fdnode, fnode, &fvap, fcnp); +rollback_attach: + udf_dir_detach(tdnode->ump, tdnode, fnode, tcnp); + + return error; +} + + +/* + * udf_gro_remove: rename an object over another link to itself, effectively + * removing just the original link. + */ +static int +udf_gro_remove(struct mount *mp, kauth_cred_t cred, + struct vnode *dvp, struct componentname *cnp, void *de, struct vnode *vp) +{ + struct udf_node *dir_node, *udf_node; + + KASSERT(mp != NULL); + KASSERT(dvp != NULL); + KASSERT(cnp != NULL); + KASSERT(vp != NULL); + KASSERT(dvp != vp); + KASSERT(dvp->v_mount == mp); + KASSERT(vp->v_mount == mp); + KASSERT(dvp->v_type == VDIR); + KASSERT(vp->v_type != VDIR); + KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); + KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); + + DPRINTF(CALL, ("udf_gro_remove called\n")); + + dir_node = VTOI(dvp); + udf_node = VTOI(vp); + udf_dir_detach(dir_node->ump, dir_node, udf_node, cnp); + + return 0; +} + + +/* + * udf_gro_lookup: look up and save the lookup results. + */ +static int +udf_gro_lookup(struct mount *mp, struct vnode *dvp, + struct componentname *cnp, void *de_ret, struct vnode **vp_ret) +{ + struct udf_node *dir_node, *res_node; + struct long_ad icb_loc; + const char *name; + int namelen, error; + int found; + + (void)mp; + KASSERT(mp != NULL); + KASSERT(dvp != NULL); + KASSERT(cnp != NULL); + KASSERT(vp_ret != NULL); + KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); + + dir_node = VTOI(dvp); + + DPRINTF(CALL, ("udf_gro_lookup called\n")); + + /* lookup filename in the directory; location icb_loc */ + name = cnp->cn_nameptr; + namelen = cnp->cn_namelen; + error = udf_lookup_name_in_dir(dvp, name, namelen, + &icb_loc, &found); + if (error) + return error; + if (!found) + return ENOENT; + + DPRINTF(LOOKUP, ("udf_gro_lookup found '%s'\n", name)); + error = udf_get_node(dir_node->ump, &icb_loc, &res_node); + if (error) + return error; + *vp_ret = res_node->vnode; + VOP_UNLOCK(res_node->vnode); + + return 0; +} + + +/* + * udf_rmdired_p: check whether the directory vp has been rmdired. + * + * vp must be locked and referenced. + */ +static bool +udf_rmdired_p(struct vnode *vp) +{ + DPRINTF(CALL, ("udf_rmdired_p called\n")); + + KASSERT(vp != NULL); + KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); + KASSERT(vp->v_type == VDIR); + + return (VTOI(vp)->i_flags & IN_DELETED); +} + + +/* + * udf_gro_genealogy: analyze the genealogy of the source and target + * directories. + */ +static int +udf_gro_genealogy(struct mount *mp, kauth_cred_t cred, + struct vnode *fdvp, struct vnode *tdvp, + struct vnode **intermediate_node_ret) +{ + struct udf_mount *ump; + struct udf_node *parent_node; + struct vnode *vp, *dvp; + struct long_ad parent_loc; + const char *name; + int namelen; + int error, found; + + (void)cred; + KASSERT(mp != NULL); + KASSERT(fdvp != NULL); + KASSERT(tdvp != NULL); + KASSERT(fdvp != tdvp); + KASSERT(intermediate_node_ret != NULL); + KASSERT(fdvp->v_mount == mp); + KASSERT(tdvp->v_mount == mp); + KASSERT(fdvp->v_type == VDIR); + KASSERT(tdvp->v_type == VDIR); + + DPRINTF(CALL, ("udf_gro_genealogy called\n")); + + /* + * We need to provisionally lock tdvp to keep rmdir from deleting it + * -- or any ancestor -- at an inopportune moment. + * + * XXX WHY is this not in genfs's rename? XXX + */ + error = udf_gro_lock_directory(mp, tdvp); + if (error) + return error; + + name = ".."; + namelen = 2; + error = 0; + + ump = VTOI(tdvp)->ump; + + /* if nodes are equal, it is no use looking */ + KASSERT(udf_compare_icb(&VTOI(fdvp)->loc, &VTOI(tdvp)->loc) != 0); + + /* start at destination vnode and walk up the tree */ + vp = tdvp; + vref(vp); + + for (;;) { + KASSERT(vp != NULL); + KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); + KASSERT(vp->v_mount == mp); + KASSERT(vp->v_type == VDIR); + KASSERT(!udf_rmdired_p(vp)); + + DPRINTF(NODE, ("udf_gro_genealogy : " + "fdvp %p, looking at vp %p\n", + fdvp, vp)); + + /* sanity check */ + if (vp->v_type != VDIR) { + vput(vp); + return ENOTDIR; + } + + /* go down one level */ + error = udf_lookup_name_in_dir(vp, name, namelen, + &parent_loc, &found); + DPRINTF(NODE, ("\tlookup of parent '..' resulted in error %d, " + "found %d\n", error, found)); + if (!found) + error = ENOENT; + if (error) { + vput(vp); + return error; + } + + /* did we encounter the root node? i.e. loop back */ + if (udf_compare_icb(&parent_loc, &VTOI(vp)->loc) == 0) { + DPRINTF(NODE, ("ROOT found!\n")); + vput(vp); + *intermediate_node_ret = NULL; + return 0; + } + + /* Did we find that fdvp is an ancestor of tdvp? */ + if (udf_compare_icb(&parent_loc, &VTOI(fdvp)->loc) == 0) { + DPRINTF(NODE, ("fdvp is ancestor of tdvp\n")); + *intermediate_node_ret = vp; + VOP_UNLOCK(vp); + return 0; + } + + /* + * Unlock vp so that we can lock the parent, but keep child vp + * referenced until after we have found the parent, so that + * parent_node will not be recycled. + */ + DPRINTF(NODE, ("\tgetting the parent node\n")); + VOP_UNLOCK(vp); + error = udf_get_node(ump, &parent_loc, &parent_node); + vrele(vp); + if (error) + return error; + + dvp = parent_node->vnode; + + /* switch */ + KASSERT(dvp != NULL); + KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); + vp = dvp; + + /* sanity check */ + if (vp->v_type != VDIR) { + /* + * Odd, but can happen if we loose the race and the + * '..' node has been recycled. + */ + vput(vp); + return ENOTDIR; + } + + if (udf_rmdired_p(vp)) { + vput(vp); + return ENOENT; + } + } +} + + +/* + * udf_gro_lock_directory: lock the directory vp, but fail if it has been + * rmdir'd. + */ +static int +udf_gro_lock_directory(struct mount *mp, struct vnode *vp) +{ + + (void)mp; + KASSERT(mp != NULL); + KASSERT(vp != NULL); + KASSERT(vp->v_mount == mp); + + DPRINTF(CALL, ("udf_gro_lock_directory called\n")); + DPRINTF(LOCKING, ("udf_gro_lock_directory called\n")); + + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + + if (udf_rmdired_p(vp)) { + VOP_UNLOCK(vp); + return ENOENT; + } + + return 0; +} + + +static const struct genfs_rename_ops udf_genfs_rename_ops = { + .gro_directory_empty_p = udf_gro_directory_empty_p, + .gro_rename_check_possible = udf_gro_rename_check_possible, + .gro_rename_check_permitted = udf_gro_rename_check_permitted, + .gro_remove_check_possible = udf_gro_remove_check_possible, + .gro_remove_check_permitted = udf_gro_remove_check_permitted, + .gro_rename = udf_gro_rename, + .gro_remove = udf_gro_remove, + .gro_lookup = udf_gro_lookup, + .gro_genealogy = udf_gro_genealogy, + .gro_lock_directory = udf_gro_lock_directory, +}; diff --git a/sys/fs/udf/udf_strat_bootstrap.c b/sys/fs/udf/udf_strat_bootstrap.c new file mode 100644 index 000000000..74567ad44 --- /dev/null +++ b/sys/fs/udf/udf_strat_bootstrap.c @@ -0,0 +1,145 @@ +/* $NetBSD: udf_strat_bootstrap.c,v 1.3 2008/12/16 16:18:25 pooka Exp $ */ + +/* + * Copyright (c) 2006, 2008 Reinoud Zandijk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#ifndef lint +__KERNEL_RCSID(0, "$NetBSD: udf_strat_bootstrap.c,v 1.3 2008/12/16 16:18:25 pooka Exp $"); +#endif /* not lint */ + + +#if defined(_KERNEL_OPT) +#include "opt_compat_netbsd.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "udf.h" +#include "udf_subr.h" +#include "udf_bswap.h" + + +#define VTOI(vnode) ((struct udf_node *) vnode->v_data) +#define PRIV(ump) ((struct strat_private *) ump->strategy_private) + +/* --------------------------------------------------------------------- */ + +static int +udf_create_logvol_dscr_bootstrap(struct udf_strat_args *args) +{ + panic("udf_create_logvol_dscr_bootstrap: not possible\n"); + return 0; +} + + +static void +udf_free_logvol_dscr_bootstrap(struct udf_strat_args *args) +{ + panic("udf_free_logvol_dscr_bootstrap: no node descriptor reading\n"); +} + + +static int +udf_read_logvol_dscr_bootstrap(struct udf_strat_args *args) +{ + panic("udf_read_logvol_dscr_bootstrap: no node descriptor reading\n"); + return 0; +} + + +static int +udf_write_logvol_dscr_bootstrap(struct udf_strat_args *args) +{ + panic("udf_write_logvol_dscr_bootstrap: no writing\n"); +} + +/* --------------------------------------------------------------------- */ + +static void +udf_queuebuf_bootstrap(struct udf_strat_args *args) +{ + struct udf_mount *ump = args->ump; + struct buf *buf = args->nestbuf; + + KASSERT(ump); + KASSERT(buf); + KASSERT(buf->b_iodone == nestiobuf_iodone); + + KASSERT(buf->b_flags & B_READ); + VOP_STRATEGY(ump->devvp, buf); +} + +static void +udf_discstrat_init_bootstrap(struct udf_strat_args *args) +{ + /* empty */ +} + + +static void +udf_discstrat_finish_bootstrap(struct udf_strat_args *args) +{ + /* empty */ +} + +/* --------------------------------------------------------------------- */ + +struct udf_strategy udf_strat_bootstrap = +{ + udf_create_logvol_dscr_bootstrap, + udf_free_logvol_dscr_bootstrap, + udf_read_logvol_dscr_bootstrap, + udf_write_logvol_dscr_bootstrap, + udf_queuebuf_bootstrap, + udf_discstrat_init_bootstrap, + udf_discstrat_finish_bootstrap +}; + + diff --git a/sys/fs/udf/udf_strat_direct.c b/sys/fs/udf/udf_strat_direct.c new file mode 100644 index 000000000..c6cd59121 --- /dev/null +++ b/sys/fs/udf/udf_strat_direct.c @@ -0,0 +1,454 @@ +/* $NetBSD: udf_strat_direct.c,v 1.12 2013/10/30 08:41:38 mrg Exp $ */ + +/* + * Copyright (c) 2006, 2008 Reinoud Zandijk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#ifndef lint +__KERNEL_RCSID(0, "$NetBSD: udf_strat_direct.c,v 1.12 2013/10/30 08:41:38 mrg Exp $"); +#endif /* not lint */ + + +#if defined(_KERNEL_OPT) +#include "opt_compat_netbsd.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "udf.h" +#include "udf_subr.h" +#include "udf_bswap.h" + + +#define VTOI(vnode) ((struct udf_node *) vnode->v_data) +#define PRIV(ump) ((struct strat_private *) ump->strategy_private) + +/* --------------------------------------------------------------------- */ + +/* BUFQ's */ +#define UDF_SHED_MAX 3 + +#define UDF_SHED_READING 0 +#define UDF_SHED_WRITING 1 +#define UDF_SHED_SEQWRITING 2 + + +struct strat_private { + struct pool desc_pool; /* node descriptors */ +}; + +/* --------------------------------------------------------------------- */ + +static void +udf_wr_nodedscr_callback(struct buf *buf) +{ + struct udf_node *udf_node; + + KASSERT(buf); + KASSERT(buf->b_data); + + /* called when write action is done */ + DPRINTF(WRITE, ("udf_wr_nodedscr_callback(): node written out\n")); + + udf_node = VTOI(buf->b_vp); + if (udf_node == NULL) { + putiobuf(buf); + printf("udf_wr_node_callback: NULL node?\n"); + return; + } + + /* XXX right flags to mark dirty again on error? */ + if (buf->b_error) { + /* write error on `defect free' media??? how to solve? */ + /* XXX lookup UDF standard for unallocatable space */ + udf_node->i_flags |= IN_MODIFIED | IN_ACCESSED; + } + + /* decrement outstanding_nodedscr */ + KASSERT(udf_node->outstanding_nodedscr >= 1); + udf_node->outstanding_nodedscr--; + if (udf_node->outstanding_nodedscr == 0) { + /* unlock the node */ + UDF_UNLOCK_NODE(udf_node, 0); + wakeup(&udf_node->outstanding_nodedscr); + } + /* unreference the vnode so it can be recycled */ + holdrele(udf_node->vnode); + + putiobuf(buf); +} + +/* --------------------------------------------------------------------- */ + +static int +udf_getblank_nodedscr_direct(struct udf_strat_args *args) +{ + union dscrptr **dscrptr = &args->dscr; + struct udf_mount *ump = args->ump; + struct strat_private *priv = PRIV(ump); + uint32_t lb_size; + + lb_size = udf_rw32(ump->logical_vol->lb_size); + *dscrptr = pool_get(&priv->desc_pool, PR_WAITOK); + memset(*dscrptr, 0, lb_size); + + return 0; +} + + +static void +udf_free_nodedscr_direct(struct udf_strat_args *args) +{ + union dscrptr *dscr = args->dscr; + struct udf_mount *ump = args->ump; + struct strat_private *priv = PRIV(ump); + + pool_put(&priv->desc_pool, dscr); +} + + +static int +udf_read_nodedscr_direct(struct udf_strat_args *args) +{ + union dscrptr **dscrptr = &args->dscr; + union dscrptr *tmpdscr; + struct udf_mount *ump = args->ump; + struct long_ad *icb = args->icb; + struct strat_private *priv = PRIV(ump); + uint32_t lb_size; + uint32_t sector, dummy; + int error; + + lb_size = udf_rw32(ump->logical_vol->lb_size); + + error = udf_translate_vtop(ump, icb, §or, &dummy); + if (error) + return error; + + /* try to read in fe/efe */ + error = udf_read_phys_dscr(ump, sector, M_UDFTEMP, &tmpdscr); + if (error) + return error; + + *dscrptr = pool_get(&priv->desc_pool, PR_WAITOK); + memcpy(*dscrptr, tmpdscr, lb_size); + free(tmpdscr, M_UDFTEMP); + + return 0; +} + + +static int +udf_write_nodedscr_direct(struct udf_strat_args *args) +{ + struct udf_mount *ump = args->ump; + struct udf_node *udf_node = args->udf_node; + union dscrptr *dscr = args->dscr; + struct long_ad *icb = args->icb; + int waitfor = args->waitfor; + uint32_t logsector, sector, dummy; + int error, vpart __diagused; + + /* + * we have to decide if we write it out sequential or at its fixed + * position by examining the partition its (to be) written on. + */ + vpart = udf_rw16(udf_node->loc.loc.part_num); + logsector = udf_rw32(icb->loc.lb_num); + KASSERT(ump->vtop_tp[vpart] != UDF_VTOP_TYPE_VIRT); + + sector = 0; + error = udf_translate_vtop(ump, icb, §or, &dummy); + if (error) + goto out; + + /* add reference to the vnode to prevent recycling */ + vhold(udf_node->vnode); + + if (waitfor) { + DPRINTF(WRITE, ("udf_write_nodedscr: sync write\n")); + + error = udf_write_phys_dscr_sync(ump, udf_node, UDF_C_NODE, + dscr, sector, logsector); + } else { + DPRINTF(WRITE, ("udf_write_nodedscr: no wait, async write\n")); + + error = udf_write_phys_dscr_async(ump, udf_node, UDF_C_NODE, + dscr, sector, logsector, udf_wr_nodedscr_callback); + /* will be UNLOCKED in call back */ + return error; + } + + holdrele(udf_node->vnode); +out: + udf_node->outstanding_nodedscr--; + if (udf_node->outstanding_nodedscr == 0) { + UDF_UNLOCK_NODE(udf_node, 0); + wakeup(&udf_node->outstanding_nodedscr); + } + + return error; +} + +/* --------------------------------------------------------------------- */ + +static void +udf_queue_buf_direct(struct udf_strat_args *args) +{ + struct udf_mount *ump = args->ump; + struct buf *buf = args->nestbuf; + struct buf *nestbuf; + struct desc_tag *tag; + struct long_ad *node_ad_cpy; + uint64_t *lmapping, *pmapping, *lmappos, run_start; + uint32_t sectornr; + uint32_t buf_offset, rbuflen, bpos; + uint16_t vpart_num; + uint8_t *fidblk; + off_t rblk; + int sector_size = ump->discinfo.sector_size; + int len, buf_len, sector, sectors, run_length; + int blks = sector_size / DEV_BSIZE; + int what, class __diagused, queue; + + KASSERT(ump); + KASSERT(buf); + KASSERT(buf->b_iodone == nestiobuf_iodone); + + what = buf->b_udf_c_type; + queue = UDF_SHED_READING; + if ((buf->b_flags & B_READ) == 0) { + /* writing */ + queue = UDF_SHED_SEQWRITING; + if (what == UDF_C_ABSOLUTE) + queue = UDF_SHED_WRITING; + if (what == UDF_C_DSCR) + queue = UDF_SHED_WRITING; + if (what == UDF_C_NODE) + queue = UDF_SHED_WRITING; + } + + /* use disc sheduler */ + class = ump->discinfo.mmc_class; + KASSERT((class == MMC_CLASS_UNKN) || (class == MMC_CLASS_DISC) || + (ump->discinfo.mmc_cur & MMC_CAP_HW_DEFECTFREE) || + (ump->vfs_mountp->mnt_flag & MNT_RDONLY)); + +#ifndef UDF_DEBUG + __USE(blks); +#endif + if (queue == UDF_SHED_READING) { + DPRINTF(SHEDULE, ("\nudf_issue_buf READ %p : sector %d type %d," + "b_resid %d, b_bcount %d, b_bufsize %d\n", + buf, (uint32_t) buf->b_blkno / blks, buf->b_udf_c_type, + buf->b_resid, buf->b_bcount, buf->b_bufsize)); + VOP_STRATEGY(ump->devvp, buf); + return; + } + + + if (queue == UDF_SHED_WRITING) { + DPRINTF(SHEDULE, ("\nudf_issue_buf WRITE %p : sector %d " + "type %d, b_resid %d, b_bcount %d, b_bufsize %d\n", + buf, (uint32_t) buf->b_blkno / blks, buf->b_udf_c_type, + buf->b_resid, buf->b_bcount, buf->b_bufsize)); + KASSERT(buf->b_udf_c_type == UDF_C_DSCR || + buf->b_udf_c_type == UDF_C_ABSOLUTE || + buf->b_udf_c_type == UDF_C_NODE); + udf_fixup_node_internals(ump, buf->b_data, buf->b_udf_c_type); + VOP_STRATEGY(ump->devvp, buf); + return; + } + + /* UDF_SHED_SEQWRITING */ + KASSERT(queue == UDF_SHED_SEQWRITING); + DPRINTF(SHEDULE, ("\nudf_issue_buf SEQWRITE %p : sector XXXX " + "type %d, b_resid %d, b_bcount %d, b_bufsize %d\n", + buf, buf->b_udf_c_type, buf->b_resid, buf->b_bcount, + buf->b_bufsize)); + + /* + * Buffers should not have been allocated to disc addresses yet on + * this queue. Note that a buffer can get multiple extents allocated. + * + * lmapping contains lb_num relative to base partition. + */ + lmapping = ump->la_lmapping; + node_ad_cpy = ump->la_node_ad_cpy; + + /* logically allocate buf and map it in the file */ + udf_late_allocate_buf(ump, buf, lmapping, node_ad_cpy, &vpart_num); + + /* if we have FIDs, fixup using the new allocation table */ + if (buf->b_udf_c_type == UDF_C_FIDS) { + buf_len = buf->b_bcount; + bpos = 0; + lmappos = lmapping; + while (buf_len) { + sectornr = *lmappos++; + len = MIN(buf_len, sector_size); + fidblk = (uint8_t *) buf->b_data + bpos; + udf_fixup_fid_block(fidblk, sector_size, + 0, len, sectornr); + bpos += len; + buf_len -= len; + } + } + if (buf->b_udf_c_type == UDF_C_METADATA_SBM) { + if (buf->b_lblkno == 0) { + /* update the tag location inside */ + tag = (struct desc_tag *) buf->b_data; + tag->tag_loc = udf_rw32(*lmapping); + udf_validate_tag_and_crc_sums(buf->b_data); + } + } + udf_fixup_node_internals(ump, buf->b_data, buf->b_udf_c_type); + + /* + * Translate new mappings in lmapping to pmappings and try to + * conglomerate extents to reduce the number of writes. + * + * pmapping to contain lb_nums as used for disc adressing. + */ + pmapping = ump->la_pmapping; + sectors = (buf->b_bcount + sector_size -1) / sector_size; + udf_translate_vtop_list(ump, sectors, vpart_num, lmapping, pmapping); + + for (sector = 0; sector < sectors; sector++) { + buf_offset = sector * sector_size; + DPRINTF(WRITE, ("\tprocessing rel sector %d\n", sector)); + + DPRINTF(WRITE, ("\tissue write sector %"PRIu64"\n", + pmapping[sector])); + + run_start = pmapping[sector]; + run_length = 1; + while (sector < sectors-1) { + if (pmapping[sector+1] != pmapping[sector]+1) + break; + run_length++; + sector++; + } + + /* nest an iobuf for the extent */ + rbuflen = run_length * sector_size; + rblk = run_start * (sector_size/DEV_BSIZE); + + nestbuf = getiobuf(NULL, true); + nestiobuf_setup(buf, nestbuf, buf_offset, rbuflen); + /* nestbuf is B_ASYNC */ + + /* identify this nestbuf */ + nestbuf->b_lblkno = sector; + assert(nestbuf->b_vp == buf->b_vp); + + /* CD shedules on raw blkno */ + nestbuf->b_blkno = rblk; + nestbuf->b_proc = NULL; + nestbuf->b_rawblkno = rblk; + nestbuf->b_udf_c_type = UDF_C_PROCESSED; + + VOP_STRATEGY(ump->devvp, nestbuf); + } +} + + +static void +udf_discstrat_init_direct(struct udf_strat_args *args) +{ + struct udf_mount *ump = args->ump; + struct strat_private *priv = PRIV(ump); + uint32_t lb_size; + + KASSERT(priv == NULL); + ump->strategy_private = malloc(sizeof(struct strat_private), + M_UDFTEMP, M_WAITOK); + priv = ump->strategy_private; + memset(priv, 0 , sizeof(struct strat_private)); + + /* + * Initialise pool for descriptors associated with nodes. This is done + * in lb_size units though currently lb_size is dictated to be + * sector_size. + */ + memset(&priv->desc_pool, 0, sizeof(struct pool)); + + lb_size = udf_rw32(ump->logical_vol->lb_size); + pool_init(&priv->desc_pool, lb_size, 0, 0, 0, "udf_desc_pool", NULL, + IPL_NONE); +} + + +static void +udf_discstrat_finish_direct(struct udf_strat_args *args) +{ + struct udf_mount *ump = args->ump; + struct strat_private *priv = PRIV(ump); + + /* destroy our pool */ + pool_destroy(&priv->desc_pool); + + /* free our private space */ + free(ump->strategy_private, M_UDFTEMP); + ump->strategy_private = NULL; +} + +/* --------------------------------------------------------------------- */ + +struct udf_strategy udf_strat_direct = +{ + udf_getblank_nodedscr_direct, + udf_free_nodedscr_direct, + udf_read_nodedscr_direct, + udf_write_nodedscr_direct, + udf_queue_buf_direct, + udf_discstrat_init_direct, + udf_discstrat_finish_direct +}; + diff --git a/sys/fs/udf/udf_strat_rmw.c b/sys/fs/udf/udf_strat_rmw.c new file mode 100644 index 000000000..cf04d8197 --- /dev/null +++ b/sys/fs/udf/udf_strat_rmw.c @@ -0,0 +1,1507 @@ +/* $NetBSD: udf_strat_rmw.c,v 1.24 2013/10/30 08:41:38 mrg Exp $ */ + +/* + * Copyright (c) 2006, 2008 Reinoud Zandijk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#ifndef lint +__KERNEL_RCSID(0, "$NetBSD: udf_strat_rmw.c,v 1.24 2013/10/30 08:41:38 mrg Exp $"); +#endif /* not lint */ + + +#if defined(_KERNEL_OPT) +#include "opt_compat_netbsd.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "udf.h" +#include "udf_subr.h" +#include "udf_bswap.h" + + +#define VTOI(vnode) ((struct udf_node *) (vnode)->v_data) +#define PRIV(ump) ((struct strat_private *) (ump)->strategy_private) +#define BTOE(buf) ((struct udf_eccline *) ((buf)->b_private)) + +/* --------------------------------------------------------------------- */ + +#define UDF_MAX_PACKET_SIZE 64 /* DONT change this */ + +/* sheduler states */ +#define UDF_SHED_WAITING 1 /* waiting on timeout */ +#define UDF_SHED_READING 2 +#define UDF_SHED_WRITING 3 +#define UDF_SHED_SEQWRITING 4 +#define UDF_SHED_IDLE 5 /* refcnt'd */ +#define UDF_SHED_FREE 6 /* recycleable */ +#define UDF_SHED_MAX 6+1 + +/* flags */ +#define ECC_LOCKED 0x01 /* prevent access */ +#define ECC_WANTED 0x02 /* trying access */ +#define ECC_SEQWRITING 0x04 /* sequential queue */ +#define ECC_FLOATING 0x08 /* not queued yet */ + +#define ECC_WAITTIME 10 + + +TAILQ_HEAD(ecclineq, udf_eccline); +struct udf_eccline { + struct udf_mount *ump; + uint64_t present; /* preserve these */ + uint64_t readin; /* bitmap */ + uint64_t dirty; /* bitmap */ + uint64_t error; /* bitmap */ + uint32_t refcnt; + + struct timespec wait_time; + uint32_t flags; + uint32_t start_sector; /* physical */ + + const char *fname; + int sline; + + struct buf *buf; + void *blob; + + struct buf *bufs[UDF_MAX_PACKET_SIZE]; + uint32_t bufs_bpos[UDF_MAX_PACKET_SIZE]; + int bufs_len[UDF_MAX_PACKET_SIZE]; + + int queued_on; /* on which BUFQ list */ + LIST_ENTRY(udf_eccline) hashchain; /* on sector lookup */ +}; + + +struct strat_private { + lwp_t *queue_lwp; + kcondvar_t discstrat_cv; /* to wait on */ + kmutex_t discstrat_mutex; /* disc strategy */ + kmutex_t seqwrite_mutex; /* protect mappings */ + + int thread_running; /* thread control */ + int run_thread; /* thread control */ + int thread_finished; /* thread control */ + int cur_queue; + + int num_floating; + int num_queued[UDF_SHED_MAX]; + struct bufq_state *queues[UDF_SHED_MAX]; + struct timespec last_queued[UDF_SHED_MAX]; + struct disk_strategy old_strategy_setting; + + struct pool eccline_pool; + struct pool ecclineblob_pool; + LIST_HEAD(, udf_eccline) eccline_hash[UDF_ECCBUF_HASHSIZE]; +}; + +/* --------------------------------------------------------------------- */ + +#define UDF_LOCK_ECCLINE(eccline) udf_lock_eccline(eccline, __FILE__, __LINE__) +#define UDF_UNLOCK_ECCLINE(eccline) udf_unlock_eccline(eccline, __FILE__, __LINE__) + +/* can be called with or without discstrat lock */ +static void +udf_lock_eccline(struct udf_eccline *eccline, const char *fname, int sline) +{ + struct strat_private *priv = PRIV(eccline->ump); + int waslocked, ret; + + KASSERT(mutex_owned(&priv->discstrat_mutex)); + + waslocked = mutex_owned(&priv->discstrat_mutex); + if (!waslocked) + mutex_enter(&priv->discstrat_mutex); + + /* wait until its unlocked first */ + eccline->refcnt++; + while (eccline->flags & ECC_LOCKED) { + DPRINTF(ECCLINE, ("waiting for lock at %s:%d\n", + fname, sline)); + DPRINTF(ECCLINE, ("was locked at %s:%d\n", + eccline->fname, eccline->sline)); + eccline->flags |= ECC_WANTED; + ret = cv_timedwait(&priv->discstrat_cv, &priv->discstrat_mutex, + hz/8); + if (ret == EWOULDBLOCK) + DPRINTF(LOCKING, ("eccline lock helt, waiting for " + "release")); + } + eccline->flags |= ECC_LOCKED; + eccline->flags &= ~ECC_WANTED; + eccline->refcnt--; + + eccline->fname = fname; + eccline->sline = sline; + + if (!waslocked) + mutex_exit(&priv->discstrat_mutex); +} + + +/* can be called with or without discstrat lock */ +static void +udf_unlock_eccline(struct udf_eccline *eccline, const char *fname, int sline) +{ + struct strat_private *priv = PRIV(eccline->ump); + int waslocked; + + KASSERT(mutex_owned(&priv->discstrat_mutex)); + + waslocked = mutex_owned(&priv->discstrat_mutex); + if (!waslocked) + mutex_enter(&priv->discstrat_mutex); + + eccline->flags &= ~ECC_LOCKED; + cv_broadcast(&priv->discstrat_cv); + + if (!waslocked) + mutex_exit(&priv->discstrat_mutex); +} + + +/* NOTE discstrat_mutex should be held! */ +static void +udf_dispose_eccline(struct udf_eccline *eccline) +{ + struct strat_private *priv = PRIV(eccline->ump); + + KASSERT(mutex_owned(&priv->discstrat_mutex)); + + DPRINTF(ECCLINE, ("dispose eccline with start sector %d, " + "present %0"PRIx64"\n", eccline->start_sector, + eccline->present)); + + KASSERT(eccline->refcnt == 0); + KASSERT(eccline->dirty == 0); + KASSERT(eccline->queued_on == 0); + KASSERT(eccline->flags & ECC_FLOATING); + KASSERT(eccline->flags & ECC_LOCKED); + + LIST_REMOVE(eccline, hashchain); + priv->num_floating--; + + putiobuf(eccline->buf); + pool_put(&priv->ecclineblob_pool, eccline->blob); + pool_put(&priv->eccline_pool, eccline); +} + + +/* NOTE discstrat_mutex should be held! */ +static void +udf_push_eccline(struct udf_eccline *eccline, int newqueue) +{ + struct strat_private *priv = PRIV(eccline->ump); + + KASSERT(mutex_owned(&priv->discstrat_mutex)); + + DPRINTF(PARANOIA, ("DEBUG: buf %p pushed on queue %d\n", eccline->buf, newqueue)); + + KASSERT(eccline->queued_on == 0); + KASSERT(eccline->flags & ECC_FLOATING); + + /* set buffer block numbers to make sure its queued correctly */ + eccline->buf->b_lblkno = eccline->start_sector; + eccline->buf->b_blkno = eccline->start_sector; + eccline->buf->b_rawblkno = eccline->start_sector; + + vfs_timestamp(&priv->last_queued[newqueue]); + eccline->flags &= ~ECC_FLOATING; + priv->num_floating--; + eccline->queued_on = newqueue; + priv->num_queued[newqueue]++; + bufq_put(priv->queues[newqueue], eccline->buf); + + UDF_UNLOCK_ECCLINE(eccline); + + /* XXX tickle disc strategy statemachine */ + if (newqueue != UDF_SHED_IDLE) + cv_signal(&priv->discstrat_cv); +} + + +static struct udf_eccline * +udf_peek_eccline(struct strat_private *priv, int queued_on) +{ + struct udf_eccline *eccline; + struct buf *buf; + + KASSERT(mutex_owned(&priv->discstrat_mutex)); + + for(;;) { + buf = bufq_peek(priv->queues[queued_on]); + /* could have been a race, but we'll revisit later */ + if (buf == NULL) + return NULL; + + eccline = BTOE(buf); + UDF_LOCK_ECCLINE(eccline); + + /* might have changed before we obtained the lock */ + if (eccline->queued_on == queued_on) + break; + + UDF_UNLOCK_ECCLINE(eccline); + } + + KASSERT(eccline->queued_on == queued_on); + KASSERT((eccline->flags & ECC_FLOATING) == 0); + + DPRINTF(PARANOIA, ("DEBUG: buf %p peeked at queue %d\n", + eccline->buf, queued_on)); + + return eccline; +} + + +static struct udf_eccline * +udf_pop_eccline(struct strat_private *priv, int queued_on) +{ + struct udf_eccline *eccline; + struct buf *buf; + + KASSERT(mutex_owned(&priv->discstrat_mutex)); + + for(;;) { + buf = bufq_get(priv->queues[queued_on]); + if (buf == NULL) { + // KASSERT(priv->num_queued[queued_on] == 0); + return NULL; + } + + eccline = BTOE(buf); + UDF_LOCK_ECCLINE(eccline); + + /* might have changed before we obtained the lock */ + if (eccline->queued_on == queued_on) + break; + + UDF_UNLOCK_ECCLINE(eccline); + } + + KASSERT(eccline->queued_on == queued_on); + KASSERT((eccline->flags & ECC_FLOATING) == 0); + + priv->num_queued[queued_on]--; + eccline->queued_on = 0; + + eccline->flags |= ECC_FLOATING; + priv->num_floating++; + + DPRINTF(PARANOIA, ("DEBUG: buf %p popped from queue %d\n", + eccline->buf, queued_on)); + + return eccline; +} + + +static void +udf_unqueue_eccline(struct strat_private *priv, struct udf_eccline *eccline) +{ + struct buf *ret __diagused; + + UDF_LOCK_ECCLINE(eccline); + if (eccline->queued_on == 0) { + KASSERT(eccline->flags & ECC_FLOATING); + return; + } + + ret = bufq_cancel(priv->queues[eccline->queued_on], eccline->buf); + KASSERT(ret == eccline->buf); + + priv->num_queued[eccline->queued_on]--; + eccline->queued_on = 0; + + eccline->flags |= ECC_FLOATING; + priv->num_floating++; +} + + +static struct udf_eccline * +udf_geteccline(struct udf_mount *ump, uint32_t sector, int flags) +{ + struct strat_private *priv = PRIV(ump); + struct udf_eccline *eccline; + uint32_t start_sector, lb_size, blobsize; + uint8_t *eccline_blob; + int line, line_offset; + int num_busy; + + mutex_enter(&priv->discstrat_mutex); + + /* lookup in our line cache hashtable */ + line_offset = sector % ump->packet_size; + start_sector = sector - line_offset; + line = (start_sector/ump->packet_size) & UDF_ECCBUF_HASHMASK; + + KASSERT(priv->thread_running); + +retry: + DPRINTF(ECCLINE, ("get line sector %d, line %d\n", sector, line)); + LIST_FOREACH(eccline, &priv->eccline_hash[line], hashchain) { + if (eccline->start_sector == start_sector) { + DPRINTF(ECCLINE, ("\tfound eccline, start_sector %d\n", + eccline->start_sector)); + udf_unqueue_eccline(priv, eccline); + + mutex_exit(&priv->discstrat_mutex); + return eccline; + } + } + + /* not found in eccline cache */ + DPRINTF(ECCLINE, ("\tnot found in eccline cache\n")); + + lb_size = udf_rw32(ump->logical_vol->lb_size); + blobsize = ump->packet_size * lb_size; + + /* dont allow too many pending requests */ + DPRINTF(ECCLINE, ("\tallocating new eccline\n")); + num_busy = (priv->num_queued[UDF_SHED_SEQWRITING] + priv->num_floating); + if ((flags & ECC_SEQWRITING) && (num_busy > UDF_ECCLINE_MAXBUSY)) { + cv_timedwait(&priv->discstrat_cv, + &priv->discstrat_mutex, hz/8); + goto retry; + } + + eccline_blob = pool_get(&priv->ecclineblob_pool, PR_NOWAIT); + eccline = pool_get(&priv->eccline_pool, PR_NOWAIT); + if ((eccline_blob == NULL) || (eccline == NULL)) { + if (eccline_blob) + pool_put(&priv->ecclineblob_pool, eccline_blob); + if (eccline) + pool_put(&priv->eccline_pool, eccline); + + /* out of memory for now; canibalise freelist */ + eccline = udf_pop_eccline(priv, UDF_SHED_FREE); + if (eccline == NULL) { + /* serious trouble; wait and retry */ + cv_timedwait(&priv->discstrat_cv, + &priv->discstrat_mutex, hz/8); + goto retry; + } + + /* push back line if we're waiting for it or its locked */ + if (eccline->flags & ECC_WANTED) { + /* we won a race, but someone else needed it */ + udf_push_eccline(eccline, UDF_SHED_FREE); + goto retry; + } + + /* unlink this entry */ + LIST_REMOVE(eccline, hashchain); + KASSERT(eccline->flags & ECC_FLOATING); + KASSERT(eccline->queued_on == 0); + + eccline_blob = eccline->blob; + eccline->flags = ECC_FLOATING | ECC_LOCKED; + } else { + eccline->flags = ECC_FLOATING | ECC_LOCKED; + priv->num_floating++; + } + + eccline->queued_on = 0; + eccline->blob = eccline_blob; + eccline->buf = getiobuf(NULL, true); + eccline->buf->b_private = eccline; /* IMPORTANT */ + + /* initialise eccline blob */ + /* XXX memset expensive and strictly not needed XXX */ + memset(eccline->blob, 0, blobsize); + + eccline->ump = ump; + eccline->present = eccline->readin = eccline->dirty = 0; + eccline->error = 0; + eccline->refcnt = 0; + memset(eccline->bufs, 0, UDF_MAX_PACKET_SIZE * sizeof(struct buf *)); + + eccline->start_sector = start_sector; + eccline->buf->b_lblkno = start_sector; + eccline->buf->b_blkno = start_sector; + eccline->buf->b_rawblkno = start_sector; + + LIST_INSERT_HEAD(&priv->eccline_hash[line], eccline, hashchain); + + /* + * TODO possible optimalisation for checking overlap with partitions + * to get a clue on future eccline usage + */ + + KASSERT(eccline->refcnt == 0); + KASSERT(eccline->flags & ECC_FLOATING); + KASSERT(eccline->flags & ECC_LOCKED); + mutex_exit(&priv->discstrat_mutex); + + return eccline; +} + + +static void +udf_puteccline(struct udf_eccline *eccline) +{ + struct strat_private *priv = PRIV(eccline->ump); + struct udf_mount *ump = eccline->ump; + uint64_t allbits = ((uint64_t) 1 << ump->packet_size)-1; + int new_queue; + + mutex_enter(&priv->discstrat_mutex); + + DPRINTF(ECCLINE, ("put eccline start sector %d, refcnt %d\n", + eccline->start_sector, eccline->refcnt)); + + KASSERT(eccline->flags & ECC_LOCKED); + KASSERT(eccline->flags & ECC_FLOATING); + + /* clear all read bits that are already read in */ + if (eccline->readin & eccline->present) + eccline->readin &= (~eccline->present) & allbits; + + /* if we have active nodes we dont set it on seqwriting */ + if (eccline->refcnt > 1) + eccline->flags &= ~ECC_SEQWRITING; + + /* select state */ + new_queue = UDF_SHED_FREE; + if (eccline->refcnt > 0) + new_queue = UDF_SHED_IDLE; + if (eccline->flags & ECC_WANTED) + new_queue = UDF_SHED_IDLE; + if (eccline->readin) + new_queue = UDF_SHED_READING; + if (eccline->dirty) { + new_queue = UDF_SHED_WAITING; + vfs_timestamp(&eccline->wait_time); + eccline->wait_time.tv_sec += ECC_WAITTIME; + + if (eccline->present == allbits) { + new_queue = UDF_SHED_WRITING; + if (eccline->flags & ECC_SEQWRITING) + new_queue = UDF_SHED_SEQWRITING; + } + } + udf_push_eccline(eccline, new_queue); + + mutex_exit(&priv->discstrat_mutex); +} + +/* --------------------------------------------------------------------- */ + +static int +udf_create_nodedscr_rmw(struct udf_strat_args *args) +{ + union dscrptr **dscrptr = &args->dscr; + struct udf_mount *ump = args->ump; + struct long_ad *icb = args->icb; + struct udf_eccline *eccline; + uint64_t bit; + uint32_t sectornr, lb_size, dummy; + uint8_t *mem; + int error, eccsect; + + error = udf_translate_vtop(ump, icb, §ornr, &dummy); + if (error) + return error; + + lb_size = udf_rw32(ump->logical_vol->lb_size); + + /* get our eccline */ + eccline = udf_geteccline(ump, sectornr, 0); + eccsect = sectornr - eccline->start_sector; + + bit = (uint64_t) 1 << eccsect; + eccline->readin &= ~bit; /* just in case */ + eccline->present |= bit; + eccline->dirty &= ~bit; /* Err... euhm... clean? */ + + eccline->refcnt++; + + /* clear space */ + mem = ((uint8_t *) eccline->blob) + eccsect * lb_size; + memset(mem, 0, lb_size); + + udf_puteccline(eccline); + + *dscrptr = (union dscrptr *) mem; + return 0; +} + + +static void +udf_free_nodedscr_rmw(struct udf_strat_args *args) +{ + struct udf_mount *ump = args->ump; + struct long_ad *icb = args->icb; + struct udf_eccline *eccline; + uint64_t bit; + uint32_t sectornr, dummy; + int error, eccsect; + + error = udf_translate_vtop(ump, icb, §ornr, &dummy); + if (error) + return; + + /* get our eccline */ + eccline = udf_geteccline(ump, sectornr, 0); + eccsect = sectornr - eccline->start_sector; + + bit = (uint64_t) 1 << eccsect; + KASSERT(eccline->present & bit); + + eccline->readin &= ~bit; /* just in case */ + /* XXX eccline->dirty? */ + + KASSERT(eccline->refcnt >= 1); + eccline->refcnt--; + + udf_puteccline(eccline); +} + + +static int +udf_read_nodedscr_rmw(struct udf_strat_args *args) +{ + union dscrptr **dscrptr = &args->dscr; + struct udf_mount *ump = args->ump; + struct long_ad *icb = args->icb; + struct strat_private *priv; + struct udf_eccline *eccline; + uint64_t bit; + uint32_t sectornr, dummy; + uint8_t *pos; + int sector_size = ump->discinfo.sector_size; + int lb_size = udf_rw32(ump->logical_vol->lb_size); + int i, error, dscrlen, eccsect; + + lb_size = lb_size; + KASSERT(sector_size == lb_size); + error = udf_translate_vtop(ump, icb, §ornr, &dummy); + if (error) + return error; + + /* get our eccline */ + eccline = udf_geteccline(ump, sectornr, 0); + eccsect = sectornr - eccline->start_sector; + + bit = (uint64_t) 1 << eccsect; + if ((eccline->present & bit) == 0) { + /* mark bit for readin */ + eccline->readin |= bit; + eccline->refcnt++; /* prevent recycling */ + KASSERT(eccline->bufs[eccsect] == NULL); + udf_puteccline(eccline); + + /* wait for completion */ + priv = PRIV(eccline->ump); + mutex_enter(&priv->discstrat_mutex); + while (((eccline->present | eccline->error) & bit) == 0) { + error = cv_timedwait(&priv->discstrat_cv, + &priv->discstrat_mutex, + hz/8); + if (error == EWOULDBLOCK) + DPRINTF(LOCKING, ("eccline waiting for read\n")); + } + mutex_exit(&priv->discstrat_mutex); + + /* reget our line */ + eccline = udf_geteccline(ump, sectornr, 0); + KASSERT(eccline->refcnt >= 1); + eccline->refcnt--; /* undo refcnt */ + + if (eccline->error & bit) { + *dscrptr = NULL; + udf_puteccline(eccline); + return EIO; /* XXX error code */ + } + } + + *dscrptr = (union dscrptr *) + (((uint8_t *) eccline->blob) + eccsect * sector_size); + + /* code from read_phys_descr */ + /* check if its a valid tag */ + error = udf_check_tag(*dscrptr); + if (error) { + /* check if its an empty block */ + pos = (uint8_t *) *dscrptr; + for (i = 0; i < sector_size; i++, pos++) { + if (*pos) break; + } + if (i == sector_size) { + /* return no error but with no dscrptr */ + error = 0; + } + *dscrptr = NULL; + udf_puteccline(eccline); + return error; + } + + /* calculate descriptor size */ + dscrlen = udf_tagsize(*dscrptr, sector_size); + error = udf_check_tag_payload(*dscrptr, dscrlen); + if (error) { + *dscrptr = NULL; + udf_puteccline(eccline); + return error; + } + + /* we have a hold since it has a node descriptor */ + eccline->refcnt++; + udf_puteccline(eccline); + + return 0; +} + + +static int +udf_write_nodedscr_rmw(struct udf_strat_args *args) +{ + union dscrptr *dscrptr = args->dscr; + struct udf_mount *ump = args->ump; + struct long_ad *icb = args->icb; + struct udf_node *udf_node = args->udf_node; + struct udf_eccline *eccline; + uint64_t bit; + uint32_t sectornr, logsectornr, dummy; + // int waitfor = args->waitfor; + int sector_size = ump->discinfo.sector_size; + int lb_size = udf_rw32(ump->logical_vol->lb_size); + int error, eccsect; + + lb_size = lb_size; + KASSERT(sector_size == lb_size); + sectornr = 0; + error = udf_translate_vtop(ump, icb, §ornr, &dummy); + if (error) + return error; + + /* paranoia: add reference to the vnode to prevent recycling */ + vhold(udf_node->vnode); + + /* get our eccline */ + eccline = udf_geteccline(ump, sectornr, 0); + eccsect = sectornr - eccline->start_sector; + + bit = (uint64_t) 1 << eccsect; + + /* old callback still pending? */ + if (eccline->bufs[eccsect]) { + DPRINTF(WRITE, ("udf_write_nodedscr_rmw: writing descriptor" + " over buffer?\n")); + nestiobuf_done(eccline->bufs[eccsect], + eccline->bufs_len[eccsect], + 0); + eccline->bufs[eccsect] = NULL; + } + + /* set sector number in the descriptor and validate */ + dscrptr = (union dscrptr *) + (((uint8_t *) eccline->blob) + eccsect * sector_size); + KASSERT(dscrptr == args->dscr); + + logsectornr = udf_rw32(icb->loc.lb_num); + dscrptr->tag.tag_loc = udf_rw32(logsectornr); + udf_validate_tag_and_crc_sums(dscrptr); + + udf_fixup_node_internals(ump, (uint8_t *) dscrptr, UDF_C_NODE); + + /* set our flags */ + KASSERT(eccline->present & bit); + eccline->dirty |= bit; + + KASSERT(udf_tagsize(dscrptr, sector_size) <= sector_size); + + udf_node->outstanding_nodedscr--; + if (udf_node->outstanding_nodedscr == 0) { + /* XXX still using wakeup! */ + UDF_UNLOCK_NODE(udf_node, 0); + wakeup(&udf_node->outstanding_nodedscr); + } + holdrele(udf_node->vnode); + udf_puteccline(eccline); + + /* XXX waitfor not used */ + return 0; +} + + +static void +udf_queuebuf_rmw(struct udf_strat_args *args) +{ + struct udf_mount *ump = args->ump; + struct buf *buf = args->nestbuf; + struct desc_tag *tag; + struct strat_private *priv = PRIV(ump); + struct udf_eccline *eccline; + struct long_ad *node_ad_cpy; + uint64_t bit, *lmapping, *pmapping, *lmappos, *pmappos, blknr; + uint32_t buf_len, len, sectors, sectornr, our_sectornr; + uint32_t bpos; + uint16_t vpart_num; + uint8_t *fidblk, *src, *dst; + int sector_size = ump->discinfo.sector_size; + int blks = sector_size / DEV_BSIZE; + int eccsect, what, queue, error; + + KASSERT(ump); + KASSERT(buf); + KASSERT(buf->b_iodone == nestiobuf_iodone); + + blknr = buf->b_blkno; + our_sectornr = blknr / blks; + + what = buf->b_udf_c_type; + queue = UDF_SHED_READING; + if ((buf->b_flags & B_READ) == 0) { + /* writing */ + queue = UDF_SHED_SEQWRITING; + if (what == UDF_C_ABSOLUTE) + queue = UDF_SHED_WRITING; + if (what == UDF_C_DSCR) + queue = UDF_SHED_WRITING; + if (what == UDF_C_NODE) + queue = UDF_SHED_WRITING; + } + + if (queue == UDF_SHED_READING) { + DPRINTF(SHEDULE, ("\nudf_queuebuf_rmw READ %p : sector %d type %d," + "b_resid %d, b_bcount %d, b_bufsize %d\n", + buf, (uint32_t) buf->b_blkno / blks, buf->b_udf_c_type, + buf->b_resid, buf->b_bcount, buf->b_bufsize)); + + /* mark bits for reading */ + buf_len = buf->b_bcount; + sectornr = our_sectornr; + eccline = udf_geteccline(ump, sectornr, 0); + eccsect = sectornr - eccline->start_sector; + bpos = 0; + while (buf_len) { + len = MIN(buf_len, sector_size); + if ((eccsect < 0) || (eccsect >= ump->packet_size)) { + udf_puteccline(eccline); + eccline = udf_geteccline(ump, sectornr, 0); + eccsect = sectornr - eccline->start_sector; + } + bit = (uint64_t) 1 << eccsect; + error = eccline->error & bit ? EIO : 0; + if (eccline->present & bit) { + src = (uint8_t *) eccline->blob + + eccsect * sector_size; + dst = (uint8_t *) buf->b_data + bpos; + if (!error) + memcpy(dst, src, len); + nestiobuf_done(buf, len, error); + } else { + eccline->readin |= bit; + KASSERT(eccline->bufs[eccsect] == NULL); + eccline->bufs[eccsect] = buf; + eccline->bufs_bpos[eccsect] = bpos; + eccline->bufs_len[eccsect] = len; + } + bpos += sector_size; + eccsect++; + sectornr++; + buf_len -= len; + } + udf_puteccline(eccline); + return; + } + + if (queue == UDF_SHED_WRITING) { + DPRINTF(SHEDULE, ("\nudf_queuebuf_rmw WRITE %p : sector %d " + "type %d, b_resid %d, b_bcount %d, b_bufsize %d\n", + buf, (uint32_t) buf->b_blkno / blks, buf->b_udf_c_type, + buf->b_resid, buf->b_bcount, buf->b_bufsize)); + + /* if we have FIDs fixup using buffer's sector number(s) */ + if (buf->b_udf_c_type == UDF_C_FIDS) + panic("UDF_C_FIDS in SHED_WRITING!\n"); + + udf_fixup_node_internals(ump, buf->b_data, buf->b_udf_c_type); + + /* copy parts into the bufs and set for writing */ + buf_len = buf->b_bcount; + sectornr = our_sectornr; + eccline = udf_geteccline(ump, sectornr, 0); + eccsect = sectornr - eccline->start_sector; + bpos = 0; + while (buf_len) { + len = MIN(buf_len, sector_size); + if ((eccsect < 0) || (eccsect >= ump->packet_size)) { + udf_puteccline(eccline); + eccline = udf_geteccline(ump, sectornr, 0); + eccsect = sectornr - eccline->start_sector; + } + bit = (uint64_t) 1 << eccsect; + KASSERT((eccline->readin & bit) == 0); + eccline->present |= bit; + eccline->dirty |= bit; + if (eccline->bufs[eccsect]) { + /* old callback still pending */ + nestiobuf_done(eccline->bufs[eccsect], + eccline->bufs_len[eccsect], + 0); + eccline->bufs[eccsect] = NULL; + } + + src = (uint8_t *) buf->b_data + bpos; + dst = (uint8_t *) eccline->blob + eccsect * sector_size; + if (len != sector_size) + memset(dst, 0, sector_size); + memcpy(dst, src, len); + + /* note that its finished for this extent */ + eccline->bufs[eccsect] = NULL; + nestiobuf_done(buf, len, 0); + + bpos += sector_size; + eccsect++; + sectornr++; + buf_len -= len; + } + udf_puteccline(eccline); + return; + + } + + /* sequential writing */ + KASSERT(queue == UDF_SHED_SEQWRITING); + DPRINTF(SHEDULE, ("\nudf_queuebuf_rmw SEQWRITE %p : sector XXXX " + "type %d, b_resid %d, b_bcount %d, b_bufsize %d\n", + buf, buf->b_udf_c_type, buf->b_resid, buf->b_bcount, + buf->b_bufsize)); + /* + * Buffers should not have been allocated to disc addresses yet on + * this queue. Note that a buffer can get multiple extents allocated. + * Note that it *looks* like the normal writing but its different in + * the details. + * + * lmapping contains lb_num relative to base partition. + * + * XXX should we try to claim/organize the allocated memory to + * block-aligned pieces? + */ + mutex_enter(&priv->seqwrite_mutex); + + lmapping = ump->la_lmapping; + node_ad_cpy = ump->la_node_ad_cpy; + + /* logically allocate buf and map it in the file */ + udf_late_allocate_buf(ump, buf, lmapping, node_ad_cpy, &vpart_num); + + /* if we have FIDs, fixup using the new allocation table */ + if (buf->b_udf_c_type == UDF_C_FIDS) { + buf_len = buf->b_bcount; + bpos = 0; + lmappos = lmapping; + while (buf_len) { + sectornr = *lmappos++; + len = MIN(buf_len, sector_size); + fidblk = (uint8_t *) buf->b_data + bpos; + udf_fixup_fid_block(fidblk, sector_size, + 0, len, sectornr); + bpos += len; + buf_len -= len; + } + } + if (buf->b_udf_c_type == UDF_C_METADATA_SBM) { + if (buf->b_lblkno == 0) { + /* update the tag location inside */ + tag = (struct desc_tag *) buf->b_data; + tag->tag_loc = udf_rw32(*lmapping); + udf_validate_tag_and_crc_sums(buf->b_data); + } + } + udf_fixup_node_internals(ump, buf->b_data, buf->b_udf_c_type); + + /* + * Translate new mappings in lmapping to pmappings. + * pmapping to contain lb_nums as used for disc adressing. + */ + pmapping = ump->la_pmapping; + sectors = (buf->b_bcount + sector_size -1) / sector_size; + udf_translate_vtop_list(ump, sectors, vpart_num, lmapping, pmapping); + + /* copy parts into the bufs and set for writing */ + pmappos = pmapping; + buf_len = buf->b_bcount; + sectornr = *pmappos++; + eccline = udf_geteccline(ump, sectornr, ECC_SEQWRITING); + eccsect = sectornr - eccline->start_sector; + bpos = 0; + while (buf_len) { + len = MIN(buf_len, sector_size); + eccsect = sectornr - eccline->start_sector; + if ((eccsect < 0) || (eccsect >= ump->packet_size)) { + eccline->flags |= ECC_SEQWRITING; + udf_puteccline(eccline); + eccline = udf_geteccline(ump, sectornr, ECC_SEQWRITING); + eccsect = sectornr - eccline->start_sector; + } + bit = (uint64_t) 1 << eccsect; + KASSERT((eccline->readin & bit) == 0); + eccline->present |= bit; + eccline->dirty |= bit; + eccline->bufs[eccsect] = NULL; + + src = (uint8_t *) buf->b_data + bpos; + dst = (uint8_t *) + eccline->blob + eccsect * sector_size; + if (len != sector_size) + memset(dst, 0, sector_size); + memcpy(dst, src, len); + + /* note that its finished for this extent */ + nestiobuf_done(buf, len, 0); + + bpos += sector_size; + sectornr = *pmappos++; + buf_len -= len; + } + eccline->flags |= ECC_SEQWRITING; + udf_puteccline(eccline); + mutex_exit(&priv->seqwrite_mutex); +} + +/* --------------------------------------------------------------------- */ + +static void +udf_shedule_read_callback(struct buf *buf) +{ + struct udf_eccline *eccline = BTOE(buf); + struct udf_mount *ump = eccline->ump; + uint64_t bit; + uint8_t *src, *dst; + int sector_size = ump->discinfo.sector_size; + int error, i, len; + + DPRINTF(ECCLINE, ("read callback called on buf %p\n", buf)); + + /* post process read action */ + KASSERT(eccline->flags & ECC_LOCKED); + error = buf->b_error; + for (i = 0; i < ump->packet_size; i++) { + bit = (uint64_t) 1 << i; + src = (uint8_t *) buf->b_data + i * sector_size; + dst = (uint8_t *) eccline->blob + i * sector_size; + if (eccline->present & bit) + continue; + eccline->present |= bit; + if (error) + eccline->error |= bit; + if (eccline->bufs[i]) { + dst = (uint8_t *) eccline->bufs[i]->b_data + + eccline->bufs_bpos[i]; + len = eccline->bufs_len[i]; + if (!error) + memcpy(dst, src, len); + nestiobuf_done(eccline->bufs[i], len, error); + eccline->bufs[i] = NULL; + } + + } + KASSERT(buf->b_data == eccline->blob); + KASSERT(eccline->present == ((uint64_t) 1 << ump->packet_size)-1); + + /* + * XXX TODO what to do on read errors? read in all sectors + * synchronously and allocate a sparable entry? + */ + + udf_puteccline(eccline); + DPRINTF(ECCLINE, ("read callback finished\n")); +} + + +static void +udf_shedule_write_callback(struct buf *buf) +{ + struct udf_eccline *eccline = BTOE(buf); + struct udf_mount *ump = eccline->ump; + uint64_t bit; + int error, i; + + DPRINTF(ECCLINE, ("write callback called on buf %p\n", buf)); + + /* post process write action */ + KASSERT(eccline->flags & ECC_LOCKED); + error = buf->b_error; + for (i = 0; i < ump->packet_size; i++) { + bit = (uint64_t) 1 << i; + if ((eccline->dirty & bit) == 0) + continue; + if (error) { + eccline->error |= bit; + } else { + eccline->dirty &= ~bit; + } + + KASSERT(eccline->bufs[i] == 0); + } + KASSERT(eccline->dirty == 0); + KASSERT(error == 0); + + /* + * XXX TODO on write errors allocate a sparable entry and reissue + */ + + udf_puteccline(eccline); + DPRINTF(ECCLINE, ("write callback finished\n")); +} + + +static void +udf_issue_eccline(struct udf_eccline *eccline, int queued_on) +{ + struct udf_mount *ump = eccline->ump; + struct strat_private *priv = PRIV(ump); + struct buf *buf, *nestbuf; + uint64_t bit, allbits = ((uint64_t) 1 << ump->packet_size)-1; + uint32_t start; + int sector_size = ump->discinfo.sector_size; + int blks = sector_size / DEV_BSIZE; + int i; + + KASSERT(eccline->flags & ECC_LOCKED); + + if (queued_on == UDF_SHED_READING) { + DPRINTF(SHEDULE, ("udf_issue_eccline reading : ")); + /* read all bits that are not yet present */ + eccline->readin = (~eccline->present) & allbits; + KASSERT(eccline->readin); + start = eccline->start_sector; + buf = eccline->buf; + buf->b_flags = B_READ | B_ASYNC; + SET(buf->b_cflags, BC_BUSY); /* mark buffer busy */ + buf->b_oflags = 0; + buf->b_iodone = udf_shedule_read_callback; + buf->b_data = eccline->blob; + buf->b_bcount = ump->packet_size * sector_size; + buf->b_resid = buf->b_bcount; + buf->b_bufsize = buf->b_bcount; + buf->b_private = eccline; + BIO_SETPRIO(buf, BPRIO_DEFAULT); + buf->b_lblkno = buf->b_blkno = buf->b_rawblkno = start * blks; + buf->b_proc = NULL; + + if (eccline->present != 0) { + for (i = 0; i < ump->packet_size; i++) { + bit = (uint64_t) 1 << i; + if (eccline->present & bit) { + nestiobuf_done(buf, sector_size, 0); + continue; + } + nestbuf = getiobuf(NULL, true); + nestiobuf_setup(buf, nestbuf, i * sector_size, + sector_size); + /* adjust blocknumber to read */ + nestbuf->b_blkno = buf->b_blkno + i*blks; + nestbuf->b_rawblkno = buf->b_rawblkno + i*blks; + + DPRINTF(SHEDULE, ("sector %d ", start + i)); + + /* mutex dance since it could lock */ + mutex_exit(&priv->discstrat_mutex); + /* call asynchronous */ + VOP_STRATEGY(ump->devvp, nestbuf); + mutex_enter(&priv->discstrat_mutex); + } + DPRINTF(SHEDULE, ("\n")); + return; + } + } else { + /* write or seqwrite */ + DPRINTF(SHEDULE, ("udf_issue_eccline writing or seqwriting : ")); + DPRINTF(SHEDULE, ("\n\tpresent %"PRIx64", readin %"PRIx64", " + "dirty %"PRIx64"\n\t", eccline->present, eccline->readin, + eccline->dirty)); + KASSERT(eccline->present == allbits); + + start = eccline->start_sector; + buf = eccline->buf; + buf->b_flags = B_WRITE | B_ASYNC; + SET(buf->b_cflags, BC_BUSY); /* mark buffer busy */ + buf->b_oflags = 0; + buf->b_iodone = udf_shedule_write_callback; + buf->b_data = eccline->blob; + buf->b_bcount = ump->packet_size * sector_size; + buf->b_resid = buf->b_bcount; + buf->b_bufsize = buf->b_bcount; + buf->b_private = eccline; + BIO_SETPRIO(buf, BPRIO_DEFAULT); + buf->b_lblkno = buf->b_blkno = buf->b_rawblkno = start * blks; + buf->b_proc = NULL; + } + + /* mutex dance since it could lock */ + mutex_exit(&priv->discstrat_mutex); + /* call asynchronous */ + DPRINTF(SHEDULE, ("sector %d for %d\n", + start, ump->packet_size)); + VOP_STRATEGY(ump->devvp, buf); + mutex_enter(&priv->discstrat_mutex); +} + + +static void +udf_discstrat_thread(void *arg) +{ + struct udf_mount *ump = (struct udf_mount *) arg; + struct strat_private *priv = PRIV(ump); + struct udf_eccline *eccline; + struct timespec now, *last; + uint64_t allbits = ((uint64_t) 1 << ump->packet_size)-1; + int new_queue, wait, work; + + work = 1; + priv->thread_running = 1; + mutex_enter(&priv->discstrat_mutex); + priv->num_floating = 0; + while (priv->run_thread || work || priv->num_floating) { + /* get our time */ + vfs_timestamp(&now); + + /* maintenance: handle eccline state machine */ + for(;;) { + /* only peek at it */ + eccline = udf_peek_eccline(priv, UDF_SHED_WAITING); + if (eccline == NULL) + break; + + /* if not reading, wait until the time has come */ + if ((priv->cur_queue != UDF_SHED_READING) && + (eccline->wait_time.tv_sec - now.tv_sec > 0)) { + UDF_UNLOCK_ECCLINE(eccline); + /* all others are later, so break off */ + break; + } + + /* release */ + UDF_UNLOCK_ECCLINE(eccline); + + /* do get it */ + eccline = udf_pop_eccline(priv, UDF_SHED_WAITING); + + /* requeue according to state */ + new_queue = UDF_SHED_FREE; /* unlikely */ + if (eccline->refcnt > 0) + new_queue = UDF_SHED_IDLE; + if (eccline->flags & ECC_WANTED) + new_queue = UDF_SHED_IDLE; + if (eccline->readin) + new_queue = UDF_SHED_READING; + if (eccline->dirty) { + new_queue = UDF_SHED_READING; + if (eccline->present == allbits) { + new_queue = UDF_SHED_WRITING; + if (eccline->flags & ECC_SEQWRITING) + new_queue = UDF_SHED_SEQWRITING; + } + } + udf_push_eccline(eccline, new_queue); + } + + /* maintenance: free excess ecclines */ + while (priv->num_queued[UDF_SHED_FREE] > UDF_ECCLINE_MAXFREE) { + eccline = udf_pop_eccline(priv, UDF_SHED_FREE); + KASSERT(eccline); + KASSERT(eccline->refcnt == 0); + if (eccline->flags & ECC_WANTED) { + /* we won the race, but we dont want to win */ + DPRINTF(ECCLINE, ("Tried removing, pushed back to free list\n")); + udf_push_eccline(eccline, UDF_SHED_IDLE); + } else { + DPRINTF(ECCLINE, ("Removing entry from free list\n")); + udf_dispose_eccline(eccline); + } + } + + /* process the current selected queue */ + /* get our time */ + vfs_timestamp(&now); + last = &priv->last_queued[priv->cur_queue]; + + /* get our line */ + eccline = udf_pop_eccline(priv, priv->cur_queue); + if (eccline) { + wait = 0; + new_queue = priv->cur_queue; + DPRINTF(ECCLINE, ("UDF_ISSUE_ECCLINE\n")); + + udf_issue_eccline(eccline, priv->cur_queue); + } else { + /* don't switch too quickly */ + if (now.tv_sec - last->tv_sec < 2) { + /* wait some time */ + cv_timedwait(&priv->discstrat_cv, + &priv->discstrat_mutex, hz); + /* we assume there is work to be done */ + work = 1; + continue; + } + + /* XXX select on queue lengths ? */ + wait = 1; + /* check if we can/should switch */ + new_queue = priv->cur_queue; + if (bufq_peek(priv->queues[UDF_SHED_READING])) + new_queue = UDF_SHED_READING; + if (bufq_peek(priv->queues[UDF_SHED_WRITING])) + new_queue = UDF_SHED_WRITING; + if (bufq_peek(priv->queues[UDF_SHED_SEQWRITING])) + new_queue = UDF_SHED_SEQWRITING; + } + + /* give room */ + mutex_exit(&priv->discstrat_mutex); + + if (new_queue != priv->cur_queue) { + wait = 0; + DPRINTF(SHEDULE, ("switching from %d to %d\n", + priv->cur_queue, new_queue)); + priv->cur_queue = new_queue; + } + mutex_enter(&priv->discstrat_mutex); + + /* wait for more if needed */ + if (wait) + cv_timedwait(&priv->discstrat_cv, + &priv->discstrat_mutex, hz/4); /* /8 */ + + work = (bufq_peek(priv->queues[UDF_SHED_WAITING]) != NULL); + work |= (bufq_peek(priv->queues[UDF_SHED_READING]) != NULL); + work |= (bufq_peek(priv->queues[UDF_SHED_WRITING]) != NULL); + work |= (bufq_peek(priv->queues[UDF_SHED_SEQWRITING]) != NULL); + + DPRINTF(PARANOIA, ("work : (%d, %d, %d) -> work %d, float %d\n", + (bufq_peek(priv->queues[UDF_SHED_READING]) != NULL), + (bufq_peek(priv->queues[UDF_SHED_WRITING]) != NULL), + (bufq_peek(priv->queues[UDF_SHED_SEQWRITING]) != NULL), + work, priv->num_floating)); + } + + mutex_exit(&priv->discstrat_mutex); + + /* tear down remaining ecclines */ + mutex_enter(&priv->discstrat_mutex); + KASSERT(bufq_peek(priv->queues[UDF_SHED_WAITING]) == NULL); + KASSERT(bufq_peek(priv->queues[UDF_SHED_IDLE]) == NULL); + KASSERT(bufq_peek(priv->queues[UDF_SHED_READING]) == NULL); + KASSERT(bufq_peek(priv->queues[UDF_SHED_WRITING]) == NULL); + KASSERT(bufq_peek(priv->queues[UDF_SHED_SEQWRITING]) == NULL); + + KASSERT(priv->num_queued[UDF_SHED_WAITING] == 0); + KASSERT(priv->num_queued[UDF_SHED_IDLE] == 0); + KASSERT(priv->num_queued[UDF_SHED_READING] == 0); + KASSERT(priv->num_queued[UDF_SHED_WRITING] == 0); + KASSERT(priv->num_queued[UDF_SHED_SEQWRITING] == 0); + + eccline = udf_pop_eccline(priv, UDF_SHED_FREE); + while (eccline) { + udf_dispose_eccline(eccline); + eccline = udf_pop_eccline(priv, UDF_SHED_FREE); + } + KASSERT(priv->num_queued[UDF_SHED_FREE] == 0); + mutex_exit(&priv->discstrat_mutex); + + priv->thread_running = 0; + priv->thread_finished = 1; + wakeup(&priv->run_thread); + kthread_exit(0); + /* not reached */ +} + +/* --------------------------------------------------------------------- */ + +/* + * Buffer memory pool allocator. + */ + +static void * +ecclinepool_page_alloc(struct pool *pp, int flags) +{ + return (void *)uvm_km_alloc(kernel_map, + MAXBSIZE, MAXBSIZE, + ((flags & PR_WAITOK) ? 0 : UVM_KMF_NOWAIT | UVM_KMF_TRYLOCK) + | UVM_KMF_WIRED /* UVM_KMF_PAGABLE? */); +} + +static void +ecclinepool_page_free(struct pool *pp, void *v) +{ + uvm_km_free(kernel_map, (vaddr_t)v, MAXBSIZE, UVM_KMF_WIRED); +} + +static struct pool_allocator ecclinepool_allocator = { + .pa_alloc = ecclinepool_page_alloc, + .pa_free = ecclinepool_page_free, + .pa_pagesz = MAXBSIZE, +}; + + +static void +udf_discstrat_init_rmw(struct udf_strat_args *args) +{ + struct udf_mount *ump = args->ump; + struct strat_private *priv = PRIV(ump); + uint32_t lb_size, blobsize, hashline; + int i; + + KASSERT(ump); + KASSERT(ump->logical_vol); + KASSERT(priv == NULL); + + lb_size = udf_rw32(ump->logical_vol->lb_size); + blobsize = ump->packet_size * lb_size; + KASSERT(lb_size > 0); + KASSERT(ump->packet_size <= 64); + + /* initialise our memory space */ + ump->strategy_private = malloc(sizeof(struct strat_private), + M_UDFTEMP, M_WAITOK); + priv = ump->strategy_private; + memset(priv, 0 , sizeof(struct strat_private)); + + /* initialise locks */ + cv_init(&priv->discstrat_cv, "udfstrat"); + mutex_init(&priv->discstrat_mutex, MUTEX_DEFAULT, IPL_NONE); + mutex_init(&priv->seqwrite_mutex, MUTEX_DEFAULT, IPL_NONE); + + /* initialise struct eccline pool */ + pool_init(&priv->eccline_pool, sizeof(struct udf_eccline), + 0, 0, 0, "udf_eccline_pool", NULL, IPL_NONE); + + /* initialise eccline blob pool */ + ecclinepool_allocator.pa_pagesz = blobsize; + pool_init(&priv->ecclineblob_pool, blobsize, + 0, 0, 0, "udf_eccline_blob", &ecclinepool_allocator, IPL_NONE); + + /* initialise main queues */ + for (i = 0; i < UDF_SHED_MAX; i++) { + priv->num_queued[i] = 0; + vfs_timestamp(&priv->last_queued[i]); + } + bufq_alloc(&priv->queues[UDF_SHED_WAITING], "fcfs", + BUFQ_SORT_RAWBLOCK); + bufq_alloc(&priv->queues[UDF_SHED_READING], "disksort", + BUFQ_SORT_RAWBLOCK); + bufq_alloc(&priv->queues[UDF_SHED_WRITING], "disksort", + BUFQ_SORT_RAWBLOCK); + bufq_alloc(&priv->queues[UDF_SHED_SEQWRITING], "disksort", 0); + + /* initialise administrative queues */ + bufq_alloc(&priv->queues[UDF_SHED_IDLE], "fcfs", 0); + bufq_alloc(&priv->queues[UDF_SHED_FREE], "fcfs", 0); + + for (hashline = 0; hashline < UDF_ECCBUF_HASHSIZE; hashline++) { + LIST_INIT(&priv->eccline_hash[hashline]); + } + + /* create our disk strategy thread */ + priv->cur_queue = UDF_SHED_READING; + priv->thread_finished = 0; + priv->thread_running = 0; + priv->run_thread = 1; + if (kthread_create(PRI_NONE, 0 /* KTHREAD_MPSAFE*/, NULL /* cpu_info*/, + udf_discstrat_thread, ump, &priv->queue_lwp, + "%s", "udf_rw")) { + panic("fork udf_rw"); + } + + /* wait for thread to spin up */ + while (!priv->thread_running) { + tsleep(&priv->thread_running, PRIBIO+1, "udfshedstart", hz); + } +} + + +static void +udf_discstrat_finish_rmw(struct udf_strat_args *args) +{ + struct udf_mount *ump = args->ump; + struct strat_private *priv = PRIV(ump); + + if (ump == NULL) + return; + + /* stop our sheduling thread */ + KASSERT(priv->run_thread == 1); + priv->run_thread = 0; + wakeup(priv->queue_lwp); + while (!priv->thread_finished) { + tsleep(&priv->run_thread, PRIBIO + 1, "udfshedfin", hz); + } + /* kthread should be finished now */ + + /* cleanup our pools */ + pool_destroy(&priv->eccline_pool); + pool_destroy(&priv->ecclineblob_pool); + + cv_destroy(&priv->discstrat_cv); + mutex_destroy(&priv->discstrat_mutex); + mutex_destroy(&priv->seqwrite_mutex); + + /* free our private space */ + free(ump->strategy_private, M_UDFTEMP); + ump->strategy_private = NULL; +} + +/* --------------------------------------------------------------------- */ + +struct udf_strategy udf_strat_rmw = +{ + udf_create_nodedscr_rmw, + udf_free_nodedscr_rmw, + udf_read_nodedscr_rmw, + udf_write_nodedscr_rmw, + udf_queuebuf_rmw, + udf_discstrat_init_rmw, + udf_discstrat_finish_rmw +}; + diff --git a/sys/fs/udf/udf_strat_sequential.c b/sys/fs/udf/udf_strat_sequential.c new file mode 100644 index 000000000..0600b6a48 --- /dev/null +++ b/sys/fs/udf/udf_strat_sequential.c @@ -0,0 +1,687 @@ +/* $NetBSD: udf_strat_sequential.c,v 1.12 2013/10/18 19:56:55 christos Exp $ */ + +/* + * Copyright (c) 2006, 2008 Reinoud Zandijk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#ifndef lint +__KERNEL_RCSID(0, "$NetBSD: udf_strat_sequential.c,v 1.12 2013/10/18 19:56:55 christos Exp $"); +#endif /* not lint */ + + +#if defined(_KERNEL_OPT) +#include "opt_compat_netbsd.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "udf.h" +#include "udf_subr.h" +#include "udf_bswap.h" + + +#define VTOI(vnode) ((struct udf_node *) vnode->v_data) +#define PRIV(ump) ((struct strat_private *) ump->strategy_private) + +/* --------------------------------------------------------------------- */ + +/* BUFQ's */ +#define UDF_SHED_MAX 3 + +#define UDF_SHED_READING 0 +#define UDF_SHED_WRITING 1 +#define UDF_SHED_SEQWRITING 2 + +struct strat_private { + struct pool desc_pool; /* node descriptors */ + + lwp_t *queue_lwp; + kcondvar_t discstrat_cv; /* to wait on */ + kmutex_t discstrat_mutex; /* disc strategy */ + + int run_thread; /* thread control */ + int cur_queue; + + struct disk_strategy old_strategy_setting; + struct bufq_state *queues[UDF_SHED_MAX]; + struct timespec last_queued[UDF_SHED_MAX]; +}; + + +/* --------------------------------------------------------------------- */ + +static void +udf_wr_nodedscr_callback(struct buf *buf) +{ + struct udf_node *udf_node; + + KASSERT(buf); + KASSERT(buf->b_data); + + /* called when write action is done */ + DPRINTF(WRITE, ("udf_wr_nodedscr_callback(): node written out\n")); + + udf_node = VTOI(buf->b_vp); + if (udf_node == NULL) { + putiobuf(buf); + printf("udf_wr_node_callback: NULL node?\n"); + return; + } + + /* XXX right flags to mark dirty again on error? */ + if (buf->b_error) { + udf_node->i_flags |= IN_MODIFIED | IN_ACCESSED; + /* XXX TODO reshedule on error */ + } + + /* decrement outstanding_nodedscr */ + KASSERT(udf_node->outstanding_nodedscr >= 1); + udf_node->outstanding_nodedscr--; + if (udf_node->outstanding_nodedscr == 0) { + /* first unlock the node */ + UDF_UNLOCK_NODE(udf_node, 0); + wakeup(&udf_node->outstanding_nodedscr); + } + + /* unreference the vnode so it can be recycled */ + holdrele(udf_node->vnode); + + putiobuf(buf); +} + +/* --------------------------------------------------------------------- */ + +static int +udf_create_logvol_dscr_seq(struct udf_strat_args *args) +{ + union dscrptr **dscrptr = &args->dscr; + struct udf_mount *ump = args->ump; + struct strat_private *priv = PRIV(ump); + uint32_t lb_size; + + lb_size = udf_rw32(ump->logical_vol->lb_size); + *dscrptr = pool_get(&priv->desc_pool, PR_WAITOK); + memset(*dscrptr, 0, lb_size); + + return 0; +} + + +static void +udf_free_logvol_dscr_seq(struct udf_strat_args *args) +{ + union dscrptr *dscr = args->dscr; + struct udf_mount *ump = args->ump; + struct strat_private *priv = PRIV(ump); + + pool_put(&priv->desc_pool, dscr); +} + + +static int +udf_read_logvol_dscr_seq(struct udf_strat_args *args) +{ + union dscrptr **dscrptr = &args->dscr; + union dscrptr *tmpdscr; + struct udf_mount *ump = args->ump; + struct long_ad *icb = args->icb; + struct strat_private *priv = PRIV(ump); + uint32_t lb_size; + uint32_t sector, dummy; + int error; + + lb_size = udf_rw32(ump->logical_vol->lb_size); + + error = udf_translate_vtop(ump, icb, §or, &dummy); + if (error) + return error; + + /* try to read in fe/efe */ + error = udf_read_phys_dscr(ump, sector, M_UDFTEMP, &tmpdscr); + if (error) + return error; + + *dscrptr = pool_get(&priv->desc_pool, PR_WAITOK); + memcpy(*dscrptr, tmpdscr, lb_size); + free(tmpdscr, M_UDFTEMP); + + return 0; +} + + +static int +udf_write_logvol_dscr_seq(struct udf_strat_args *args) +{ + union dscrptr *dscr = args->dscr; + struct udf_mount *ump = args->ump; + struct udf_node *udf_node = args->udf_node; + struct long_ad *icb = args->icb; + int waitfor = args->waitfor; + uint32_t logsectornr, sectornr, dummy; + int error, vpart; + + /* + * we have to decide if we write it out sequential or at its fixed + * position by examining the partition its (to be) written on. + */ + vpart = udf_rw16(udf_node->loc.loc.part_num); + logsectornr = udf_rw32(icb->loc.lb_num); + sectornr = 0; + if (ump->vtop_tp[vpart] != UDF_VTOP_TYPE_VIRT) { + error = udf_translate_vtop(ump, icb, §ornr, &dummy); + if (error) + goto out; + } + + /* add reference to the vnode to prevent recycling */ + vhold(udf_node->vnode); + + if (waitfor) { + DPRINTF(WRITE, ("udf_write_logvol_dscr: sync write\n")); + + error = udf_write_phys_dscr_sync(ump, udf_node, UDF_C_NODE, + dscr, sectornr, logsectornr); + } else { + DPRINTF(WRITE, ("udf_write_logvol_dscr: no wait, async write\n")); + + error = udf_write_phys_dscr_async(ump, udf_node, UDF_C_NODE, + dscr, sectornr, logsectornr, udf_wr_nodedscr_callback); + /* will be UNLOCKED in call back */ + return error; + } + + holdrele(udf_node->vnode); +out: + udf_node->outstanding_nodedscr--; + if (udf_node->outstanding_nodedscr == 0) { + UDF_UNLOCK_NODE(udf_node, 0); + wakeup(&udf_node->outstanding_nodedscr); + } + + return error; +} + +/* --------------------------------------------------------------------- */ + +/* + * Main file-system specific sheduler. Due to the nature of optical media + * sheduling can't be performed in the traditional way. Most OS + * implementations i've seen thus read or write a file atomically giving all + * kinds of side effects. + * + * This implementation uses a kernel thread to shedule the queued requests in + * such a way that is semi-optimal for optical media; this means aproximately + * (R*|(Wr*|Ws*))* since switching between reading and writing is expensive in + * time. + */ + +static void +udf_queuebuf_seq(struct udf_strat_args *args) +{ + struct udf_mount *ump = args->ump; + struct buf *nestbuf = args->nestbuf; + struct strat_private *priv = PRIV(ump); + int queue; + int what; + + KASSERT(ump); + KASSERT(nestbuf); + KASSERT(nestbuf->b_iodone == nestiobuf_iodone); + + what = nestbuf->b_udf_c_type; + queue = UDF_SHED_READING; + if ((nestbuf->b_flags & B_READ) == 0) { + /* writing */ + queue = UDF_SHED_SEQWRITING; + if (what == UDF_C_ABSOLUTE) + queue = UDF_SHED_WRITING; + } + + /* use our own sheduler lists for more complex sheduling */ + mutex_enter(&priv->discstrat_mutex); + bufq_put(priv->queues[queue], nestbuf); + vfs_timestamp(&priv->last_queued[queue]); + mutex_exit(&priv->discstrat_mutex); + + /* signal our thread that there might be something to do */ + cv_signal(&priv->discstrat_cv); +} + +/* --------------------------------------------------------------------- */ + +/* TODO convert to lb_size */ +static void +udf_VAT_mapping_update(struct udf_mount *ump, struct buf *buf, uint32_t lb_map) +{ + union dscrptr *fdscr = (union dscrptr *) buf->b_data; + struct vnode *vp = buf->b_vp; + struct udf_node *udf_node = VTOI(vp); + uint32_t lb_num; + uint32_t udf_rw32_lbmap; + int c_type = buf->b_udf_c_type; + int error; + + /* only interested when we're using a VAT */ + KASSERT(ump->vat_node); + KASSERT(ump->vtop_alloc[ump->node_part] == UDF_ALLOC_VAT); + + /* only nodes are recorded in the VAT */ + /* NOTE: and the fileset descriptor (FIXME ?) */ + if (c_type != UDF_C_NODE) + return; + + udf_rw32_lbmap = udf_rw32(lb_map); + + /* if we're the VAT itself, only update our assigned sector number */ + if (udf_node == ump->vat_node) { + fdscr->tag.tag_loc = udf_rw32_lbmap; + udf_validate_tag_sum(fdscr); + DPRINTF(TRANSLATE, ("VAT assigned to sector %u\n", + udf_rw32(udf_rw32_lbmap))); + /* no use mapping the VAT node in the VAT */ + return; + } + + /* record new position in VAT file */ + lb_num = udf_rw32(fdscr->tag.tag_loc); + + /* lb_num = udf_rw32(udf_node->write_loc.loc.lb_num); */ + + DPRINTF(TRANSLATE, ("VAT entry change (log %u -> phys %u)\n", + lb_num, lb_map)); + + /* VAT should be the longer than this write, can't go wrong */ + KASSERT(lb_num <= ump->vat_entries); + + mutex_enter(&ump->allocate_mutex); + error = udf_vat_write(ump->vat_node, + (uint8_t *) &udf_rw32_lbmap, 4, + ump->vat_offset + lb_num * 4); + mutex_exit(&ump->allocate_mutex); + + if (error) + panic( "udf_VAT_mapping_update: HELP! i couldn't " + "write in the VAT file ?\n"); +} + + +static void +udf_issue_buf(struct udf_mount *ump, int queue, struct buf *buf) +{ + union dscrptr *dscr; + struct long_ad *node_ad_cpy; + struct part_desc *pdesc; + uint64_t *lmapping, *lmappos; + uint32_t sectornr, bpos; + uint32_t ptov; + uint16_t vpart_num; + uint8_t *fidblk; + int sector_size = ump->discinfo.sector_size; + int blks = sector_size / DEV_BSIZE; + int len, buf_len; + + /* if reading, just pass to the device's STRATEGY */ + if (queue == UDF_SHED_READING) { + DPRINTF(SHEDULE, ("\nudf_issue_buf READ %p : sector %d type %d," + "b_resid %d, b_bcount %d, b_bufsize %d\n", + buf, (uint32_t) buf->b_blkno / blks, buf->b_udf_c_type, + buf->b_resid, buf->b_bcount, buf->b_bufsize)); + VOP_STRATEGY(ump->devvp, buf); + return; + } + + if (queue == UDF_SHED_WRITING) { + DPRINTF(SHEDULE, ("\nudf_issue_buf WRITE %p : sector %d " + "type %d, b_resid %d, b_bcount %d, b_bufsize %d\n", + buf, (uint32_t) buf->b_blkno / blks, buf->b_udf_c_type, + buf->b_resid, buf->b_bcount, buf->b_bufsize)); + KASSERT(buf->b_udf_c_type == UDF_C_ABSOLUTE); + + // udf_fixup_node_internals(ump, buf->b_data, buf->b_udf_c_type); + VOP_STRATEGY(ump->devvp, buf); + return; + } + + KASSERT(queue == UDF_SHED_SEQWRITING); + DPRINTF(SHEDULE, ("\nudf_issue_buf SEQWRITE %p : sector XXXX " + "type %d, b_resid %d, b_bcount %d, b_bufsize %d\n", + buf, buf->b_udf_c_type, buf->b_resid, buf->b_bcount, + buf->b_bufsize)); + + /* + * Buffers should not have been allocated to disc addresses yet on + * this queue. Note that a buffer can get multiple extents allocated. + * + * lmapping contains lb_num relative to base partition. + */ + lmapping = ump->la_lmapping; + node_ad_cpy = ump->la_node_ad_cpy; + + /* logically allocate buf and map it in the file */ + udf_late_allocate_buf(ump, buf, lmapping, node_ad_cpy, &vpart_num); + + /* + * NOTE We are using the knowledge here that sequential media will + * always be mapped linearly. Thus no use to explicitly translate the + * lmapping list. + */ + + /* calculate offset from physical base partition */ + pdesc = ump->partitions[ump->vtop[vpart_num]]; + ptov = udf_rw32(pdesc->start_loc); + + /* set buffers blkno to the physical block number */ + buf->b_blkno = (*lmapping + ptov) * blks; + + /* fixate floating descriptors */ + if (buf->b_udf_c_type == UDF_C_FLOAT_DSCR) { + /* set our tag location to the absolute position */ + dscr = (union dscrptr *) buf->b_data; + dscr->tag.tag_loc = udf_rw32(*lmapping + ptov); + udf_validate_tag_and_crc_sums(dscr); + } + + /* update mapping in the VAT */ + if (buf->b_udf_c_type == UDF_C_NODE) { + udf_VAT_mapping_update(ump, buf, *lmapping); + udf_fixup_node_internals(ump, buf->b_data, buf->b_udf_c_type); + } + + /* if we have FIDs, fixup using the new allocation table */ + if (buf->b_udf_c_type == UDF_C_FIDS) { + buf_len = buf->b_bcount; + bpos = 0; + lmappos = lmapping; + while (buf_len) { + sectornr = *lmappos++; + len = MIN(buf_len, sector_size); + fidblk = (uint8_t *) buf->b_data + bpos; + udf_fixup_fid_block(fidblk, sector_size, + 0, len, sectornr); + bpos += len; + buf_len -= len; + } + } + + VOP_STRATEGY(ump->devvp, buf); +} + + +static void +udf_doshedule(struct udf_mount *ump) +{ + struct buf *buf; + struct timespec now, *last; + struct strat_private *priv = PRIV(ump); + void (*b_callback)(struct buf *); + int new_queue; + int error; + + buf = bufq_get(priv->queues[priv->cur_queue]); + if (buf) { + /* transfer from the current queue to the device queue */ + mutex_exit(&priv->discstrat_mutex); + + /* transform buffer to synchronous; XXX needed? */ + b_callback = buf->b_iodone; + buf->b_iodone = NULL; + CLR(buf->b_flags, B_ASYNC); + + /* issue and wait on completion */ + udf_issue_buf(ump, priv->cur_queue, buf); + biowait(buf); + + mutex_enter(&priv->discstrat_mutex); + + /* if there is an error, repair this error, otherwise propagate */ + if (buf->b_error && ((buf->b_flags & B_READ) == 0)) { + /* check what we need to do */ + panic("UDF write error, can't handle yet!\n"); + } + + /* propagate result to higher layers */ + if (b_callback) { + buf->b_iodone = b_callback; + (*buf->b_iodone)(buf); + } + + return; + } + + /* Check if we're idling in this state */ + vfs_timestamp(&now); + last = &priv->last_queued[priv->cur_queue]; + if (ump->discinfo.mmc_class == MMC_CLASS_CD) { + /* dont switch too fast for CD media; its expensive in time */ + if (now.tv_sec - last->tv_sec < 3) + return; + } + + /* check if we can/should switch */ + new_queue = priv->cur_queue; + + if (bufq_peek(priv->queues[UDF_SHED_READING])) + new_queue = UDF_SHED_READING; + if (bufq_peek(priv->queues[UDF_SHED_WRITING])) /* only for unmount */ + new_queue = UDF_SHED_WRITING; + if (bufq_peek(priv->queues[UDF_SHED_SEQWRITING])) + new_queue = UDF_SHED_SEQWRITING; + if (priv->cur_queue == UDF_SHED_READING) { + if (new_queue == UDF_SHED_SEQWRITING) { + /* TODO use flag to signal if this is needed */ + mutex_exit(&priv->discstrat_mutex); + + /* update trackinfo for data and metadata */ + error = udf_update_trackinfo(ump, + &ump->data_track); + assert(error == 0); + error = udf_update_trackinfo(ump, + &ump->metadata_track); + assert(error == 0); + mutex_enter(&priv->discstrat_mutex); + } + } + + if (new_queue != priv->cur_queue) { + DPRINTF(SHEDULE, ("switching from %d to %d\n", + priv->cur_queue, new_queue)); + } + + priv->cur_queue = new_queue; +} + + +static void +udf_discstrat_thread(void *arg) +{ + struct udf_mount *ump = (struct udf_mount *) arg; + struct strat_private *priv = PRIV(ump); + int empty; + + empty = 1; + mutex_enter(&priv->discstrat_mutex); + while (priv->run_thread || !empty) { + /* process the current selected queue */ + udf_doshedule(ump); + empty = (bufq_peek(priv->queues[UDF_SHED_READING]) == NULL); + empty &= (bufq_peek(priv->queues[UDF_SHED_WRITING]) == NULL); + empty &= (bufq_peek(priv->queues[UDF_SHED_SEQWRITING]) == NULL); + + /* wait for more if needed */ + if (empty) + cv_timedwait(&priv->discstrat_cv, + &priv->discstrat_mutex, hz/8); + } + mutex_exit(&priv->discstrat_mutex); + + wakeup(&priv->run_thread); + kthread_exit(0); + /* not reached */ +} + +/* --------------------------------------------------------------------- */ + +static void +udf_discstrat_init_seq(struct udf_strat_args *args) +{ + struct udf_mount *ump = args->ump; + struct strat_private *priv = PRIV(ump); + struct disk_strategy dkstrat; + uint32_t lb_size; + + KASSERT(ump); + KASSERT(ump->logical_vol); + KASSERT(priv == NULL); + + lb_size = udf_rw32(ump->logical_vol->lb_size); + KASSERT(lb_size > 0); + + /* initialise our memory space */ + ump->strategy_private = malloc(sizeof(struct strat_private), + M_UDFTEMP, M_WAITOK); + priv = ump->strategy_private; + memset(priv, 0 , sizeof(struct strat_private)); + + /* initialise locks */ + cv_init(&priv->discstrat_cv, "udfstrat"); + mutex_init(&priv->discstrat_mutex, MUTEX_DEFAULT, IPL_NONE); + + /* + * Initialise pool for descriptors associated with nodes. This is done + * in lb_size units though currently lb_size is dictated to be + * sector_size. + */ + pool_init(&priv->desc_pool, lb_size, 0, 0, 0, "udf_desc_pool", NULL, + IPL_NONE); + + /* + * remember old device strategy method and explicit set method + * `discsort' since we have our own more complex strategy that is not + * implementable on the CD device and other strategies will get in the + * way. + */ + memset(&priv->old_strategy_setting, 0, + sizeof(struct disk_strategy)); + VOP_IOCTL(ump->devvp, DIOCGSTRATEGY, &priv->old_strategy_setting, + FREAD | FKIOCTL, NOCRED); + memset(&dkstrat, 0, sizeof(struct disk_strategy)); + strcpy(dkstrat.dks_name, "discsort"); + VOP_IOCTL(ump->devvp, DIOCSSTRATEGY, &dkstrat, FWRITE | FKIOCTL, + NOCRED); + + /* initialise our internal sheduler */ + priv->cur_queue = UDF_SHED_READING; + bufq_alloc(&priv->queues[UDF_SHED_READING], "disksort", + BUFQ_SORT_RAWBLOCK); + bufq_alloc(&priv->queues[UDF_SHED_WRITING], "disksort", + BUFQ_SORT_RAWBLOCK); + bufq_alloc(&priv->queues[UDF_SHED_SEQWRITING], "fcfs", 0); + vfs_timestamp(&priv->last_queued[UDF_SHED_READING]); + vfs_timestamp(&priv->last_queued[UDF_SHED_WRITING]); + vfs_timestamp(&priv->last_queued[UDF_SHED_SEQWRITING]); + + /* create our disk strategy thread */ + priv->run_thread = 1; + if (kthread_create(PRI_NONE, 0 /* KTHREAD_MPSAFE*/, NULL /* cpu_info*/, + udf_discstrat_thread, ump, &priv->queue_lwp, + "%s", "udf_rw")) { + panic("fork udf_rw"); + } +} + + +static void +udf_discstrat_finish_seq(struct udf_strat_args *args) +{ + struct udf_mount *ump = args->ump; + struct strat_private *priv = PRIV(ump); + int error; + + if (ump == NULL) + return; + + /* stop our sheduling thread */ + KASSERT(priv->run_thread == 1); + priv->run_thread = 0; + wakeup(priv->queue_lwp); + do { + error = tsleep(&priv->run_thread, PRIBIO+1, + "udfshedfin", hz); + } while (error); + /* kthread should be finished now */ + + /* set back old device strategy method */ + VOP_IOCTL(ump->devvp, DIOCSSTRATEGY, &priv->old_strategy_setting, + FWRITE, NOCRED); + + /* destroy our pool */ + pool_destroy(&priv->desc_pool); + + mutex_destroy(&priv->discstrat_mutex); + cv_destroy(&priv->discstrat_cv); + + /* free our private space */ + free(ump->strategy_private, M_UDFTEMP); + ump->strategy_private = NULL; +} + +/* --------------------------------------------------------------------- */ + +struct udf_strategy udf_strat_sequential = +{ + udf_create_logvol_dscr_seq, + udf_free_logvol_dscr_seq, + udf_read_logvol_dscr_seq, + udf_write_logvol_dscr_seq, + udf_queuebuf_seq, + udf_discstrat_init_seq, + udf_discstrat_finish_seq +}; + + diff --git a/sys/fs/udf/udf_subr.c b/sys/fs/udf/udf_subr.c new file mode 100644 index 000000000..83254af45 --- /dev/null +++ b/sys/fs/udf/udf_subr.c @@ -0,0 +1,6817 @@ +/* $NetBSD: udf_subr.c,v 1.122 2013/11/21 23:42:09 riz Exp $ */ + +/* + * Copyright (c) 2006, 2008 Reinoud Zandijk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include +#ifndef lint +__KERNEL_RCSID(0, "$NetBSD: udf_subr.c,v 1.122 2013/11/21 23:42:09 riz Exp $"); +#endif /* not lint */ + + +#if defined(_KERNEL_OPT) +#include "opt_compat_netbsd.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "udf.h" +#include "udf_subr.h" +#include "udf_bswap.h" + + +#define VTOI(vnode) ((struct udf_node *) (vnode)->v_data) + +#define UDF_SET_SYSTEMFILE(vp) \ + /* XXXAD Is the vnode locked? */ \ + (vp)->v_vflag |= VV_SYSTEM; \ + vref((vp)); \ + vput((vp)); \ + +extern int syncer_maxdelay; /* maximum delay time */ +extern int (**udf_vnodeop_p)(void *); + +/* --------------------------------------------------------------------- */ + +//#ifdef DEBUG +#if 1 + +#if 0 +static void +udf_dumpblob(boid *blob, uint32_t dlen) +{ + int i, j; + + printf("blob = %p\n", blob); + printf("dump of %d bytes\n", dlen); + + for (i = 0; i < dlen; i+ = 16) { + printf("%04x ", i); + for (j = 0; j < 16; j++) { + if (i+j < dlen) { + printf("%02x ", blob[i+j]); + } else { + printf(" "); + } + } + for (j = 0; j < 16; j++) { + if (i+j < dlen) { + if (blob[i+j]>32 && blob[i+j]! = 127) { + printf("%c", blob[i+j]); + } else { + printf("."); + } + } + } + printf("\n"); + } + printf("\n"); + Debugger(); +} +#endif + +static void +udf_dump_discinfo(struct udf_mount *ump) +{ + char bits[128]; + struct mmc_discinfo *di = &ump->discinfo; + + if ((udf_verbose & UDF_DEBUG_VOLUMES) == 0) + return; + + printf("Device/media info :\n"); + printf("\tMMC profile 0x%02x\n", di->mmc_profile); + printf("\tderived class %d\n", di->mmc_class); + printf("\tsector size %d\n", di->sector_size); + printf("\tdisc state %d\n", di->disc_state); + printf("\tlast ses state %d\n", di->last_session_state); + printf("\tbg format state %d\n", di->bg_format_state); + printf("\tfrst track %d\n", di->first_track); + printf("\tfst on last ses %d\n", di->first_track_last_session); + printf("\tlst on last ses %d\n", di->last_track_last_session); + printf("\tlink block penalty %d\n", di->link_block_penalty); + snprintb(bits, sizeof(bits), MMC_DFLAGS_FLAGBITS, di->disc_flags); + printf("\tdisc flags %s\n", bits); + printf("\tdisc id %x\n", di->disc_id); + printf("\tdisc barcode %"PRIx64"\n", di->disc_barcode); + + printf("\tnum sessions %d\n", di->num_sessions); + printf("\tnum tracks %d\n", di->num_tracks); + + snprintb(bits, sizeof(bits), MMC_CAP_FLAGBITS, di->mmc_cur); + printf("\tcapabilities cur %s\n", bits); + snprintb(bits, sizeof(bits), MMC_CAP_FLAGBITS, di->mmc_cap); + printf("\tcapabilities cap %s\n", bits); +} + +static void +udf_dump_trackinfo(struct mmc_trackinfo *trackinfo) +{ + char bits[128]; + + if ((udf_verbose & UDF_DEBUG_VOLUMES) == 0) + return; + + printf("Trackinfo for track %d:\n", trackinfo->tracknr); + printf("\tsessionnr %d\n", trackinfo->sessionnr); + printf("\ttrack mode %d\n", trackinfo->track_mode); + printf("\tdata mode %d\n", trackinfo->data_mode); + snprintb(bits, sizeof(bits), MMC_TRACKINFO_FLAGBITS, trackinfo->flags); + printf("\tflags %s\n", bits); + + printf("\ttrack start %d\n", trackinfo->track_start); + printf("\tnext_writable %d\n", trackinfo->next_writable); + printf("\tfree_blocks %d\n", trackinfo->free_blocks); + printf("\tpacket_size %d\n", trackinfo->packet_size); + printf("\ttrack size %d\n", trackinfo->track_size); + printf("\tlast recorded block %d\n", trackinfo->last_recorded); +} + +#else +#define udf_dump_discinfo(a); +#define udf_dump_trackinfo(a); +#endif + + +/* --------------------------------------------------------------------- */ + +/* not called often */ +int +udf_update_discinfo(struct udf_mount *ump) +{ + struct vnode *devvp = ump->devvp; + uint64_t psize; + unsigned secsize; + struct mmc_discinfo *di; + int error; + + DPRINTF(VOLUMES, ("read/update disc info\n")); + di = &ump->discinfo; + memset(di, 0, sizeof(struct mmc_discinfo)); + + /* check if we're on a MMC capable device, i.e. CD/DVD */ + error = VOP_IOCTL(devvp, MMCGETDISCINFO, di, FKIOCTL, NOCRED); + if (error == 0) { + udf_dump_discinfo(ump); + return 0; + } + + /* disc partition support */ + error = getdisksize(devvp, &psize, &secsize); + if (error) + return error; + + /* set up a disc info profile for partitions */ + di->mmc_profile = 0x01; /* disc type */ + di->mmc_class = MMC_CLASS_DISC; + di->disc_state = MMC_STATE_CLOSED; + di->last_session_state = MMC_STATE_CLOSED; + di->bg_format_state = MMC_BGFSTATE_COMPLETED; + di->link_block_penalty = 0; + + di->mmc_cur = MMC_CAP_RECORDABLE | MMC_CAP_REWRITABLE | + MMC_CAP_ZEROLINKBLK | MMC_CAP_HW_DEFECTFREE; + di->mmc_cap = di->mmc_cur; + di->disc_flags = MMC_DFLAGS_UNRESTRICTED; + + /* TODO problem with last_possible_lba on resizable VND; request */ + di->last_possible_lba = psize; + di->sector_size = secsize; + + di->num_sessions = 1; + di->num_tracks = 1; + + di->first_track = 1; + di->first_track_last_session = di->last_track_last_session = 1; + + udf_dump_discinfo(ump); + return 0; +} + + +int +udf_update_trackinfo(struct udf_mount *ump, struct mmc_trackinfo *ti) +{ + struct vnode *devvp = ump->devvp; + struct mmc_discinfo *di = &ump->discinfo; + int error, class; + + DPRINTF(VOLUMES, ("read track info\n")); + + class = di->mmc_class; + if (class != MMC_CLASS_DISC) { + /* tracknr specified in struct ti */ + error = VOP_IOCTL(devvp, MMCGETTRACKINFO, ti, FKIOCTL, NOCRED); + return error; + } + + /* disc partition support */ + if (ti->tracknr != 1) + return EIO; + + /* create fake ti (TODO check for resized vnds) */ + ti->sessionnr = 1; + + ti->track_mode = 0; /* XXX */ + ti->data_mode = 0; /* XXX */ + ti->flags = MMC_TRACKINFO_LRA_VALID | MMC_TRACKINFO_NWA_VALID; + + ti->track_start = 0; + ti->packet_size = 1; + + /* TODO support for resizable vnd */ + ti->track_size = di->last_possible_lba; + ti->next_writable = di->last_possible_lba; + ti->last_recorded = ti->next_writable; + ti->free_blocks = 0; + + return 0; +} + + +int +udf_setup_writeparams(struct udf_mount *ump) +{ + struct mmc_writeparams mmc_writeparams; + int error; + + if (ump->discinfo.mmc_class == MMC_CLASS_DISC) + return 0; + + /* + * only CD burning normally needs setting up, but other disc types + * might need other settings to be made. The MMC framework will set up + * the nessisary recording parameters according to the disc + * characteristics read in. Modifications can be made in the discinfo + * structure passed to change the nature of the disc. + */ + + memset(&mmc_writeparams, 0, sizeof(struct mmc_writeparams)); + mmc_writeparams.mmc_class = ump->discinfo.mmc_class; + mmc_writeparams.mmc_cur = ump->discinfo.mmc_cur; + + /* + * UDF dictates first track to determine track mode for the whole + * disc. [UDF 1.50/6.10.1.1, UDF 1.50/6.10.2.1] + * To prevent problems with a `reserved' track in front we start with + * the 2nd track and if that is not valid, go for the 1st. + */ + mmc_writeparams.tracknr = 2; + mmc_writeparams.data_mode = MMC_DATAMODE_DEFAULT; /* XA disc */ + mmc_writeparams.track_mode = MMC_TRACKMODE_DEFAULT; /* data */ + + error = VOP_IOCTL(ump->devvp, MMCSETUPWRITEPARAMS, &mmc_writeparams, + FKIOCTL, NOCRED); + if (error) { + mmc_writeparams.tracknr = 1; + error = VOP_IOCTL(ump->devvp, MMCSETUPWRITEPARAMS, + &mmc_writeparams, FKIOCTL, NOCRED); + } + return error; +} + + +int +udf_synchronise_caches(struct udf_mount *ump) +{ + struct mmc_op mmc_op; + + DPRINTF(CALL, ("udf_synchronise_caches()\n")); + + if (ump->vfs_mountp->mnt_flag & MNT_RDONLY) + return 0; + + /* discs are done now */ + if (ump->discinfo.mmc_class == MMC_CLASS_DISC) + return 0; + + memset(&mmc_op, 0, sizeof(struct mmc_op)); + mmc_op.operation = MMC_OP_SYNCHRONISECACHE; + + /* ignore return code */ + (void) VOP_IOCTL(ump->devvp, MMCOP, &mmc_op, FKIOCTL, NOCRED); + + return 0; +} + +/* --------------------------------------------------------------------- */ + +/* track/session searching for mounting */ +int +udf_search_tracks(struct udf_mount *ump, struct udf_args *args, + int *first_tracknr, int *last_tracknr) +{ + struct mmc_trackinfo trackinfo; + uint32_t tracknr, start_track, num_tracks; + int error; + + /* if negative, sessionnr is relative to last session */ + if (args->sessionnr < 0) { + args->sessionnr += ump->discinfo.num_sessions; + } + + /* sanity */ + if (args->sessionnr < 0) + args->sessionnr = 0; + if (args->sessionnr > ump->discinfo.num_sessions) + args->sessionnr = ump->discinfo.num_sessions; + + /* search the tracks for this session, zero session nr indicates last */ + if (args->sessionnr == 0) + args->sessionnr = ump->discinfo.num_sessions; + if (ump->discinfo.last_session_state == MMC_STATE_EMPTY) + args->sessionnr--; + + /* sanity again */ + if (args->sessionnr < 0) + args->sessionnr = 0; + + /* search the first and last track of the specified session */ + num_tracks = ump->discinfo.num_tracks; + start_track = ump->discinfo.first_track; + + /* search for first track of this session */ + for (tracknr = start_track; tracknr <= num_tracks; tracknr++) { + /* get track info */ + trackinfo.tracknr = tracknr; + error = udf_update_trackinfo(ump, &trackinfo); + if (error) + return error; + + if (trackinfo.sessionnr == args->sessionnr) + break; + } + *first_tracknr = tracknr; + + /* search for last track of this session */ + for (;tracknr <= num_tracks; tracknr++) { + /* get track info */ + trackinfo.tracknr = tracknr; + error = udf_update_trackinfo(ump, &trackinfo); + if (error || (trackinfo.sessionnr != args->sessionnr)) { + tracknr--; + break; + } + } + if (tracknr > num_tracks) + tracknr--; + + *last_tracknr = tracknr; + + if (*last_tracknr < *first_tracknr) { + printf( "udf_search_tracks: sanity check on drive+disc failed, " + "drive returned garbage\n"); + return EINVAL; + } + + assert(*last_tracknr >= *first_tracknr); + return 0; +} + + +/* + * NOTE: this is the only routine in this file that directly peeks into the + * metadata file but since its at a larval state of the mount it can't hurt. + * + * XXX candidate for udf_allocation.c + * XXX clean me up!, change to new node reading code. + */ + +static void +udf_check_track_metadata_overlap(struct udf_mount *ump, + struct mmc_trackinfo *trackinfo) +{ + struct part_desc *part; + struct file_entry *fe; + struct extfile_entry *efe; + struct short_ad *s_ad; + struct long_ad *l_ad; + uint32_t track_start, track_end; + uint32_t phys_part_start, phys_part_end, part_start, part_end; + uint32_t sector_size, len, alloclen, plb_num; + uint8_t *pos; + int addr_type, icblen, icbflags; + + /* get our track extents */ + track_start = trackinfo->track_start; + track_end = track_start + trackinfo->track_size; + + /* get our base partition extent */ + KASSERT(ump->node_part == ump->fids_part); + part = ump->partitions[ump->vtop[ump->node_part]]; + phys_part_start = udf_rw32(part->start_loc); + phys_part_end = phys_part_start + udf_rw32(part->part_len); + + /* no use if its outside the physical partition */ + if ((phys_part_start >= track_end) || (phys_part_end < track_start)) + return; + + /* + * now follow all extents in the fe/efe to see if they refer to this + * track + */ + + sector_size = ump->discinfo.sector_size; + + /* XXX should we claim exclusive access to the metafile ? */ + /* TODO: move to new node read code */ + fe = ump->metadata_node->fe; + efe = ump->metadata_node->efe; + if (fe) { + alloclen = udf_rw32(fe->l_ad); + pos = &fe->data[0] + udf_rw32(fe->l_ea); + icbflags = udf_rw16(fe->icbtag.flags); + } else { + assert(efe); + alloclen = udf_rw32(efe->l_ad); + pos = &efe->data[0] + udf_rw32(efe->l_ea); + icbflags = udf_rw16(efe->icbtag.flags); + } + addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK; + + while (alloclen) { + if (addr_type == UDF_ICB_SHORT_ALLOC) { + icblen = sizeof(struct short_ad); + s_ad = (struct short_ad *) pos; + len = udf_rw32(s_ad->len); + plb_num = udf_rw32(s_ad->lb_num); + } else { + /* should not be present, but why not */ + icblen = sizeof(struct long_ad); + l_ad = (struct long_ad *) pos; + len = udf_rw32(l_ad->len); + plb_num = udf_rw32(l_ad->loc.lb_num); + /* pvpart_num = udf_rw16(l_ad->loc.part_num); */ + } + /* process extent */ + len = UDF_EXT_LEN(len); + + part_start = phys_part_start + plb_num; + part_end = part_start + (len / sector_size); + + if ((part_start >= track_start) && (part_end <= track_end)) { + /* extent is enclosed within this track */ + ump->metadata_track = *trackinfo; + return; + } + + pos += icblen; + alloclen -= icblen; + } +} + + +int +udf_search_writing_tracks(struct udf_mount *ump) +{ + struct vnode *devvp = ump->devvp; + struct mmc_trackinfo trackinfo; + struct mmc_op mmc_op; + struct part_desc *part; + uint32_t tracknr, start_track, num_tracks; + uint32_t track_start, track_end, part_start, part_end; + int node_alloc, error; + + /* + * in the CD/(HD)DVD/BD recordable device model a few tracks within + * the last session might be open but in the UDF device model at most + * three tracks can be open: a reserved track for delayed ISO VRS + * writing, a data track and a metadata track. We search here for the + * data track and the metadata track. Note that the reserved track is + * troublesome but can be detected by its small size of < 512 sectors. + */ + + /* update discinfo since it might have changed */ + error = udf_update_discinfo(ump); + if (error) + return error; + + num_tracks = ump->discinfo.num_tracks; + start_track = ump->discinfo.first_track; + + /* fetch info on first and possibly only track */ + trackinfo.tracknr = start_track; + error = udf_update_trackinfo(ump, &trackinfo); + if (error) + return error; + + /* copy results to our mount point */ + ump->data_track = trackinfo; + ump->metadata_track = trackinfo; + + /* if not sequential, we're done */ + if (num_tracks == 1) + return 0; + + for (tracknr = start_track;tracknr <= num_tracks; tracknr++) { + /* get track info */ + trackinfo.tracknr = tracknr; + error = udf_update_trackinfo(ump, &trackinfo); + if (error) + return error; + + /* + * If this track is marked damaged, ask for repair. This is an + * optional command, so ignore its error but report warning. + */ + if (trackinfo.flags & MMC_TRACKINFO_DAMAGED) { + memset(&mmc_op, 0, sizeof(mmc_op)); + mmc_op.operation = MMC_OP_REPAIRTRACK; + mmc_op.mmc_profile = ump->discinfo.mmc_profile; + mmc_op.tracknr = tracknr; + error = VOP_IOCTL(devvp, MMCOP, &mmc_op, FKIOCTL, NOCRED); + if (error) + (void)printf("Drive can't explicitly repair " + "damaged track %d, but it might " + "autorepair\n", tracknr); + + /* reget track info */ + error = udf_update_trackinfo(ump, &trackinfo); + if (error) + return error; + } + if ((trackinfo.flags & MMC_TRACKINFO_NWA_VALID) == 0) + continue; + + track_start = trackinfo.track_start; + track_end = track_start + trackinfo.track_size; + + /* check for overlap on data partition */ + part = ump->partitions[ump->data_part]; + part_start = udf_rw32(part->start_loc); + part_end = part_start + udf_rw32(part->part_len); + if ((part_start < track_end) && (part_end > track_start)) { + ump->data_track = trackinfo; + /* TODO check if UDF partition data_part is writable */ + } + + /* check for overlap on metadata partition */ + node_alloc = ump->vtop_alloc[ump->node_part]; + if ((node_alloc == UDF_ALLOC_METASEQUENTIAL) || + (node_alloc == UDF_ALLOC_METABITMAP)) { + udf_check_track_metadata_overlap(ump, &trackinfo); + } else { + ump->metadata_track = trackinfo; + } + } + + if ((ump->data_track.flags & MMC_TRACKINFO_NWA_VALID) == 0) + return EROFS; + + if ((ump->metadata_track.flags & MMC_TRACKINFO_NWA_VALID) == 0) + return EROFS; + + return 0; +} + +/* --------------------------------------------------------------------- */ + +/* + * Check if the blob starts with a good UDF tag. Tags are protected by a + * checksum over the reader except one byte at position 4 that is the checksum + * itself. + */ + +int +udf_check_tag(void *blob) +{ + struct desc_tag *tag = blob; + uint8_t *pos, sum, cnt; + + /* check TAG header checksum */ + pos = (uint8_t *) tag; + sum = 0; + + for(cnt = 0; cnt < 16; cnt++) { + if (cnt != 4) + sum += *pos; + pos++; + } + if (sum != tag->cksum) { + /* bad tag header checksum; this is not a valid tag */ + return EINVAL; + } + + return 0; +} + + +/* + * check tag payload will check descriptor CRC as specified. + * If the descriptor is too long, it will return EIO otherwise EINVAL. + */ + +int +udf_check_tag_payload(void *blob, uint32_t max_length) +{ + struct desc_tag *tag = blob; + uint16_t crc, crc_len; + + crc_len = udf_rw16(tag->desc_crc_len); + + /* check payload CRC if applicable */ + if (crc_len == 0) + return 0; + + if (crc_len > max_length) + return EIO; + + crc = udf_cksum(((uint8_t *) tag) + UDF_DESC_TAG_LENGTH, crc_len); + if (crc != udf_rw16(tag->desc_crc)) { + /* bad payload CRC; this is a broken tag */ + return EINVAL; + } + + return 0; +} + + +void +udf_validate_tag_sum(void *blob) +{ + struct desc_tag *tag = blob; + uint8_t *pos, sum, cnt; + + /* calculate TAG header checksum */ + pos = (uint8_t *) tag; + sum = 0; + + for(cnt = 0; cnt < 16; cnt++) { + if (cnt != 4) sum += *pos; + pos++; + } + tag->cksum = sum; /* 8 bit */ +} + + +/* assumes sector number of descriptor to be saved already present */ +void +udf_validate_tag_and_crc_sums(void *blob) +{ + struct desc_tag *tag = blob; + uint8_t *btag = (uint8_t *) tag; + uint16_t crc, crc_len; + + crc_len = udf_rw16(tag->desc_crc_len); + + /* check payload CRC if applicable */ + if (crc_len > 0) { + crc = udf_cksum(btag + UDF_DESC_TAG_LENGTH, crc_len); + tag->desc_crc = udf_rw16(crc); + } + + /* calculate TAG header checksum */ + udf_validate_tag_sum(blob); +} + +/* --------------------------------------------------------------------- */ + +/* + * XXX note the different semantics from udfclient: for FIDs it still rounds + * up to sectors. Use udf_fidsize() for a correct length. + */ + +int +udf_tagsize(union dscrptr *dscr, uint32_t lb_size) +{ + uint32_t size, tag_id, num_lb, elmsz; + + tag_id = udf_rw16(dscr->tag.id); + + switch (tag_id) { + case TAGID_LOGVOL : + size = sizeof(struct logvol_desc) - 1; + size += udf_rw32(dscr->lvd.mt_l); + break; + case TAGID_UNALLOC_SPACE : + elmsz = sizeof(struct extent_ad); + size = sizeof(struct unalloc_sp_desc) - elmsz; + size += udf_rw32(dscr->usd.alloc_desc_num) * elmsz; + break; + case TAGID_FID : + size = UDF_FID_SIZE + dscr->fid.l_fi + udf_rw16(dscr->fid.l_iu); + size = (size + 3) & ~3; + break; + case TAGID_LOGVOL_INTEGRITY : + size = sizeof(struct logvol_int_desc) - sizeof(uint32_t); + size += udf_rw32(dscr->lvid.l_iu); + size += (2 * udf_rw32(dscr->lvid.num_part) * sizeof(uint32_t)); + break; + case TAGID_SPACE_BITMAP : + size = sizeof(struct space_bitmap_desc) - 1; + size += udf_rw32(dscr->sbd.num_bytes); + break; + case TAGID_SPARING_TABLE : + elmsz = sizeof(struct spare_map_entry); + size = sizeof(struct udf_sparing_table) - elmsz; + size += udf_rw16(dscr->spt.rt_l) * elmsz; + break; + case TAGID_FENTRY : + size = sizeof(struct file_entry); + size += udf_rw32(dscr->fe.l_ea) + udf_rw32(dscr->fe.l_ad)-1; + break; + case TAGID_EXTFENTRY : + size = sizeof(struct extfile_entry); + size += udf_rw32(dscr->efe.l_ea) + udf_rw32(dscr->efe.l_ad)-1; + break; + case TAGID_FSD : + size = sizeof(struct fileset_desc); + break; + default : + size = sizeof(union dscrptr); + break; + } + + if ((size == 0) || (lb_size == 0)) + return 0; + + if (lb_size == 1) + return size; + + /* round up in sectors */ + num_lb = (size + lb_size -1) / lb_size; + return num_lb * lb_size; +} + + +int +udf_fidsize(struct fileid_desc *fid) +{ + uint32_t size; + + if (udf_rw16(fid->tag.id) != TAGID_FID) + panic("got udf_fidsize on non FID\n"); + + size = UDF_FID_SIZE + fid->l_fi + udf_rw16(fid->l_iu); + size = (size + 3) & ~3; + + return size; +} + +/* --------------------------------------------------------------------- */ + +void +udf_lock_node(struct udf_node *udf_node, int flag, char const *fname, const int lineno) +{ + int ret; + + mutex_enter(&udf_node->node_mutex); + /* wait until free */ + while (udf_node->i_flags & IN_LOCKED) { + ret = cv_timedwait(&udf_node->node_lock, &udf_node->node_mutex, hz/8); + /* TODO check if we should return error; abort */ + if (ret == EWOULDBLOCK) { + DPRINTF(LOCKING, ( "udf_lock_node: udf_node %p would block " + "wanted at %s:%d, previously locked at %s:%d\n", + udf_node, fname, lineno, + udf_node->lock_fname, udf_node->lock_lineno)); + } + } + /* grab */ + udf_node->i_flags |= IN_LOCKED | flag; + /* debug */ + udf_node->lock_fname = fname; + udf_node->lock_lineno = lineno; + + mutex_exit(&udf_node->node_mutex); +} + + +void +udf_unlock_node(struct udf_node *udf_node, int flag) +{ + mutex_enter(&udf_node->node_mutex); + udf_node->i_flags &= ~(IN_LOCKED | flag); + cv_broadcast(&udf_node->node_lock); + mutex_exit(&udf_node->node_mutex); +} + + +/* --------------------------------------------------------------------- */ + +static int +udf_read_anchor(struct udf_mount *ump, uint32_t sector, struct anchor_vdp **dst) +{ + int error; + + error = udf_read_phys_dscr(ump, sector, M_UDFVOLD, + (union dscrptr **) dst); + if (!error) { + /* blank terminator blocks are not allowed here */ + if (*dst == NULL) + return ENOENT; + if (udf_rw16((*dst)->tag.id) != TAGID_ANCHOR) { + error = ENOENT; + free(*dst, M_UDFVOLD); + *dst = NULL; + DPRINTF(VOLUMES, ("Not an anchor\n")); + } + } + + return error; +} + + +int +udf_read_anchors(struct udf_mount *ump) +{ + struct udf_args *args = &ump->mount_args; + struct mmc_trackinfo first_track; + struct mmc_trackinfo second_track; + struct mmc_trackinfo last_track; + struct anchor_vdp **anchorsp; + uint32_t track_start; + uint32_t track_end; + uint32_t positions[4]; + int first_tracknr, last_tracknr; + int error, anch, ok, first_anchor; + + /* search the first and last track of the specified session */ + error = udf_search_tracks(ump, args, &first_tracknr, &last_tracknr); + if (!error) { + first_track.tracknr = first_tracknr; + error = udf_update_trackinfo(ump, &first_track); + } + if (!error) { + last_track.tracknr = last_tracknr; + error = udf_update_trackinfo(ump, &last_track); + } + if ((!error) && (first_tracknr != last_tracknr)) { + second_track.tracknr = first_tracknr+1; + error = udf_update_trackinfo(ump, &second_track); + } + if (error) { + printf("UDF mount: reading disc geometry failed\n"); + return 0; + } + + track_start = first_track.track_start; + + /* `end' is not as straitforward as start. */ + track_end = last_track.track_start + + last_track.track_size - last_track.free_blocks - 1; + + if (ump->discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) { + /* end of track is not straitforward here */ + if (last_track.flags & MMC_TRACKINFO_LRA_VALID) + track_end = last_track.last_recorded; + else if (last_track.flags & MMC_TRACKINFO_NWA_VALID) + track_end = last_track.next_writable + - ump->discinfo.link_block_penalty; + } + + /* its no use reading a blank track */ + first_anchor = 0; + if (first_track.flags & MMC_TRACKINFO_BLANK) + first_anchor = 1; + + /* get our packet size */ + ump->packet_size = first_track.packet_size; + if (first_track.flags & MMC_TRACKINFO_BLANK) + ump->packet_size = second_track.packet_size; + + if (ump->packet_size <= 1) { + /* take max, but not bigger than 64 */ + ump->packet_size = MAXPHYS / ump->discinfo.sector_size; + ump->packet_size = MIN(ump->packet_size, 64); + } + KASSERT(ump->packet_size >= 1); + + /* read anchors start+256, start+512, end-256, end */ + positions[0] = track_start+256; + positions[1] = track_end-256; + positions[2] = track_end; + positions[3] = track_start+512; /* [UDF 2.60/6.11.2] */ + /* XXX shouldn't +512 be prefered above +256 for compat with Roxio CD */ + + ok = 0; + anchorsp = ump->anchors; + for (anch = first_anchor; anch < 4; anch++) { + DPRINTF(VOLUMES, ("Read anchor %d at sector %d\n", anch, + positions[anch])); + error = udf_read_anchor(ump, positions[anch], anchorsp); + if (!error) { + anchorsp++; + ok++; + } + } + + /* VATs are only recorded on sequential media, but initialise */ + ump->first_possible_vat_location = track_start + 2; + ump->last_possible_vat_location = track_end + last_track.packet_size; + + return ok; +} + +/* --------------------------------------------------------------------- */ + +int +udf_get_c_type(struct udf_node *udf_node) +{ + int isdir, what; + + isdir = (udf_node->vnode->v_type == VDIR); + what = isdir ? UDF_C_FIDS : UDF_C_USERDATA; + + if (udf_node->ump) + if (udf_node == udf_node->ump->metadatabitmap_node) + what = UDF_C_METADATA_SBM; + + return what; +} + + +int +udf_get_record_vpart(struct udf_mount *ump, int udf_c_type) +{ + int vpart_num; + + vpart_num = ump->data_part; + if (udf_c_type == UDF_C_NODE) + vpart_num = ump->node_part; + if (udf_c_type == UDF_C_FIDS) + vpart_num = ump->fids_part; + + return vpart_num; +} + + +/* + * BUGALERT: some rogue implementations use random physical partition + * numbers to break other implementations so lookup the number. + */ + +static uint16_t +udf_find_raw_phys(struct udf_mount *ump, uint16_t raw_phys_part) +{ + struct part_desc *part; + uint16_t phys_part; + + for (phys_part = 0; phys_part < UDF_PARTITIONS; phys_part++) { + part = ump->partitions[phys_part]; + if (part == NULL) + break; + if (udf_rw16(part->part_num) == raw_phys_part) + break; + } + return phys_part; +} + +/* --------------------------------------------------------------------- */ + +/* we dont try to be smart; we just record the parts */ +#define UDF_UPDATE_DSCR(name, dscr) \ + if (name) \ + free(name, M_UDFVOLD); \ + name = dscr; + +static int +udf_process_vds_descriptor(struct udf_mount *ump, union dscrptr *dscr) +{ + uint16_t phys_part, raw_phys_part; + + DPRINTF(VOLUMES, ("\tprocessing VDS descr %d\n", + udf_rw16(dscr->tag.id))); + switch (udf_rw16(dscr->tag.id)) { + case TAGID_PRI_VOL : /* primary partition */ + UDF_UPDATE_DSCR(ump->primary_vol, &dscr->pvd); + break; + case TAGID_LOGVOL : /* logical volume */ + UDF_UPDATE_DSCR(ump->logical_vol, &dscr->lvd); + break; + case TAGID_UNALLOC_SPACE : /* unallocated space */ + UDF_UPDATE_DSCR(ump->unallocated, &dscr->usd); + break; + case TAGID_IMP_VOL : /* implementation */ + /* XXX do we care about multiple impl. descr ? */ + UDF_UPDATE_DSCR(ump->implementation, &dscr->ivd); + break; + case TAGID_PARTITION : /* physical partition */ + /* not much use if its not allocated */ + if ((udf_rw16(dscr->pd.flags) & UDF_PART_FLAG_ALLOCATED) == 0) { + free(dscr, M_UDFVOLD); + break; + } + + /* + * BUGALERT: some rogue implementations use random physical + * partition numbers to break other implementations so lookup + * the number. + */ + raw_phys_part = udf_rw16(dscr->pd.part_num); + phys_part = udf_find_raw_phys(ump, raw_phys_part); + + if (phys_part == UDF_PARTITIONS) { + free(dscr, M_UDFVOLD); + return EINVAL; + } + + UDF_UPDATE_DSCR(ump->partitions[phys_part], &dscr->pd); + break; + case TAGID_VOL : /* volume space extender; rare */ + DPRINTF(VOLUMES, ("VDS extender ignored\n")); + free(dscr, M_UDFVOLD); + break; + default : + DPRINTF(VOLUMES, ("Unhandled VDS type %d\n", + udf_rw16(dscr->tag.id))); + free(dscr, M_UDFVOLD); + } + + return 0; +} +#undef UDF_UPDATE_DSCR + +/* --------------------------------------------------------------------- */ + +static int +udf_read_vds_extent(struct udf_mount *ump, uint32_t loc, uint32_t len) +{ + union dscrptr *dscr; + uint32_t sector_size, dscr_size; + int error; + + sector_size = ump->discinfo.sector_size; + + /* loc is sectornr, len is in bytes */ + error = EIO; + while (len) { + error = udf_read_phys_dscr(ump, loc, M_UDFVOLD, &dscr); + if (error) + return error; + + /* blank block is a terminator */ + if (dscr == NULL) + return 0; + + /* TERM descriptor is a terminator */ + if (udf_rw16(dscr->tag.id) == TAGID_TERM) { + free(dscr, M_UDFVOLD); + return 0; + } + + /* process all others */ + dscr_size = udf_tagsize(dscr, sector_size); + error = udf_process_vds_descriptor(ump, dscr); + if (error) { + free(dscr, M_UDFVOLD); + break; + } + assert((dscr_size % sector_size) == 0); + + len -= dscr_size; + loc += dscr_size / sector_size; + } + + return error; +} + + +int +udf_read_vds_space(struct udf_mount *ump) +{ + /* struct udf_args *args = &ump->mount_args; */ + struct anchor_vdp *anchor, *anchor2; + size_t size; + uint32_t main_loc, main_len; + uint32_t reserve_loc, reserve_len; + int error; + + /* + * read in VDS space provided by the anchors; if one descriptor read + * fails, try the mirror sector. + * + * check if 2nd anchor is different from 1st; if so, go for 2nd. This + * avoids the `compatibility features' of DirectCD that may confuse + * stuff completely. + */ + + anchor = ump->anchors[0]; + anchor2 = ump->anchors[1]; + assert(anchor); + + if (anchor2) { + size = sizeof(struct extent_ad); + if (memcmp(&anchor->main_vds_ex, &anchor2->main_vds_ex, size)) + anchor = anchor2; + /* reserve is specified to be a literal copy of main */ + } + + main_loc = udf_rw32(anchor->main_vds_ex.loc); + main_len = udf_rw32(anchor->main_vds_ex.len); + + reserve_loc = udf_rw32(anchor->reserve_vds_ex.loc); + reserve_len = udf_rw32(anchor->reserve_vds_ex.len); + + error = udf_read_vds_extent(ump, main_loc, main_len); + if (error) { + printf("UDF mount: reading in reserve VDS extent\n"); + error = udf_read_vds_extent(ump, reserve_loc, reserve_len); + } + + return error; +} + +/* --------------------------------------------------------------------- */ + +/* + * Read in the logical volume integrity sequence pointed to by our logical + * volume descriptor. Its a sequence that can be extended using fields in the + * integrity descriptor itself. On sequential media only one is found, on + * rewritable media a sequence of descriptors can be found as a form of + * history keeping and on non sequential write-once media the chain is vital + * to allow more and more descriptors to be written. The last descriptor + * written in an extent needs to claim space for a new extent. + */ + +static int +udf_retrieve_lvint(struct udf_mount *ump) +{ + union dscrptr *dscr; + struct logvol_int_desc *lvint; + struct udf_lvintq *trace; + uint32_t lb_size, lbnum, len; + int dscr_type, error, trace_len; + + lb_size = udf_rw32(ump->logical_vol->lb_size); + len = udf_rw32(ump->logical_vol->integrity_seq_loc.len); + lbnum = udf_rw32(ump->logical_vol->integrity_seq_loc.loc); + + /* clean trace */ + memset(ump->lvint_trace, 0, + UDF_LVDINT_SEGMENTS * sizeof(struct udf_lvintq)); + + trace_len = 0; + trace = ump->lvint_trace; + trace->start = lbnum; + trace->end = lbnum + len/lb_size; + trace->pos = 0; + trace->wpos = 0; + + lvint = NULL; + dscr = NULL; + error = 0; + while (len) { + trace->pos = lbnum - trace->start; + trace->wpos = trace->pos + 1; + + /* read in our integrity descriptor */ + error = udf_read_phys_dscr(ump, lbnum, M_UDFVOLD, &dscr); + if (!error) { + if (dscr == NULL) { + trace->wpos = trace->pos; + break; /* empty terminates */ + } + dscr_type = udf_rw16(dscr->tag.id); + if (dscr_type == TAGID_TERM) { + trace->wpos = trace->pos; + break; /* clean terminator */ + } + if (dscr_type != TAGID_LOGVOL_INTEGRITY) { + /* fatal... corrupt disc */ + error = ENOENT; + break; + } + if (lvint) + free(lvint, M_UDFVOLD); + lvint = &dscr->lvid; + dscr = NULL; + } /* else hope for the best... maybe the next is ok */ + + DPRINTFIF(VOLUMES, lvint, ("logvol integrity read, state %s\n", + udf_rw32(lvint->integrity_type) ? "CLOSED" : "OPEN")); + + /* proceed sequential */ + lbnum += 1; + len -= lb_size; + + /* are we linking to a new piece? */ + if (dscr && lvint->next_extent.len) { + len = udf_rw32(lvint->next_extent.len); + lbnum = udf_rw32(lvint->next_extent.loc); + + if (trace_len >= UDF_LVDINT_SEGMENTS-1) { + /* IEK! segment link full... */ + DPRINTF(VOLUMES, ("lvdint segments full\n")); + error = EINVAL; + } else { + trace++; + trace_len++; + + trace->start = lbnum; + trace->end = lbnum + len/lb_size; + trace->pos = 0; + trace->wpos = 0; + } + } + } + + /* clean up the mess, esp. when there is an error */ + if (dscr) + free(dscr, M_UDFVOLD); + + if (error && lvint) { + free(lvint, M_UDFVOLD); + lvint = NULL; + } + + if (!lvint) + error = ENOENT; + + ump->logvol_integrity = lvint; + return error; +} + + +static int +udf_loose_lvint_history(struct udf_mount *ump) +{ + union dscrptr **bufs, *dscr, *last_dscr; + struct udf_lvintq *trace, *in_trace, *out_trace; + struct logvol_int_desc *lvint; + uint32_t in_ext, in_pos, in_len; + uint32_t out_ext, out_wpos, out_len; + uint32_t lb_num; + uint32_t len, start; + int ext, minext, extlen, cnt, cpy_len, dscr_type; + int losing; + int error; + + DPRINTF(VOLUMES, ("need to lose some lvint history\n")); + + /* search smallest extent */ + trace = &ump->lvint_trace[0]; + minext = trace->end - trace->start; + for (ext = 1; ext < UDF_LVDINT_SEGMENTS; ext++) { + trace = &ump->lvint_trace[ext]; + extlen = trace->end - trace->start; + if (extlen == 0) + break; + minext = MIN(minext, extlen); + } + losing = MIN(minext, UDF_LVINT_LOSSAGE); + /* no sense wiping all */ + if (losing == minext) + losing--; + + DPRINTF(VOLUMES, ("\tlosing %d entries\n", losing)); + + /* get buffer for pieces */ + bufs = malloc(UDF_LVDINT_SEGMENTS * sizeof(void *), M_TEMP, M_WAITOK); + + in_ext = 0; + in_pos = losing; + in_trace = &ump->lvint_trace[in_ext]; + in_len = in_trace->end - in_trace->start; + out_ext = 0; + out_wpos = 0; + out_trace = &ump->lvint_trace[out_ext]; + out_len = out_trace->end - out_trace->start; + + last_dscr = NULL; + for(;;) { + out_trace->pos = out_wpos; + out_trace->wpos = out_trace->pos; + if (in_pos >= in_len) { + in_ext++; + in_pos = 0; + in_trace = &ump->lvint_trace[in_ext]; + in_len = in_trace->end - in_trace->start; + } + if (out_wpos >= out_len) { + out_ext++; + out_wpos = 0; + out_trace = &ump->lvint_trace[out_ext]; + out_len = out_trace->end - out_trace->start; + } + /* copy overlap contents */ + cpy_len = MIN(in_len - in_pos, out_len - out_wpos); + cpy_len = MIN(cpy_len, in_len - in_trace->pos); + if (cpy_len == 0) + break; + + /* copy */ + DPRINTF(VOLUMES, ("\treading %d lvid descriptors\n", cpy_len)); + for (cnt = 0; cnt < cpy_len; cnt++) { + /* read in our integrity descriptor */ + lb_num = in_trace->start + in_pos + cnt; + error = udf_read_phys_dscr(ump, lb_num, M_UDFVOLD, + &dscr); + if (error) { + /* copy last one */ + dscr = last_dscr; + } + bufs[cnt] = dscr; + if (!error) { + if (dscr == NULL) { + out_trace->pos = out_wpos + cnt; + out_trace->wpos = out_trace->pos; + break; /* empty terminates */ + } + dscr_type = udf_rw16(dscr->tag.id); + if (dscr_type == TAGID_TERM) { + out_trace->pos = out_wpos + cnt; + out_trace->wpos = out_trace->pos; + break; /* clean terminator */ + } + if (dscr_type != TAGID_LOGVOL_INTEGRITY) { + panic( "UDF integrity sequence " + "corrupted while mounted!\n"); + } + last_dscr = dscr; + } + } + + /* patch up if first entry was on error */ + if (bufs[0] == NULL) { + for (cnt = 0; cnt < cpy_len; cnt++) + if (bufs[cnt] != NULL) + break; + last_dscr = bufs[cnt]; + for (; cnt > 0; cnt--) { + bufs[cnt] = last_dscr; + } + } + + /* glue + write out */ + DPRINTF(VOLUMES, ("\twriting %d lvid descriptors\n", cpy_len)); + for (cnt = 0; cnt < cpy_len; cnt++) { + lb_num = out_trace->start + out_wpos + cnt; + lvint = &bufs[cnt]->lvid; + + /* set continuation */ + len = 0; + start = 0; + if (out_wpos + cnt == out_len) { + /* get continuation */ + trace = &ump->lvint_trace[out_ext+1]; + len = trace->end - trace->start; + start = trace->start; + } + lvint->next_extent.len = udf_rw32(len); + lvint->next_extent.loc = udf_rw32(start); + + lb_num = trace->start + trace->wpos; + error = udf_write_phys_dscr_sync(ump, NULL, UDF_C_DSCR, + bufs[cnt], lb_num, lb_num); + DPRINTFIF(VOLUMES, error, + ("error writing lvint lb_num\n")); + } + + /* free non repeating descriptors */ + last_dscr = NULL; + for (cnt = 0; cnt < cpy_len; cnt++) { + if (bufs[cnt] != last_dscr) + free(bufs[cnt], M_UDFVOLD); + last_dscr = bufs[cnt]; + } + + /* advance */ + in_pos += cpy_len; + out_wpos += cpy_len; + } + + free(bufs, M_TEMP); + + return 0; +} + + +static int +udf_writeout_lvint(struct udf_mount *ump, int lvflag) +{ + struct udf_lvintq *trace; + struct timeval now_v; + struct timespec now_s; + uint32_t sector; + int logvol_integrity; + int space, error; + + DPRINTF(VOLUMES, ("writing out logvol integrity descriptor\n")); + +again: + /* get free space in last chunk */ + trace = ump->lvint_trace; + while (trace->wpos > (trace->end - trace->start)) { + DPRINTF(VOLUMES, ("skip : start = %d, end = %d, pos = %d, " + "wpos = %d\n", trace->start, trace->end, + trace->pos, trace->wpos)); + trace++; + } + + /* check if there is space to append */ + space = (trace->end - trace->start) - trace->wpos; + DPRINTF(VOLUMES, ("write start = %d, end = %d, pos = %d, wpos = %d, " + "space = %d\n", trace->start, trace->end, trace->pos, + trace->wpos, space)); + + /* get state */ + logvol_integrity = udf_rw32(ump->logvol_integrity->integrity_type); + if (logvol_integrity == UDF_INTEGRITY_CLOSED) { + if ((space < 3) && (lvflag & UDF_APPENDONLY_LVINT)) { + /* TODO extent LVINT space if possible */ + return EROFS; + } + } + + if (space < 1) { + if (lvflag & UDF_APPENDONLY_LVINT) + return EROFS; + /* loose history by re-writing extents */ + error = udf_loose_lvint_history(ump); + if (error) + return error; + goto again; + } + + /* update our integrity descriptor to identify us and timestamp it */ + DPRINTF(VOLUMES, ("updating integrity descriptor\n")); + microtime(&now_v); + TIMEVAL_TO_TIMESPEC(&now_v, &now_s); + udf_timespec_to_timestamp(&now_s, &ump->logvol_integrity->time); + udf_set_regid(&ump->logvol_info->impl_id, IMPL_NAME); + udf_add_impl_regid(ump, &ump->logvol_info->impl_id); + + /* writeout integrity descriptor */ + sector = trace->start + trace->wpos; + error = udf_write_phys_dscr_sync(ump, NULL, UDF_C_DSCR, + (union dscrptr *) ump->logvol_integrity, + sector, sector); + DPRINTF(VOLUMES, ("writeout lvint : error = %d\n", error)); + if (error) + return error; + + /* advance write position */ + trace->wpos++; space--; + if (space >= 1) { + /* append terminator */ + sector = trace->start + trace->wpos; + error = udf_write_terminator(ump, sector); + + DPRINTF(VOLUMES, ("write terminator : error = %d\n", error)); + } + + space = (trace->end - trace->start) - trace->wpos; + DPRINTF(VOLUMES, ("write start = %d, end = %d, pos = %d, wpos = %d, " + "space = %d\n", trace->start, trace->end, trace->pos, + trace->wpos, space)); + DPRINTF(VOLUMES, ("finished writing out logvol integrity descriptor " + "successfull\n")); + + return error; +} + +/* --------------------------------------------------------------------- */ + +static int +udf_read_physical_partition_spacetables(struct udf_mount *ump) +{ + union dscrptr *dscr; + /* struct udf_args *args = &ump->mount_args; */ + struct part_desc *partd; + struct part_hdr_desc *parthdr; + struct udf_bitmap *bitmap; + uint32_t phys_part; + uint32_t lb_num, len; + int error, dscr_type; + + /* unallocated space map */ + for (phys_part = 0; phys_part < UDF_PARTITIONS; phys_part++) { + partd = ump->partitions[phys_part]; + if (partd == NULL) + continue; + parthdr = &partd->_impl_use.part_hdr; + + lb_num = udf_rw32(partd->start_loc); + lb_num += udf_rw32(parthdr->unalloc_space_bitmap.lb_num); + len = udf_rw32(parthdr->unalloc_space_bitmap.len); + if (len == 0) + continue; + + DPRINTF(VOLUMES, ("Read unalloc. space bitmap %d\n", lb_num)); + error = udf_read_phys_dscr(ump, lb_num, M_UDFVOLD, &dscr); + if (!error && dscr) { + /* analyse */ + dscr_type = udf_rw16(dscr->tag.id); + if (dscr_type == TAGID_SPACE_BITMAP) { + DPRINTF(VOLUMES, ("Accepting space bitmap\n")); + ump->part_unalloc_dscr[phys_part] = &dscr->sbd; + + /* fill in ump->part_unalloc_bits */ + bitmap = &ump->part_unalloc_bits[phys_part]; + bitmap->blob = (uint8_t *) dscr; + bitmap->bits = dscr->sbd.data; + bitmap->max_offset = udf_rw32(dscr->sbd.num_bits); + bitmap->pages = NULL; /* TODO */ + bitmap->data_pos = 0; + bitmap->metadata_pos = 0; + } else { + free(dscr, M_UDFVOLD); + + printf( "UDF mount: error reading unallocated " + "space bitmap\n"); + return EROFS; + } + } else { + /* blank not allowed */ + printf("UDF mount: blank unallocated space bitmap\n"); + return EROFS; + } + } + + /* unallocated space table (not supported) */ + for (phys_part = 0; phys_part < UDF_PARTITIONS; phys_part++) { + partd = ump->partitions[phys_part]; + if (partd == NULL) + continue; + parthdr = &partd->_impl_use.part_hdr; + + len = udf_rw32(parthdr->unalloc_space_table.len); + if (len) { + printf("UDF mount: space tables not supported\n"); + return EROFS; + } + } + + /* freed space map */ + for (phys_part = 0; phys_part < UDF_PARTITIONS; phys_part++) { + partd = ump->partitions[phys_part]; + if (partd == NULL) + continue; + parthdr = &partd->_impl_use.part_hdr; + + /* freed space map */ + lb_num = udf_rw32(partd->start_loc); + lb_num += udf_rw32(parthdr->freed_space_bitmap.lb_num); + len = udf_rw32(parthdr->freed_space_bitmap.len); + if (len == 0) + continue; + + DPRINTF(VOLUMES, ("Read unalloc. space bitmap %d\n", lb_num)); + error = udf_read_phys_dscr(ump, lb_num, M_UDFVOLD, &dscr); + if (!error && dscr) { + /* analyse */ + dscr_type = udf_rw16(dscr->tag.id); + if (dscr_type == TAGID_SPACE_BITMAP) { + DPRINTF(VOLUMES, ("Accepting space bitmap\n")); + ump->part_freed_dscr[phys_part] = &dscr->sbd; + + /* fill in ump->part_freed_bits */ + bitmap = &ump->part_unalloc_bits[phys_part]; + bitmap->blob = (uint8_t *) dscr; + bitmap->bits = dscr->sbd.data; + bitmap->max_offset = udf_rw32(dscr->sbd.num_bits); + bitmap->pages = NULL; /* TODO */ + bitmap->data_pos = 0; + bitmap->metadata_pos = 0; + } else { + free(dscr, M_UDFVOLD); + + printf( "UDF mount: error reading freed " + "space bitmap\n"); + return EROFS; + } + } else { + /* blank not allowed */ + printf("UDF mount: blank freed space bitmap\n"); + return EROFS; + } + } + + /* freed space table (not supported) */ + for (phys_part = 0; phys_part < UDF_PARTITIONS; phys_part++) { + partd = ump->partitions[phys_part]; + if (partd == NULL) + continue; + parthdr = &partd->_impl_use.part_hdr; + + len = udf_rw32(parthdr->freed_space_table.len); + if (len) { + printf("UDF mount: space tables not supported\n"); + return EROFS; + } + } + + return 0; +} + + +/* TODO implement async writeout */ +int +udf_write_physical_partition_spacetables(struct udf_mount *ump, int waitfor) +{ + union dscrptr *dscr; + /* struct udf_args *args = &ump->mount_args; */ + struct part_desc *partd; + struct part_hdr_desc *parthdr; + uint32_t phys_part; + uint32_t lb_num, len, ptov; + int error_all, error; + + error_all = 0; + /* unallocated space map */ + for (phys_part = 0; phys_part < UDF_PARTITIONS; phys_part++) { + partd = ump->partitions[phys_part]; + if (partd == NULL) + continue; + parthdr = &partd->_impl_use.part_hdr; + + ptov = udf_rw32(partd->start_loc); + lb_num = udf_rw32(parthdr->unalloc_space_bitmap.lb_num); + len = udf_rw32(parthdr->unalloc_space_bitmap.len); + if (len == 0) + continue; + + DPRINTF(VOLUMES, ("Write unalloc. space bitmap %d\n", + lb_num + ptov)); + dscr = (union dscrptr *) ump->part_unalloc_dscr[phys_part]; + error = udf_write_phys_dscr_sync(ump, NULL, UDF_C_DSCR, + (union dscrptr *) dscr, + ptov + lb_num, lb_num); + if (error) { + DPRINTF(VOLUMES, ("\tfailed!! (error %d)\n", error)); + error_all = error; + } + } + + /* freed space map */ + for (phys_part = 0; phys_part < UDF_PARTITIONS; phys_part++) { + partd = ump->partitions[phys_part]; + if (partd == NULL) + continue; + parthdr = &partd->_impl_use.part_hdr; + + /* freed space map */ + ptov = udf_rw32(partd->start_loc); + lb_num = udf_rw32(parthdr->freed_space_bitmap.lb_num); + len = udf_rw32(parthdr->freed_space_bitmap.len); + if (len == 0) + continue; + + DPRINTF(VOLUMES, ("Write freed space bitmap %d\n", + lb_num + ptov)); + dscr = (union dscrptr *) ump->part_freed_dscr[phys_part]; + error = udf_write_phys_dscr_sync(ump, NULL, UDF_C_DSCR, + (union dscrptr *) dscr, + ptov + lb_num, lb_num); + if (error) { + DPRINTF(VOLUMES, ("\tfailed!! (error %d)\n", error)); + error_all = error; + } + } + + return error_all; +} + + +static int +udf_read_metadata_partition_spacetable(struct udf_mount *ump) +{ + struct udf_node *bitmap_node; + union dscrptr *dscr; + struct udf_bitmap *bitmap; + uint64_t inflen; + int error, dscr_type; + + bitmap_node = ump->metadatabitmap_node; + + /* only read in when metadata bitmap node is read in */ + if (bitmap_node == NULL) + return 0; + + if (bitmap_node->fe) { + inflen = udf_rw64(bitmap_node->fe->inf_len); + } else { + KASSERT(bitmap_node->efe); + inflen = udf_rw64(bitmap_node->efe->inf_len); + } + + DPRINTF(VOLUMES, ("Reading metadata space bitmap for " + "%"PRIu64" bytes\n", inflen)); + + /* allocate space for bitmap */ + dscr = malloc(inflen, M_UDFVOLD, M_CANFAIL | M_WAITOK); + if (!dscr) + return ENOMEM; + + /* set vnode type to regular file or we can't read from it! */ + bitmap_node->vnode->v_type = VREG; + + /* read in complete metadata bitmap file */ + error = vn_rdwr(UIO_READ, bitmap_node->vnode, + dscr, + inflen, 0, + UIO_SYSSPACE, + IO_SYNC | IO_ALTSEMANTICS, FSCRED, + NULL, NULL); + if (error) { + DPRINTF(VOLUMES, ("Error reading metadata space bitmap\n")); + goto errorout; + } + + /* analyse */ + dscr_type = udf_rw16(dscr->tag.id); + if (dscr_type == TAGID_SPACE_BITMAP) { + DPRINTF(VOLUMES, ("Accepting metadata space bitmap\n")); + ump->metadata_unalloc_dscr = &dscr->sbd; + + /* fill in bitmap bits */ + bitmap = &ump->metadata_unalloc_bits; + bitmap->blob = (uint8_t *) dscr; + bitmap->bits = dscr->sbd.data; + bitmap->max_offset = udf_rw32(dscr->sbd.num_bits); + bitmap->pages = NULL; /* TODO */ + bitmap->data_pos = 0; + bitmap->metadata_pos = 0; + } else { + DPRINTF(VOLUMES, ("No valid bitmap found!\n")); + goto errorout; + } + + return 0; + +errorout: + free(dscr, M_UDFVOLD); + printf( "UDF mount: error reading unallocated " + "space bitmap for metadata partition\n"); + return EROFS; +} + + +int +udf_write_metadata_partition_spacetable(struct udf_mount *ump, int waitfor) +{ + struct udf_node *bitmap_node; + union dscrptr *dscr; + uint64_t new_inflen; + int dummy, error; + + bitmap_node = ump->metadatabitmap_node; + + /* only write out when metadata bitmap node is known */ + if (bitmap_node == NULL) + return 0; + + if (!bitmap_node->fe) { + KASSERT(bitmap_node->efe); + } + + /* reduce length to zero */ + dscr = (union dscrptr *) ump->metadata_unalloc_dscr; + new_inflen = udf_tagsize(dscr, 1); + + DPRINTF(VOLUMES, ("Resize and write out metadata space bitmap from " + "%"PRIu64" to %"PRIu64" bytes\n", inflen, new_inflen)); + + error = udf_resize_node(bitmap_node, new_inflen, &dummy); + if (error) + printf("Error resizing metadata space bitmap\n"); + + error = vn_rdwr(UIO_WRITE, bitmap_node->vnode, + dscr, + new_inflen, 0, + UIO_SYSSPACE, + IO_ALTSEMANTICS, FSCRED, + NULL, NULL); + + bitmap_node->i_flags |= IN_MODIFIED; + error = vflushbuf(bitmap_node->vnode, FSYNC_WAIT); + if (error == 0) + error = VOP_FSYNC(bitmap_node->vnode, + FSCRED, FSYNC_WAIT, 0, 0); + + if (error) + printf( "Error writing out metadata partition unalloced " + "space bitmap!\n"); + + return error; +} + + +/* --------------------------------------------------------------------- */ + +/* + * Checks if ump's vds information is correct and complete + */ + +int +udf_process_vds(struct udf_mount *ump) { + union udf_pmap *mapping; + /* struct udf_args *args = &ump->mount_args; */ + struct logvol_int_desc *lvint; + struct udf_logvol_info *lvinfo; + uint32_t n_pm; + uint8_t *pmap_pos; + char *domain_name, *map_name; + const char *check_name; + char bits[128]; + int pmap_stype, pmap_size; + int pmap_type, log_part, phys_part, raw_phys_part, maps_on; + int n_phys, n_virt, n_spar, n_meta; + int len; + + if (ump == NULL) + return ENOENT; + + /* we need at least an anchor (trivial, but for safety) */ + if (ump->anchors[0] == NULL) + return EINVAL; + + /* we need at least one primary and one logical volume descriptor */ + if ((ump->primary_vol == NULL) || (ump->logical_vol) == NULL) + return EINVAL; + + /* we need at least one partition descriptor */ + if (ump->partitions[0] == NULL) + return EINVAL; + + /* check logical volume sector size verses device sector size */ + if (udf_rw32(ump->logical_vol->lb_size) != ump->discinfo.sector_size) { + printf("UDF mount: format violation, lb_size != sector size\n"); + return EINVAL; + } + + /* check domain name */ + domain_name = ump->logical_vol->domain_id.id; + if (strncmp(domain_name, "*OSTA UDF Compliant", 20)) { + printf("mount_udf: disc not OSTA UDF Compliant, aborting\n"); + return EINVAL; + } + + /* retrieve logical volume integrity sequence */ + (void)udf_retrieve_lvint(ump); + + /* + * We need at least one logvol integrity descriptor recorded. Note + * that its OK to have an open logical volume integrity here. The VAT + * will close/update the integrity. + */ + if (ump->logvol_integrity == NULL) + return EINVAL; + + /* process derived structures */ + n_pm = udf_rw32(ump->logical_vol->n_pm); /* num partmaps */ + lvint = ump->logvol_integrity; + lvinfo = (struct udf_logvol_info *) (&lvint->tables[2 * n_pm]); + ump->logvol_info = lvinfo; + + /* TODO check udf versions? */ + + /* + * check logvol mappings: effective virt->log partmap translation + * check and recording of the mapping results. Saves expensive + * strncmp() in tight places. + */ + DPRINTF(VOLUMES, ("checking logvol mappings\n")); + n_pm = udf_rw32(ump->logical_vol->n_pm); /* num partmaps */ + pmap_pos = ump->logical_vol->maps; + + if (n_pm > UDF_PMAPS) { + printf("UDF mount: too many mappings\n"); + return EINVAL; + } + + /* count types and set partition numbers */ + ump->data_part = ump->node_part = ump->fids_part = 0; + n_phys = n_virt = n_spar = n_meta = 0; + for (log_part = 0; log_part < n_pm; log_part++) { + mapping = (union udf_pmap *) pmap_pos; + pmap_stype = pmap_pos[0]; + pmap_size = pmap_pos[1]; + switch (pmap_stype) { + case 1: /* physical mapping */ + /* volseq = udf_rw16(mapping->pm1.vol_seq_num); */ + raw_phys_part = udf_rw16(mapping->pm1.part_num); + pmap_type = UDF_VTOP_TYPE_PHYS; + n_phys++; + ump->data_part = log_part; + ump->node_part = log_part; + ump->fids_part = log_part; + break; + case 2: /* virtual/sparable/meta mapping */ + map_name = mapping->pm2.part_id.id; + /* volseq = udf_rw16(mapping->pm2.vol_seq_num); */ + raw_phys_part = udf_rw16(mapping->pm2.part_num); + pmap_type = UDF_VTOP_TYPE_UNKNOWN; + len = UDF_REGID_ID_SIZE; + + check_name = "*UDF Virtual Partition"; + if (strncmp(map_name, check_name, len) == 0) { + pmap_type = UDF_VTOP_TYPE_VIRT; + n_virt++; + ump->node_part = log_part; + break; + } + check_name = "*UDF Sparable Partition"; + if (strncmp(map_name, check_name, len) == 0) { + pmap_type = UDF_VTOP_TYPE_SPARABLE; + n_spar++; + ump->data_part = log_part; + ump->node_part = log_part; + ump->fids_part = log_part; + break; + } + check_name = "*UDF Metadata Partition"; + if (strncmp(map_name, check_name, len) == 0) { + pmap_type = UDF_VTOP_TYPE_META; + n_meta++; + ump->node_part = log_part; + ump->fids_part = log_part; + break; + } + break; + default: + return EINVAL; + } + + /* + * BUGALERT: some rogue implementations use random physical + * partition numbers to break other implementations so lookup + * the number. + */ + phys_part = udf_find_raw_phys(ump, raw_phys_part); + + DPRINTF(VOLUMES, ("\t%d -> %d(%d) type %d\n", log_part, + raw_phys_part, phys_part, pmap_type)); + + if (phys_part == UDF_PARTITIONS) + return EINVAL; + if (pmap_type == UDF_VTOP_TYPE_UNKNOWN) + return EINVAL; + + ump->vtop [log_part] = phys_part; + ump->vtop_tp[log_part] = pmap_type; + + pmap_pos += pmap_size; + } + /* not winning the beauty contest */ + ump->vtop_tp[UDF_VTOP_RAWPART] = UDF_VTOP_TYPE_RAW; + + /* test some basic UDF assertions/requirements */ + if ((n_virt > 1) || (n_spar > 1) || (n_meta > 1)) + return EINVAL; + + if (n_virt) { + if ((n_phys == 0) || n_spar || n_meta) + return EINVAL; + } + if (n_spar + n_phys == 0) + return EINVAL; + + /* select allocation type for each logical partition */ + for (log_part = 0; log_part < n_pm; log_part++) { + maps_on = ump->vtop[log_part]; + switch (ump->vtop_tp[log_part]) { + case UDF_VTOP_TYPE_PHYS : + assert(maps_on == log_part); + ump->vtop_alloc[log_part] = UDF_ALLOC_SPACEMAP; + break; + case UDF_VTOP_TYPE_VIRT : + ump->vtop_alloc[log_part] = UDF_ALLOC_VAT; + ump->vtop_alloc[maps_on] = UDF_ALLOC_SEQUENTIAL; + break; + case UDF_VTOP_TYPE_SPARABLE : + assert(maps_on == log_part); + ump->vtop_alloc[log_part] = UDF_ALLOC_SPACEMAP; + break; + case UDF_VTOP_TYPE_META : + ump->vtop_alloc[log_part] = UDF_ALLOC_METABITMAP; + if (ump->discinfo.mmc_cur & MMC_CAP_PSEUDOOVERWRITE) { + /* special case for UDF 2.60 */ + ump->vtop_alloc[log_part] = UDF_ALLOC_METASEQUENTIAL; + ump->vtop_alloc[maps_on] = UDF_ALLOC_SEQUENTIAL; + } + break; + default: + panic("bad alloction type in udf's ump->vtop\n"); + } + } + + /* determine logical volume open/closure actions */ + if (n_virt) { + ump->lvopen = 0; + if (ump->discinfo.last_session_state == MMC_STATE_EMPTY) + ump->lvopen |= UDF_OPEN_SESSION ; + ump->lvclose = UDF_WRITE_VAT; + if (ump->mount_args.udfmflags & UDFMNT_CLOSESESSION) + ump->lvclose |= UDF_CLOSE_SESSION; + } else { + /* `normal' rewritable or non sequential media */ + ump->lvopen = UDF_WRITE_LVINT; + ump->lvclose = UDF_WRITE_LVINT; + if ((ump->discinfo.mmc_cur & MMC_CAP_REWRITABLE) == 0) + ump->lvopen |= UDF_APPENDONLY_LVINT; + if ((ump->discinfo.mmc_cur & MMC_CAP_PSEUDOOVERWRITE)) + ump->lvopen &= ~UDF_APPENDONLY_LVINT; + } + + /* + * Determine sheduler error behaviour. For virtual partitions, update + * the trackinfo; for sparable partitions replace a whole block on the + * sparable table. Allways requeue. + */ + ump->lvreadwrite = 0; + if (n_virt) + ump->lvreadwrite = UDF_UPDATE_TRACKINFO; + if (n_spar) + ump->lvreadwrite = UDF_REMAP_BLOCK; + + /* + * Select our sheduler + */ + ump->strategy = &udf_strat_rmw; + if (n_virt || (ump->discinfo.mmc_cur & MMC_CAP_PSEUDOOVERWRITE)) + ump->strategy = &udf_strat_sequential; + if ((ump->discinfo.mmc_class == MMC_CLASS_DISC) || + (ump->discinfo.mmc_class == MMC_CLASS_UNKN)) + ump->strategy = &udf_strat_direct; + if (n_spar) + ump->strategy = &udf_strat_rmw; + +#if 0 + /* read-only access won't benefit from the other shedulers */ + if (ump->vfs_mountp->mnt_flag & MNT_RDONLY) + ump->strategy = &udf_strat_direct; +#endif + + /* print results */ + DPRINTF(VOLUMES, ("\tdata partition %d\n", ump->data_part)); + DPRINTF(VOLUMES, ("\t\talloc scheme %d\n", ump->vtop_alloc[ump->data_part])); + DPRINTF(VOLUMES, ("\tnode partition %d\n", ump->node_part)); + DPRINTF(VOLUMES, ("\t\talloc scheme %d\n", ump->vtop_alloc[ump->node_part])); + DPRINTF(VOLUMES, ("\tfids partition %d\n", ump->fids_part)); + DPRINTF(VOLUMES, ("\t\talloc scheme %d\n", ump->vtop_alloc[ump->fids_part])); + + snprintb(bits, sizeof(bits), UDFLOGVOL_BITS, ump->lvopen); + DPRINTF(VOLUMES, ("\tactions on logvol open %s\n", bits)); + snprintb(bits, sizeof(bits), UDFLOGVOL_BITS, ump->lvclose); + DPRINTF(VOLUMES, ("\tactions on logvol close %s\n", bits)); + snprintb(bits, sizeof(bits), UDFONERROR_BITS, ump->lvreadwrite); + DPRINTF(VOLUMES, ("\tactions on logvol errors %s\n", bits)); + + DPRINTF(VOLUMES, ("\tselected sheduler `%s`\n", + (ump->strategy == &udf_strat_direct) ? "Direct" : + (ump->strategy == &udf_strat_sequential) ? "Sequential" : + (ump->strategy == &udf_strat_rmw) ? "RMW" : "UNKNOWN!")); + + /* signal its OK for now */ + return 0; +} + +/* --------------------------------------------------------------------- */ + +/* + * Update logical volume name in all structures that keep a record of it. We + * use memmove since each of them might be specified as a source. + * + * Note that it doesn't update the VAT structure! + */ + +static void +udf_update_logvolname(struct udf_mount *ump, char *logvol_id) +{ + struct logvol_desc *lvd = NULL; + struct fileset_desc *fsd = NULL; + struct udf_lv_info *lvi = NULL; + + DPRINTF(VOLUMES, ("Updating logical volume name\n")); + lvd = ump->logical_vol; + fsd = ump->fileset_desc; + if (ump->implementation) + lvi = &ump->implementation->_impl_use.lv_info; + + /* logvol's id might be specified as origional so use memmove here */ + memmove(lvd->logvol_id, logvol_id, 128); + if (fsd) + memmove(fsd->logvol_id, logvol_id, 128); + if (lvi) + memmove(lvi->logvol_id, logvol_id, 128); +} + +/* --------------------------------------------------------------------- */ + +void +udf_inittag(struct udf_mount *ump, struct desc_tag *tag, int tagid, + uint32_t sector) +{ + assert(ump->logical_vol); + + tag->id = udf_rw16(tagid); + tag->descriptor_ver = ump->logical_vol->tag.descriptor_ver; + tag->cksum = 0; + tag->reserved = 0; + tag->serial_num = ump->logical_vol->tag.serial_num; + tag->tag_loc = udf_rw32(sector); +} + + +uint64_t +udf_advance_uniqueid(struct udf_mount *ump) +{ + uint64_t unique_id; + + mutex_enter(&ump->logvol_mutex); + unique_id = udf_rw64(ump->logvol_integrity->lvint_next_unique_id); + if (unique_id < 0x10) + unique_id = 0x10; + ump->logvol_integrity->lvint_next_unique_id = udf_rw64(unique_id + 1); + mutex_exit(&ump->logvol_mutex); + + return unique_id; +} + + +static void +udf_adjust_filecount(struct udf_node *udf_node, int sign) +{ + struct udf_mount *ump = udf_node->ump; + uint32_t num_dirs, num_files; + int udf_file_type; + + /* get file type */ + if (udf_node->fe) { + udf_file_type = udf_node->fe->icbtag.file_type; + } else { + udf_file_type = udf_node->efe->icbtag.file_type; + } + + /* adjust file count */ + mutex_enter(&ump->allocate_mutex); + if (udf_file_type == UDF_ICB_FILETYPE_DIRECTORY) { + num_dirs = udf_rw32(ump->logvol_info->num_directories); + ump->logvol_info->num_directories = + udf_rw32((num_dirs + sign)); + } else { + num_files = udf_rw32(ump->logvol_info->num_files); + ump->logvol_info->num_files = + udf_rw32((num_files + sign)); + } + mutex_exit(&ump->allocate_mutex); +} + + +void +udf_osta_charset(struct charspec *charspec) +{ + memset(charspec, 0, sizeof(struct charspec)); + charspec->type = 0; + strcpy((char *) charspec->inf, "OSTA Compressed Unicode"); +} + + +/* first call udf_set_regid and then the suffix */ +void +udf_set_regid(struct regid *regid, char const *name) +{ + memset(regid, 0, sizeof(struct regid)); + regid->flags = 0; /* not dirty and not protected */ + strcpy((char *) regid->id, name); +} + + +void +udf_add_domain_regid(struct udf_mount *ump, struct regid *regid) +{ + uint16_t *ver; + + ver = (uint16_t *) regid->id_suffix; + *ver = ump->logvol_info->min_udf_readver; +} + + +void +udf_add_udf_regid(struct udf_mount *ump, struct regid *regid) +{ + uint16_t *ver; + + ver = (uint16_t *) regid->id_suffix; + *ver = ump->logvol_info->min_udf_readver; + + regid->id_suffix[2] = 4; /* unix */ + regid->id_suffix[3] = 8; /* NetBSD */ +} + + +void +udf_add_impl_regid(struct udf_mount *ump, struct regid *regid) +{ + regid->id_suffix[0] = 4; /* unix */ + regid->id_suffix[1] = 8; /* NetBSD */ +} + + +void +udf_add_app_regid(struct udf_mount *ump, struct regid *regid) +{ + regid->id_suffix[0] = APP_VERSION_MAIN; + regid->id_suffix[1] = APP_VERSION_SUB; +} + +static int +udf_create_parentfid(struct udf_mount *ump, struct fileid_desc *fid, + struct long_ad *parent, uint64_t unique_id) +{ + /* the size of an empty FID is 38 but needs to be a multiple of 4 */ + int fidsize = 40; + + udf_inittag(ump, &fid->tag, TAGID_FID, udf_rw32(parent->loc.lb_num)); + fid->file_version_num = udf_rw16(1); /* UDF 2.3.4.1 */ + fid->file_char = UDF_FILE_CHAR_DIR | UDF_FILE_CHAR_PAR; + fid->icb = *parent; + fid->icb.longad_uniqueid = udf_rw32((uint32_t) unique_id); + fid->tag.desc_crc_len = udf_rw16(fidsize - UDF_DESC_TAG_LENGTH); + (void) udf_validate_tag_and_crc_sums((union dscrptr *) fid); + + return fidsize; +} + +/* --------------------------------------------------------------------- */ + +/* + * Extended attribute support. UDF knows of 3 places for extended attributes: + * + * (a) inside the file's (e)fe in the length of the extended attribute area + * before the allocation descriptors/filedata + * + * (b) in a file referenced by (e)fe->ext_attr_icb and + * + * (c) in the e(fe)'s associated stream directory that can hold various + * sub-files. In the stream directory a few fixed named subfiles are reserved + * for NT/Unix ACL's and OS/2 attributes. + * + * NOTE: Extended attributes are read randomly but allways written + * *atomicaly*. For ACL's this interface is propably different but not known + * to me yet. + * + * Order of extended attributes in a space : + * ECMA 167 EAs + * Non block aligned Implementation Use EAs + * Block aligned Implementation Use EAs + * Application Use EAs + */ + +static int +udf_impl_extattr_check(struct impl_extattr_entry *implext) +{ + uint16_t *spos; + + if (strncmp(implext->imp_id.id, "*UDF", 4) == 0) { + /* checksum valid? */ + DPRINTF(EXTATTR, ("checking UDF impl. attr checksum\n")); + spos = (uint16_t *) implext->data; + if (udf_rw16(*spos) != udf_ea_cksum((uint8_t *) implext)) + return EINVAL; + } + return 0; +} + +static void +udf_calc_impl_extattr_checksum(struct impl_extattr_entry *implext) +{ + uint16_t *spos; + + if (strncmp(implext->imp_id.id, "*UDF", 4) == 0) { + /* set checksum */ + spos = (uint16_t *) implext->data; + *spos = udf_rw16(udf_ea_cksum((uint8_t *) implext)); + } +} + + +int +udf_extattr_search_intern(struct udf_node *node, + uint32_t sattr, char const *sattrname, + uint32_t *offsetp, uint32_t *lengthp) +{ + struct extattrhdr_desc *eahdr; + struct extattr_entry *attrhdr; + struct impl_extattr_entry *implext; + uint32_t offset, a_l, sector_size; + int32_t l_ea; + uint8_t *pos; + int error; + + /* get mountpoint */ + sector_size = node->ump->discinfo.sector_size; + + /* get information from fe/efe */ + if (node->fe) { + l_ea = udf_rw32(node->fe->l_ea); + eahdr = (struct extattrhdr_desc *) node->fe->data; + } else { + assert(node->efe); + l_ea = udf_rw32(node->efe->l_ea); + eahdr = (struct extattrhdr_desc *) node->efe->data; + } + + /* something recorded here? */ + if (l_ea == 0) + return ENOENT; + + /* check extended attribute tag; what to do if it fails? */ + error = udf_check_tag(eahdr); + if (error) + return EINVAL; + if (udf_rw16(eahdr->tag.id) != TAGID_EXTATTR_HDR) + return EINVAL; + error = udf_check_tag_payload(eahdr, sizeof(struct extattrhdr_desc)); + if (error) + return EINVAL; + + DPRINTF(EXTATTR, ("Found %d bytes of extended attributes\n", l_ea)); + + /* looking for Ecma-167 attributes? */ + offset = sizeof(struct extattrhdr_desc); + + /* looking for either implemenation use or application use */ + if (sattr == 2048) { /* [4/48.10.8] */ + offset = udf_rw32(eahdr->impl_attr_loc); + if (offset == UDF_IMPL_ATTR_LOC_NOT_PRESENT) + return ENOENT; + } + if (sattr == 65536) { /* [4/48.10.9] */ + offset = udf_rw32(eahdr->appl_attr_loc); + if (offset == UDF_APPL_ATTR_LOC_NOT_PRESENT) + return ENOENT; + } + + /* paranoia check offset and l_ea */ + if (l_ea + offset >= sector_size - sizeof(struct extattr_entry)) + return EINVAL; + + DPRINTF(EXTATTR, ("Starting at offset %d\n", offset)); + + /* find our extended attribute */ + l_ea -= offset; + pos = (uint8_t *) eahdr + offset; + + while (l_ea >= sizeof(struct extattr_entry)) { + DPRINTF(EXTATTR, ("%d extended attr bytes left\n", l_ea)); + attrhdr = (struct extattr_entry *) pos; + implext = (struct impl_extattr_entry *) pos; + + /* get complete attribute length and check for roque values */ + a_l = udf_rw32(attrhdr->a_l); + DPRINTF(EXTATTR, ("attribute %d:%d, len %d/%d\n", + udf_rw32(attrhdr->type), + attrhdr->subtype, a_l, l_ea)); + if ((a_l == 0) || (a_l > l_ea)) + return EINVAL; + + if (attrhdr->type != sattr) + goto next_attribute; + + /* we might have found it! */ + if (attrhdr->type < 2048) { /* Ecma-167 attribute */ + *offsetp = offset; + *lengthp = a_l; + return 0; /* success */ + } + + /* + * Implementation use and application use extended attributes + * have a name to identify. They share the same structure only + * UDF implementation use extended attributes have a checksum + * we need to check + */ + + DPRINTF(EXTATTR, ("named attribute %s\n", implext->imp_id.id)); + if (strcmp(implext->imp_id.id, sattrname) == 0) { + /* we have found our appl/implementation attribute */ + *offsetp = offset; + *lengthp = a_l; + return 0; /* success */ + } + +next_attribute: + /* next attribute */ + pos += a_l; + l_ea -= a_l; + offset += a_l; + } + /* not found */ + return ENOENT; +} + + +static void +udf_extattr_insert_internal(struct udf_mount *ump, union dscrptr *dscr, + struct extattr_entry *extattr) +{ + struct file_entry *fe; + struct extfile_entry *efe; + struct extattrhdr_desc *extattrhdr; + struct impl_extattr_entry *implext; + uint32_t impl_attr_loc, appl_attr_loc, l_ea, a_l, exthdr_len; + uint32_t *l_eap, l_ad; + uint16_t *spos; + uint8_t *bpos, *data; + + if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) { + fe = &dscr->fe; + data = fe->data; + l_eap = &fe->l_ea; + l_ad = udf_rw32(fe->l_ad); + } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) { + efe = &dscr->efe; + data = efe->data; + l_eap = &efe->l_ea; + l_ad = udf_rw32(efe->l_ad); + } else { + panic("Bad tag passed to udf_extattr_insert_internal"); + } + + /* can't append already written to file descriptors yet */ + assert(l_ad == 0); + + /* should have a header! */ + extattrhdr = (struct extattrhdr_desc *) data; + l_ea = udf_rw32(*l_eap); + if (l_ea == 0) { + /* create empty extended attribute header */ + exthdr_len = sizeof(struct extattrhdr_desc); + + udf_inittag(ump, &extattrhdr->tag, TAGID_EXTATTR_HDR, + /* loc */ 0); + extattrhdr->impl_attr_loc = udf_rw32(exthdr_len); + extattrhdr->appl_attr_loc = udf_rw32(exthdr_len); + extattrhdr->tag.desc_crc_len = udf_rw16(8); + + /* record extended attribute header length */ + l_ea = exthdr_len; + *l_eap = udf_rw32(l_ea); + } + + /* extract locations */ + impl_attr_loc = udf_rw32(extattrhdr->impl_attr_loc); + appl_attr_loc = udf_rw32(extattrhdr->appl_attr_loc); + if (impl_attr_loc == UDF_IMPL_ATTR_LOC_NOT_PRESENT) + impl_attr_loc = l_ea; + if (appl_attr_loc == UDF_IMPL_ATTR_LOC_NOT_PRESENT) + appl_attr_loc = l_ea; + + /* Ecma 167 EAs */ + if (udf_rw32(extattr->type) < 2048) { + assert(impl_attr_loc == l_ea); + assert(appl_attr_loc == l_ea); + } + + /* implementation use extended attributes */ + if (udf_rw32(extattr->type) == 2048) { + assert(appl_attr_loc == l_ea); + + /* calculate and write extended attribute header checksum */ + implext = (struct impl_extattr_entry *) extattr; + assert(udf_rw32(implext->iu_l) == 4); /* [UDF 3.3.4.5] */ + spos = (uint16_t *) implext->data; + *spos = udf_rw16(udf_ea_cksum((uint8_t *) implext)); + } + + /* application use extended attributes */ + assert(udf_rw32(extattr->type) != 65536); + assert(appl_attr_loc == l_ea); + + /* append the attribute at the end of the current space */ + bpos = data + udf_rw32(*l_eap); + a_l = udf_rw32(extattr->a_l); + + /* update impl. attribute locations */ + if (udf_rw32(extattr->type) < 2048) { + impl_attr_loc = l_ea + a_l; + appl_attr_loc = l_ea + a_l; + } + if (udf_rw32(extattr->type) == 2048) { + appl_attr_loc = l_ea + a_l; + } + + /* copy and advance */ + memcpy(bpos, extattr, a_l); + l_ea += a_l; + *l_eap = udf_rw32(l_ea); + + /* do the `dance` again backwards */ + if (udf_rw16(ump->logical_vol->tag.descriptor_ver) != 2) { + if (impl_attr_loc == l_ea) + impl_attr_loc = UDF_IMPL_ATTR_LOC_NOT_PRESENT; + if (appl_attr_loc == l_ea) + appl_attr_loc = UDF_APPL_ATTR_LOC_NOT_PRESENT; + } + + /* store offsets */ + extattrhdr->impl_attr_loc = udf_rw32(impl_attr_loc); + extattrhdr->appl_attr_loc = udf_rw32(appl_attr_loc); +} + + +/* --------------------------------------------------------------------- */ + +static int +udf_update_lvid_from_vat_extattr(struct udf_node *vat_node) +{ + struct udf_mount *ump; + struct udf_logvol_info *lvinfo; + struct impl_extattr_entry *implext; + struct vatlvext_extattr_entry lvext; + const char *extstr = "*UDF VAT LVExtension"; + uint64_t vat_uniqueid; + uint32_t offset, a_l; + uint8_t *ea_start, *lvextpos; + int error; + + /* get mountpoint and lvinfo */ + ump = vat_node->ump; + lvinfo = ump->logvol_info; + + /* get information from fe/efe */ + if (vat_node->fe) { + vat_uniqueid = udf_rw64(vat_node->fe->unique_id); + ea_start = vat_node->fe->data; + } else { + vat_uniqueid = udf_rw64(vat_node->efe->unique_id); + ea_start = vat_node->efe->data; + } + + error = udf_extattr_search_intern(vat_node, 2048, extstr, &offset, &a_l); + if (error) + return error; + + implext = (struct impl_extattr_entry *) (ea_start + offset); + error = udf_impl_extattr_check(implext); + if (error) + return error; + + /* paranoia */ + if (a_l != sizeof(*implext) -1 + udf_rw32(implext->iu_l) + sizeof(lvext)) { + DPRINTF(VOLUMES, ("VAT LVExtension size doesn't compute\n")); + return EINVAL; + } + + /* + * we have found our "VAT LVExtension attribute. BUT due to a + * bug in the specification it might not be word aligned so + * copy first to avoid panics on some machines (!!) + */ + DPRINTF(VOLUMES, ("Found VAT LVExtension attr\n")); + lvextpos = implext->data + udf_rw32(implext->iu_l); + memcpy(&lvext, lvextpos, sizeof(lvext)); + + /* check if it was updated the last time */ + if (udf_rw64(lvext.unique_id_chk) == vat_uniqueid) { + lvinfo->num_files = lvext.num_files; + lvinfo->num_directories = lvext.num_directories; + udf_update_logvolname(ump, lvext.logvol_id); + } else { + DPRINTF(VOLUMES, ("VAT LVExtension out of date\n")); + /* replace VAT LVExt by free space EA */ + memset(implext->imp_id.id, 0, UDF_REGID_ID_SIZE); + strcpy(implext->imp_id.id, "*UDF FreeEASpace"); + udf_calc_impl_extattr_checksum(implext); + } + + return 0; +} + + +static int +udf_update_vat_extattr_from_lvid(struct udf_node *vat_node) +{ + struct udf_mount *ump; + struct udf_logvol_info *lvinfo; + struct impl_extattr_entry *implext; + struct vatlvext_extattr_entry lvext; + const char *extstr = "*UDF VAT LVExtension"; + uint64_t vat_uniqueid; + uint32_t offset, a_l; + uint8_t *ea_start, *lvextpos; + int error; + + /* get mountpoint and lvinfo */ + ump = vat_node->ump; + lvinfo = ump->logvol_info; + + /* get information from fe/efe */ + if (vat_node->fe) { + vat_uniqueid = udf_rw64(vat_node->fe->unique_id); + ea_start = vat_node->fe->data; + } else { + vat_uniqueid = udf_rw64(vat_node->efe->unique_id); + ea_start = vat_node->efe->data; + } + + error = udf_extattr_search_intern(vat_node, 2048, extstr, &offset, &a_l); + if (error) + return error; + /* found, it existed */ + + /* paranoia */ + implext = (struct impl_extattr_entry *) (ea_start + offset); + error = udf_impl_extattr_check(implext); + if (error) { + DPRINTF(VOLUMES, ("VAT LVExtension bad on update\n")); + return error; + } + /* it is correct */ + + /* + * we have found our "VAT LVExtension attribute. BUT due to a + * bug in the specification it might not be word aligned so + * copy first to avoid panics on some machines (!!) + */ + DPRINTF(VOLUMES, ("Updating VAT LVExtension attr\n")); + lvextpos = implext->data + udf_rw32(implext->iu_l); + + lvext.unique_id_chk = vat_uniqueid; + lvext.num_files = lvinfo->num_files; + lvext.num_directories = lvinfo->num_directories; + memmove(lvext.logvol_id, ump->logical_vol->logvol_id, 128); + + memcpy(lvextpos, &lvext, sizeof(lvext)); + + return 0; +} + +/* --------------------------------------------------------------------- */ + +int +udf_vat_read(struct udf_node *vat_node, uint8_t *blob, int size, uint32_t offset) +{ + struct udf_mount *ump = vat_node->ump; + + if (offset + size > ump->vat_offset + ump->vat_entries * 4) + return EINVAL; + + memcpy(blob, ump->vat_table + offset, size); + return 0; +} + +int +udf_vat_write(struct udf_node *vat_node, uint8_t *blob, int size, uint32_t offset) +{ + struct udf_mount *ump = vat_node->ump; + uint32_t offset_high; + uint8_t *new_vat_table; + + /* extent VAT allocation if needed */ + offset_high = offset + size; + if (offset_high >= ump->vat_table_alloc_len) { + /* realloc */ + new_vat_table = realloc(ump->vat_table, + ump->vat_table_alloc_len + UDF_VAT_CHUNKSIZE, + M_UDFVOLD, M_WAITOK | M_CANFAIL); + if (!new_vat_table) { + printf("udf_vat_write: can't extent VAT, out of mem\n"); + return ENOMEM; + } + ump->vat_table = new_vat_table; + ump->vat_table_alloc_len += UDF_VAT_CHUNKSIZE; + } + ump->vat_table_len = MAX(ump->vat_table_len, offset_high); + + memcpy(ump->vat_table + offset, blob, size); + return 0; +} + +/* --------------------------------------------------------------------- */ + +/* TODO support previous VAT location writeout */ +static int +udf_update_vat_descriptor(struct udf_mount *ump) +{ + struct udf_node *vat_node = ump->vat_node; + struct udf_logvol_info *lvinfo = ump->logvol_info; + struct icb_tag *icbtag; + struct udf_oldvat_tail *oldvat_tl; + struct udf_vat *vat; + uint64_t unique_id; + uint32_t lb_size; + uint8_t *raw_vat; + int filetype, error; + + KASSERT(vat_node); + KASSERT(lvinfo); + lb_size = udf_rw32(ump->logical_vol->lb_size); + + /* get our new unique_id */ + unique_id = udf_advance_uniqueid(ump); + + /* get information from fe/efe */ + if (vat_node->fe) { + icbtag = &vat_node->fe->icbtag; + vat_node->fe->unique_id = udf_rw64(unique_id); + } else { + icbtag = &vat_node->efe->icbtag; + vat_node->efe->unique_id = udf_rw64(unique_id); + } + + /* Check icb filetype! it has to be 0 or UDF_ICB_FILETYPE_VAT */ + filetype = icbtag->file_type; + KASSERT((filetype == 0) || (filetype == UDF_ICB_FILETYPE_VAT)); + + /* allocate piece to process head or tail of VAT file */ + raw_vat = malloc(lb_size, M_TEMP, M_WAITOK); + + if (filetype == 0) { + /* + * Update "*UDF VAT LVExtension" extended attribute from the + * lvint if present. + */ + udf_update_vat_extattr_from_lvid(vat_node); + + /* setup identifying regid */ + oldvat_tl = (struct udf_oldvat_tail *) raw_vat; + memset(oldvat_tl, 0, sizeof(struct udf_oldvat_tail)); + + udf_set_regid(&oldvat_tl->id, "*UDF Virtual Alloc Tbl"); + udf_add_udf_regid(ump, &oldvat_tl->id); + oldvat_tl->prev_vat = udf_rw32(0xffffffff); + + /* write out new tail of virtual allocation table file */ + error = udf_vat_write(vat_node, raw_vat, + sizeof(struct udf_oldvat_tail), ump->vat_entries * 4); + } else { + /* compose the VAT2 header */ + vat = (struct udf_vat *) raw_vat; + memset(vat, 0, sizeof(struct udf_vat)); + + vat->header_len = udf_rw16(152); /* as per spec */ + vat->impl_use_len = udf_rw16(0); + memmove(vat->logvol_id, ump->logical_vol->logvol_id, 128); + vat->prev_vat = udf_rw32(0xffffffff); + vat->num_files = lvinfo->num_files; + vat->num_directories = lvinfo->num_directories; + vat->min_udf_readver = lvinfo->min_udf_readver; + vat->min_udf_writever = lvinfo->min_udf_writever; + vat->max_udf_writever = lvinfo->max_udf_writever; + + error = udf_vat_write(vat_node, raw_vat, + sizeof(struct udf_vat), 0); + } + free(raw_vat, M_TEMP); + + return error; /* success! */ +} + + +int +udf_writeout_vat(struct udf_mount *ump) +{ + struct udf_node *vat_node = ump->vat_node; + int error; + + KASSERT(vat_node); + + DPRINTF(CALL, ("udf_writeout_vat\n")); + +// mutex_enter(&ump->allocate_mutex); + udf_update_vat_descriptor(ump); + + /* write out the VAT contents ; TODO intelligent writing */ + error = vn_rdwr(UIO_WRITE, vat_node->vnode, + ump->vat_table, ump->vat_table_len, 0, + UIO_SYSSPACE, 0, FSCRED, NULL, NULL); + if (error) { + printf("udf_writeout_vat: failed to write out VAT contents\n"); + goto out; + } + +// mutex_exit(&ump->allocate_mutex); + + error = vflushbuf(ump->vat_node->vnode, FSYNC_WAIT); + if (error) + goto out; + error = VOP_FSYNC(ump->vat_node->vnode, + FSCRED, FSYNC_WAIT, 0, 0); + if (error) + printf("udf_writeout_vat: error writing VAT node!\n"); +out: + + return error; +} + +/* --------------------------------------------------------------------- */ + +/* + * Read in relevant pieces of VAT file and check if its indeed a VAT file + * descriptor. If OK, read in complete VAT file. + */ + +static int +udf_check_for_vat(struct udf_node *vat_node) +{ + struct udf_mount *ump; + struct icb_tag *icbtag; + struct timestamp *mtime; + struct udf_vat *vat; + struct udf_oldvat_tail *oldvat_tl; + struct udf_logvol_info *lvinfo; + uint64_t unique_id; + uint32_t vat_length; + uint32_t vat_offset, vat_entries, vat_table_alloc_len; + uint32_t sector_size; + uint32_t *raw_vat; + uint8_t *vat_table; + char *regid_name; + int filetype; + int error; + + /* vat_length is really 64 bits though impossible */ + + DPRINTF(VOLUMES, ("Checking for VAT\n")); + if (!vat_node) + return ENOENT; + + /* get mount info */ + ump = vat_node->ump; + sector_size = udf_rw32(ump->logical_vol->lb_size); + + /* check assertions */ + assert(vat_node->fe || vat_node->efe); + assert(ump->logvol_integrity); + + /* set vnode type to regular file or we can't read from it! */ + vat_node->vnode->v_type = VREG; + + /* get information from fe/efe */ + if (vat_node->fe) { + vat_length = udf_rw64(vat_node->fe->inf_len); + icbtag = &vat_node->fe->icbtag; + mtime = &vat_node->fe->mtime; + unique_id = udf_rw64(vat_node->fe->unique_id); + } else { + vat_length = udf_rw64(vat_node->efe->inf_len); + icbtag = &vat_node->efe->icbtag; + mtime = &vat_node->efe->mtime; + unique_id = udf_rw64(vat_node->efe->unique_id); + } + + /* Check icb filetype! it has to be 0 or UDF_ICB_FILETYPE_VAT */ + filetype = icbtag->file_type; + if ((filetype != 0) && (filetype != UDF_ICB_FILETYPE_VAT)) + return ENOENT; + + DPRINTF(VOLUMES, ("\tPossible VAT length %d\n", vat_length)); + + vat_table_alloc_len = + ((vat_length + UDF_VAT_CHUNKSIZE-1) / UDF_VAT_CHUNKSIZE) + * UDF_VAT_CHUNKSIZE; + + vat_table = malloc(vat_table_alloc_len, M_UDFVOLD, + M_CANFAIL | M_WAITOK); + if (vat_table == NULL) { + printf("allocation of %d bytes failed for VAT\n", + vat_table_alloc_len); + return ENOMEM; + } + + /* allocate piece to read in head or tail of VAT file */ + raw_vat = malloc(sector_size, M_TEMP, M_WAITOK); + + /* + * check contents of the file if its the old 1.50 VAT table format. + * Its notoriously broken and allthough some implementations support an + * extention as defined in the UDF 1.50 errata document, its doubtfull + * to be useable since a lot of implementations don't maintain it. + */ + lvinfo = ump->logvol_info; + + if (filetype == 0) { + /* definition */ + vat_offset = 0; + vat_entries = (vat_length-36)/4; + + /* read in tail of virtual allocation table file */ + error = vn_rdwr(UIO_READ, vat_node->vnode, + (uint8_t *) raw_vat, + sizeof(struct udf_oldvat_tail), + vat_entries * 4, + UIO_SYSSPACE, IO_SYNC | IO_NODELOCKED, FSCRED, + NULL, NULL); + if (error) + goto out; + + /* check 1.50 VAT */ + oldvat_tl = (struct udf_oldvat_tail *) raw_vat; + regid_name = (char *) oldvat_tl->id.id; + error = strncmp(regid_name, "*UDF Virtual Alloc Tbl", 22); + if (error) { + DPRINTF(VOLUMES, ("VAT format 1.50 rejected\n")); + error = ENOENT; + goto out; + } + + /* + * update LVID from "*UDF VAT LVExtension" extended attribute + * if present. + */ + udf_update_lvid_from_vat_extattr(vat_node); + } else { + /* read in head of virtual allocation table file */ + error = vn_rdwr(UIO_READ, vat_node->vnode, + (uint8_t *) raw_vat, + sizeof(struct udf_vat), 0, + UIO_SYSSPACE, IO_SYNC | IO_NODELOCKED, FSCRED, + NULL, NULL); + if (error) + goto out; + + /* definition */ + vat = (struct udf_vat *) raw_vat; + vat_offset = vat->header_len; + vat_entries = (vat_length - vat_offset)/4; + + assert(lvinfo); + lvinfo->num_files = vat->num_files; + lvinfo->num_directories = vat->num_directories; + lvinfo->min_udf_readver = vat->min_udf_readver; + lvinfo->min_udf_writever = vat->min_udf_writever; + lvinfo->max_udf_writever = vat->max_udf_writever; + + udf_update_logvolname(ump, vat->logvol_id); + } + + /* read in complete VAT file */ + error = vn_rdwr(UIO_READ, vat_node->vnode, + vat_table, + vat_length, 0, + UIO_SYSSPACE, IO_SYNC | IO_NODELOCKED, FSCRED, + NULL, NULL); + if (error) + printf("read in of complete VAT file failed (error %d)\n", + error); + if (error) + goto out; + + DPRINTF(VOLUMES, ("VAT format accepted, marking it closed\n")); + ump->logvol_integrity->lvint_next_unique_id = udf_rw64(unique_id); + ump->logvol_integrity->integrity_type = udf_rw32(UDF_INTEGRITY_CLOSED); + ump->logvol_integrity->time = *mtime; + + ump->vat_table_len = vat_length; + ump->vat_table_alloc_len = vat_table_alloc_len; + ump->vat_table = vat_table; + ump->vat_offset = vat_offset; + ump->vat_entries = vat_entries; + ump->vat_last_free_lb = 0; /* start at beginning */ + +out: + if (error) { + if (vat_table) + free(vat_table, M_UDFVOLD); + } + free(raw_vat, M_TEMP); + + return error; +} + +/* --------------------------------------------------------------------- */ + +static int +udf_search_vat(struct udf_mount *ump, union udf_pmap *mapping) +{ + struct udf_node *vat_node; + struct long_ad icb_loc; + uint32_t early_vat_loc, vat_loc; + int error; + + /* mapping info not needed */ + mapping = mapping; + + vat_loc = ump->last_possible_vat_location; + early_vat_loc = vat_loc - 256; /* 8 blocks of 32 sectors */ + + DPRINTF(VOLUMES, ("1) last possible %d, early_vat_loc %d \n", + vat_loc, early_vat_loc)); + early_vat_loc = MAX(early_vat_loc, ump->first_possible_vat_location); + + DPRINTF(VOLUMES, ("2) last possible %d, early_vat_loc %d \n", + vat_loc, early_vat_loc)); + + /* start looking from the end of the range */ + do { + DPRINTF(VOLUMES, ("Checking for VAT at sector %d\n", vat_loc)); + icb_loc.loc.part_num = udf_rw16(UDF_VTOP_RAWPART); + icb_loc.loc.lb_num = udf_rw32(vat_loc); + + error = udf_get_node(ump, &icb_loc, &vat_node); + if (!error) { + error = udf_check_for_vat(vat_node); + DPRINTFIF(VOLUMES, !error, + ("VAT accepted at %d\n", vat_loc)); + if (!error) + break; + } + if (vat_node) { + vput(vat_node->vnode); + vat_node = NULL; + } + vat_loc--; /* walk backwards */ + } while (vat_loc >= early_vat_loc); + + /* keep our VAT node around */ + if (vat_node) { + UDF_SET_SYSTEMFILE(vat_node->vnode); + ump->vat_node = vat_node; + } + + return error; +} + +/* --------------------------------------------------------------------- */ + +static int +udf_read_sparables(struct udf_mount *ump, union udf_pmap *mapping) +{ + union dscrptr *dscr; + struct part_map_spare *pms = &mapping->pms; + uint32_t lb_num; + int spar, error; + + /* + * The partition mapping passed on to us specifies the information we + * need to locate and initialise the sparable partition mapping + * information we need. + */ + + DPRINTF(VOLUMES, ("Read sparable table\n")); + ump->sparable_packet_size = udf_rw16(pms->packet_len); + KASSERT(ump->sparable_packet_size >= ump->packet_size); /* XXX */ + + for (spar = 0; spar < pms->n_st; spar++) { + lb_num = pms->st_loc[spar]; + DPRINTF(VOLUMES, ("Checking for sparing table %d\n", lb_num)); + error = udf_read_phys_dscr(ump, lb_num, M_UDFVOLD, &dscr); + if (!error && dscr) { + if (udf_rw16(dscr->tag.id) == TAGID_SPARING_TABLE) { + if (ump->sparing_table) + free(ump->sparing_table, M_UDFVOLD); + ump->sparing_table = &dscr->spt; + dscr = NULL; + DPRINTF(VOLUMES, + ("Sparing table accepted (%d entries)\n", + udf_rw16(ump->sparing_table->rt_l))); + break; /* we're done */ + } + } + if (dscr) + free(dscr, M_UDFVOLD); + } + + if (ump->sparing_table) + return 0; + + return ENOENT; +} + +/* --------------------------------------------------------------------- */ + +static int +udf_read_metadata_nodes(struct udf_mount *ump, union udf_pmap *mapping) +{ + struct part_map_meta *pmm = &mapping->pmm; + struct long_ad icb_loc; + struct vnode *vp; + uint16_t raw_phys_part, phys_part; + int error; + + /* + * BUGALERT: some rogue implementations use random physical + * partition numbers to break other implementations so lookup + * the number. + */ + + /* extract our allocation parameters set up on format */ + ump->metadata_alloc_unit_size = udf_rw32(mapping->pmm.alloc_unit_size); + ump->metadata_alignment_unit_size = udf_rw16(mapping->pmm.alignment_unit_size); + ump->metadata_flags = mapping->pmm.flags; + + DPRINTF(VOLUMES, ("Reading in Metadata files\n")); + raw_phys_part = udf_rw16(pmm->part_num); + phys_part = udf_find_raw_phys(ump, raw_phys_part); + + icb_loc.loc.part_num = udf_rw16(phys_part); + + DPRINTF(VOLUMES, ("Metadata file\n")); + icb_loc.loc.lb_num = pmm->meta_file_lbn; + error = udf_get_node(ump, &icb_loc, &ump->metadata_node); + if (ump->metadata_node) { + vp = ump->metadata_node->vnode; + UDF_SET_SYSTEMFILE(vp); + } + + icb_loc.loc.lb_num = pmm->meta_mirror_file_lbn; + if (icb_loc.loc.lb_num != -1) { + DPRINTF(VOLUMES, ("Metadata copy file\n")); + error = udf_get_node(ump, &icb_loc, &ump->metadatamirror_node); + if (ump->metadatamirror_node) { + vp = ump->metadatamirror_node->vnode; + UDF_SET_SYSTEMFILE(vp); + } + } + + icb_loc.loc.lb_num = pmm->meta_bitmap_file_lbn; + if (icb_loc.loc.lb_num != -1) { + DPRINTF(VOLUMES, ("Metadata bitmap file\n")); + error = udf_get_node(ump, &icb_loc, &ump->metadatabitmap_node); + if (ump->metadatabitmap_node) { + vp = ump->metadatabitmap_node->vnode; + UDF_SET_SYSTEMFILE(vp); + } + } + + /* if we're mounting read-only we relax the requirements */ + if (ump->vfs_mountp->mnt_flag & MNT_RDONLY) { + error = EFAULT; + if (ump->metadata_node) + error = 0; + if ((ump->metadata_node == NULL) && (ump->metadatamirror_node)) { + printf( "udf mount: Metadata file not readable, " + "substituting Metadata copy file\n"); + ump->metadata_node = ump->metadatamirror_node; + ump->metadatamirror_node = NULL; + error = 0; + } + } else { + /* mounting read/write */ + /* XXX DISABLED! metadata writing is not working yet XXX */ + if (error) + error = EROFS; + } + DPRINTFIF(VOLUMES, error, ("udf mount: failed to read " + "metadata files\n")); + return error; +} + +/* --------------------------------------------------------------------- */ + +int +udf_read_vds_tables(struct udf_mount *ump) +{ + union udf_pmap *mapping; + /* struct udf_args *args = &ump->mount_args; */ + uint32_t n_pm; + uint32_t log_part; + uint8_t *pmap_pos; + int pmap_size; + int error; + + /* Iterate (again) over the part mappings for locations */ + n_pm = udf_rw32(ump->logical_vol->n_pm); /* num partmaps */ + pmap_pos = ump->logical_vol->maps; + + for (log_part = 0; log_part < n_pm; log_part++) { + mapping = (union udf_pmap *) pmap_pos; + switch (ump->vtop_tp[log_part]) { + case UDF_VTOP_TYPE_PHYS : + /* nothing */ + break; + case UDF_VTOP_TYPE_VIRT : + /* search and load VAT */ + error = udf_search_vat(ump, mapping); + if (error) + return ENOENT; + break; + case UDF_VTOP_TYPE_SPARABLE : + /* load one of the sparable tables */ + error = udf_read_sparables(ump, mapping); + if (error) + return ENOENT; + break; + case UDF_VTOP_TYPE_META : + /* load the associated file descriptors */ + error = udf_read_metadata_nodes(ump, mapping); + if (error) + return ENOENT; + break; + default: + break; + } + pmap_size = pmap_pos[1]; + pmap_pos += pmap_size; + } + + /* read in and check unallocated and free space info if writing */ + if ((ump->vfs_mountp->mnt_flag & MNT_RDONLY) == 0) { + error = udf_read_physical_partition_spacetables(ump); + if (error) + return error; + + /* also read in metadata partition spacebitmap if defined */ + error = udf_read_metadata_partition_spacetable(ump); + return error; + } + + return 0; +} + +/* --------------------------------------------------------------------- */ + +int +udf_read_rootdirs(struct udf_mount *ump) +{ + union dscrptr *dscr; + /* struct udf_args *args = &ump->mount_args; */ + struct udf_node *rootdir_node, *streamdir_node; + struct long_ad fsd_loc, *dir_loc; + uint32_t lb_num, dummy; + uint32_t fsd_len; + int dscr_type; + int error; + + /* TODO implement FSD reading in separate function like integrity? */ + /* get fileset descriptor sequence */ + fsd_loc = ump->logical_vol->lv_fsd_loc; + fsd_len = udf_rw32(fsd_loc.len); + + dscr = NULL; + error = 0; + while (fsd_len || error) { + DPRINTF(VOLUMES, ("fsd_len = %d\n", fsd_len)); + /* translate fsd_loc to lb_num */ + error = udf_translate_vtop(ump, &fsd_loc, &lb_num, &dummy); + if (error) + break; + DPRINTF(VOLUMES, ("Reading FSD at lb %d\n", lb_num)); + error = udf_read_phys_dscr(ump, lb_num, M_UDFVOLD, &dscr); + /* end markers */ + if (error || (dscr == NULL)) + break; + + /* analyse */ + dscr_type = udf_rw16(dscr->tag.id); + if (dscr_type == TAGID_TERM) + break; + if (dscr_type != TAGID_FSD) { + free(dscr, M_UDFVOLD); + return ENOENT; + } + + /* + * TODO check for multiple fileset descriptors; its only + * picking the last now. Also check for FSD + * correctness/interpretability + */ + + /* update */ + if (ump->fileset_desc) { + free(ump->fileset_desc, M_UDFVOLD); + } + ump->fileset_desc = &dscr->fsd; + dscr = NULL; + + /* continue to the next fsd */ + fsd_len -= ump->discinfo.sector_size; + fsd_loc.loc.lb_num = udf_rw32(udf_rw32(fsd_loc.loc.lb_num)+1); + + /* follow up to fsd->next_ex (long_ad) if its not null */ + if (udf_rw32(ump->fileset_desc->next_ex.len)) { + DPRINTF(VOLUMES, ("follow up FSD extent\n")); + fsd_loc = ump->fileset_desc->next_ex; + fsd_len = udf_rw32(ump->fileset_desc->next_ex.len); + } + } + if (dscr) + free(dscr, M_UDFVOLD); + + /* there has to be one */ + if (ump->fileset_desc == NULL) + return ENOENT; + + DPRINTF(VOLUMES, ("FSD read in fine\n")); + DPRINTF(VOLUMES, ("Updating fsd logical volume id\n")); + udf_update_logvolname(ump, ump->logical_vol->logvol_id); + + /* + * Now the FSD is known, read in the rootdirectory and if one exists, + * the system stream dir. Some files in the system streamdir are not + * wanted in this implementation since they are not maintained. If + * writing is enabled we'll delete these files if they exist. + */ + + rootdir_node = streamdir_node = NULL; + dir_loc = NULL; + + /* try to read in the rootdir */ + dir_loc = &ump->fileset_desc->rootdir_icb; + error = udf_get_node(ump, dir_loc, &rootdir_node); + if (error) + return ENOENT; + + /* aparently it read in fine */ + + /* + * Try the system stream directory; not very likely in the ones we + * test, but for completeness. + */ + dir_loc = &ump->fileset_desc->streamdir_icb; + if (udf_rw32(dir_loc->len)) { + printf("udf_read_rootdirs: streamdir defined "); + error = udf_get_node(ump, dir_loc, &streamdir_node); + if (error) { + printf("but error in streamdir reading\n"); + } else { + printf("but ignored\n"); + /* + * TODO process streamdir `baddies' i.e. files we dont + * want if R/W + */ + } + } + + DPRINTF(VOLUMES, ("Rootdir(s) read in fine\n")); + + /* release the vnodes again; they'll be auto-recycled later */ + if (streamdir_node) { + vput(streamdir_node->vnode); + } + if (rootdir_node) { + vput(rootdir_node->vnode); + } + + return 0; +} + +/* --------------------------------------------------------------------- */ + +/* To make absolutely sure we are NOT returning zero, add one :) */ + +long +udf_get_node_id(const struct long_ad *icbptr) +{ + /* ought to be enough since each mountpoint has its own chain */ + return udf_rw32(icbptr->loc.lb_num) + 1; +} + + +int +udf_compare_icb(const struct long_ad *a, const struct long_ad *b) +{ + if (udf_rw16(a->loc.part_num) < udf_rw16(b->loc.part_num)) + return -1; + if (udf_rw16(a->loc.part_num) > udf_rw16(b->loc.part_num)) + return 1; + + if (udf_rw32(a->loc.lb_num) < udf_rw32(b->loc.lb_num)) + return -1; + if (udf_rw32(a->loc.lb_num) > udf_rw32(b->loc.lb_num)) + return 1; + + return 0; +} + + +static int +udf_compare_rbnodes(void *ctx, const void *a, const void *b) +{ + const struct udf_node *a_node = a; + const struct udf_node *b_node = b; + + return udf_compare_icb(&a_node->loc, &b_node->loc); +} + + +static int +udf_compare_rbnode_icb(void *ctx, const void *a, const void *key) +{ + const struct udf_node *a_node = a; + const struct long_ad * const icb = key; + + return udf_compare_icb(&a_node->loc, icb); +} + + +static const rb_tree_ops_t udf_node_rbtree_ops = { + .rbto_compare_nodes = udf_compare_rbnodes, + .rbto_compare_key = udf_compare_rbnode_icb, + .rbto_node_offset = offsetof(struct udf_node, rbnode), + .rbto_context = NULL +}; + + +void +udf_init_nodes_tree(struct udf_mount *ump) +{ + + rb_tree_init(&ump->udf_node_tree, &udf_node_rbtree_ops); +} + + +static struct udf_node * +udf_node_lookup(struct udf_mount *ump, struct long_ad *icbptr) +{ + struct udf_node *udf_node; + struct vnode *vp; + +loop: + mutex_enter(&ump->ihash_lock); + + udf_node = rb_tree_find_node(&ump->udf_node_tree, icbptr); + if (udf_node) { + vp = udf_node->vnode; + assert(vp); + mutex_enter(vp->v_interlock); + mutex_exit(&ump->ihash_lock); + if (vget(vp, LK_EXCLUSIVE)) + goto loop; + return udf_node; + } + mutex_exit(&ump->ihash_lock); + + return NULL; +} + + +static void +udf_register_node(struct udf_node *udf_node) +{ + struct udf_mount *ump = udf_node->ump; + + /* add node to the rb tree */ + mutex_enter(&ump->ihash_lock); + rb_tree_insert_node(&ump->udf_node_tree, udf_node); + mutex_exit(&ump->ihash_lock); +} + + +static void +udf_deregister_node(struct udf_node *udf_node) +{ + struct udf_mount *ump = udf_node->ump; + + /* remove node from the rb tree */ + mutex_enter(&ump->ihash_lock); + rb_tree_remove_node(&ump->udf_node_tree, udf_node); + mutex_exit(&ump->ihash_lock); +} + +/* --------------------------------------------------------------------- */ + +static int +udf_validate_session_start(struct udf_mount *ump) +{ + struct mmc_trackinfo trackinfo; + struct vrs_desc *vrs; + uint32_t tracknr, sessionnr, sector, sector_size; + uint32_t iso9660_vrs, write_track_start; + uint8_t *buffer, *blank, *pos; + int blks, max_sectors, vrs_len; + int error; + + /* disc appendable? */ + if (ump->discinfo.disc_state == MMC_STATE_FULL) + return EROFS; + + /* already written here? if so, there should be an ISO VDS */ + if (ump->discinfo.last_session_state == MMC_STATE_INCOMPLETE) + return 0; + + /* + * Check if the first track of the session is blank and if so, copy or + * create a dummy ISO descriptor so the disc is valid again. + */ + + tracknr = ump->discinfo.first_track_last_session; + memset(&trackinfo, 0, sizeof(struct mmc_trackinfo)); + trackinfo.tracknr = tracknr; + error = udf_update_trackinfo(ump, &trackinfo); + if (error) + return error; + + udf_dump_trackinfo(&trackinfo); + KASSERT(trackinfo.flags & (MMC_TRACKINFO_BLANK | MMC_TRACKINFO_RESERVED)); + KASSERT(trackinfo.sessionnr > 1); + + KASSERT(trackinfo.flags & MMC_TRACKINFO_NWA_VALID); + write_track_start = trackinfo.next_writable; + + /* we have to copy the ISO VRS from a former session */ + DPRINTF(VOLUMES, ("validate_session_start: " + "blank or reserved track, copying VRS\n")); + + /* sessionnr should be the session we're mounting */ + sessionnr = ump->mount_args.sessionnr; + + /* start at the first track */ + tracknr = ump->discinfo.first_track; + while (tracknr <= ump->discinfo.num_tracks) { + trackinfo.tracknr = tracknr; + error = udf_update_trackinfo(ump, &trackinfo); + if (error) { + DPRINTF(VOLUMES, ("failed to get trackinfo; aborting\n")); + return error; + } + if (trackinfo.sessionnr == sessionnr) + break; + tracknr++; + } + if (trackinfo.sessionnr != sessionnr) { + DPRINTF(VOLUMES, ("failed to get trackinfo; aborting\n")); + return ENOENT; + } + + DPRINTF(VOLUMES, ("found possible former ISO VRS at\n")); + udf_dump_trackinfo(&trackinfo); + + /* + * location of iso9660 vrs is defined as first sector AFTER 32kb, + * minimum ISO `sector size' 2048 + */ + sector_size = ump->discinfo.sector_size; + iso9660_vrs = ((32*1024 + sector_size - 1) / sector_size) + + trackinfo.track_start; + + buffer = malloc(UDF_ISO_VRS_SIZE, M_TEMP, M_WAITOK); + max_sectors = UDF_ISO_VRS_SIZE / sector_size; + blks = MAX(1, 2048 / sector_size); + + error = 0; + for (sector = 0; sector < max_sectors; sector += blks) { + pos = buffer + sector * sector_size; + error = udf_read_phys_sectors(ump, UDF_C_DSCR, pos, + iso9660_vrs + sector, blks); + if (error) + break; + /* check this ISO descriptor */ + vrs = (struct vrs_desc *) pos; + DPRINTF(VOLUMES, ("got VRS id `%4s`\n", vrs->identifier)); + if (strncmp(vrs->identifier, VRS_CD001, 5) == 0) + continue; + if (strncmp(vrs->identifier, VRS_CDW02, 5) == 0) + continue; + if (strncmp(vrs->identifier, VRS_BEA01, 5) == 0) + continue; + if (strncmp(vrs->identifier, VRS_NSR02, 5) == 0) + continue; + if (strncmp(vrs->identifier, VRS_NSR03, 5) == 0) + continue; + if (strncmp(vrs->identifier, VRS_TEA01, 5) == 0) + break; + /* now what? for now, end of sequence */ + break; + } + vrs_len = sector + blks; + if (error) { + DPRINTF(VOLUMES, ("error reading old ISO VRS\n")); + DPRINTF(VOLUMES, ("creating minimal ISO VRS\n")); + + memset(buffer, 0, UDF_ISO_VRS_SIZE); + + vrs = (struct vrs_desc *) (buffer); + vrs->struct_type = 0; + vrs->version = 1; + memcpy(vrs->identifier,VRS_BEA01, 5); + + vrs = (struct vrs_desc *) (buffer + 2048); + vrs->struct_type = 0; + vrs->version = 1; + if (udf_rw16(ump->logical_vol->tag.descriptor_ver) == 2) { + memcpy(vrs->identifier,VRS_NSR02, 5); + } else { + memcpy(vrs->identifier,VRS_NSR03, 5); + } + + vrs = (struct vrs_desc *) (buffer + 4096); + vrs->struct_type = 0; + vrs->version = 1; + memcpy(vrs->identifier, VRS_TEA01, 5); + + vrs_len = 3*blks; + } + + DPRINTF(VOLUMES, ("Got VRS of %d sectors long\n", vrs_len)); + + /* + * location of iso9660 vrs is defined as first sector AFTER 32kb, + * minimum ISO `sector size' 2048 + */ + sector_size = ump->discinfo.sector_size; + iso9660_vrs = ((32*1024 + sector_size - 1) / sector_size) + + write_track_start; + + /* write out 32 kb */ + blank = malloc(sector_size, M_TEMP, M_WAITOK); + memset(blank, 0, sector_size); + error = 0; + for (sector = write_track_start; sector < iso9660_vrs; sector ++) { + error = udf_write_phys_sectors(ump, UDF_C_ABSOLUTE, + blank, sector, 1); + if (error) + break; + } + if (!error) { + /* write out our ISO VRS */ + KASSERT(sector == iso9660_vrs); + error = udf_write_phys_sectors(ump, UDF_C_ABSOLUTE, buffer, + sector, vrs_len); + sector += vrs_len; + } + if (!error) { + /* fill upto the first anchor at S+256 */ + for (; sector < write_track_start+256; sector++) { + error = udf_write_phys_sectors(ump, UDF_C_ABSOLUTE, + blank, sector, 1); + if (error) + break; + } + } + if (!error) { + /* write out anchor; write at ABSOLUTE place! */ + error = udf_write_phys_dscr_sync(ump, NULL, UDF_C_ABSOLUTE, + (union dscrptr *) ump->anchors[0], sector, sector); + if (error) + printf("writeout of anchor failed!\n"); + } + + free(blank, M_TEMP); + free(buffer, M_TEMP); + + if (error) + printf("udf_open_session: error writing iso vrs! : " + "leaving disc in compromised state!\n"); + + /* synchronise device caches */ + (void) udf_synchronise_caches(ump); + + return error; +} + + +int +udf_open_logvol(struct udf_mount *ump) +{ + int logvol_integrity; + int error; + + /* already/still open? */ + logvol_integrity = udf_rw32(ump->logvol_integrity->integrity_type); + if (logvol_integrity == UDF_INTEGRITY_OPEN) + return 0; + + /* can we open it ? */ + if (ump->vfs_mountp->mnt_flag & MNT_RDONLY) + return EROFS; + + /* setup write parameters */ + DPRINTF(VOLUMES, ("Setting up write parameters\n")); + if ((error = udf_setup_writeparams(ump)) != 0) + return error; + + /* determine data and metadata tracks (most likely same) */ + error = udf_search_writing_tracks(ump); + if (error) { + /* most likely lack of space */ + printf("udf_open_logvol: error searching writing tracks\n"); + return EROFS; + } + + /* writeout/update lvint on disc or only in memory */ + DPRINTF(VOLUMES, ("Opening logical volume\n")); + if (ump->lvopen & UDF_OPEN_SESSION) { + /* TODO optional track reservation opening */ + error = udf_validate_session_start(ump); + if (error) + return error; + + /* determine data and metadata tracks again */ + error = udf_search_writing_tracks(ump); + } + + /* mark it open */ + ump->logvol_integrity->integrity_type = udf_rw32(UDF_INTEGRITY_OPEN); + + /* do we need to write it out? */ + if (ump->lvopen & UDF_WRITE_LVINT) { + error = udf_writeout_lvint(ump, ump->lvopen); + /* if we couldn't write it mark it closed again */ + if (error) { + ump->logvol_integrity->integrity_type = + udf_rw32(UDF_INTEGRITY_CLOSED); + return error; + } + } + + return 0; +} + + +int +udf_close_logvol(struct udf_mount *ump, int mntflags) +{ + struct vnode *devvp = ump->devvp; + struct mmc_op mmc_op; + int logvol_integrity; + int error = 0, error1 = 0, error2 = 0; + int tracknr; + int nvats, n, nok; + + /* already/still closed? */ + logvol_integrity = udf_rw32(ump->logvol_integrity->integrity_type); + if (logvol_integrity == UDF_INTEGRITY_CLOSED) + return 0; + + /* writeout/update lvint or write out VAT */ + DPRINTF(VOLUMES, ("udf_close_logvol: closing logical volume\n")); +#ifdef DIAGNOSTIC + if (ump->lvclose & UDF_CLOSE_SESSION) + KASSERT(ump->lvclose & UDF_WRITE_VAT); +#endif + + if (ump->lvclose & UDF_WRITE_VAT) { + DPRINTF(VOLUMES, ("lvclose & UDF_WRITE_VAT\n")); + + /* write out the VAT data and all its descriptors */ + DPRINTF(VOLUMES, ("writeout vat_node\n")); + udf_writeout_vat(ump); + (void) vflushbuf(ump->vat_node->vnode, FSYNC_WAIT); + + (void) VOP_FSYNC(ump->vat_node->vnode, + FSCRED, FSYNC_WAIT, 0, 0); + + if (ump->lvclose & UDF_CLOSE_SESSION) { + DPRINTF(VOLUMES, ("udf_close_logvol: closing session " + "as requested\n")); + } + + /* at least two DVD packets and 3 CD-R packets */ + nvats = 32; + +#if notyet + /* + * TODO calculate the available space and if the disc is + * allmost full, write out till end-256-1 with banks, write + * AVDP and fill up with VATs, then close session and close + * disc. + */ + if (ump->lvclose & UDF_FINALISE_DISC) { + error = udf_write_phys_dscr_sync(ump, NULL, + UDF_C_FLOAT_DSCR, + (union dscrptr *) ump->anchors[0], + 0, 0); + if (error) + printf("writeout of anchor failed!\n"); + + /* pad space with VAT ICBs */ + nvats = 256; + } +#endif + + /* write out a number of VAT nodes */ + nok = 0; + for (n = 0; n < nvats; n++) { + /* will now only write last FE/EFE */ + ump->vat_node->i_flags |= IN_MODIFIED; + error = VOP_FSYNC(ump->vat_node->vnode, + FSCRED, FSYNC_WAIT, 0, 0); + if (!error) + nok++; + } + if (nok < 14) { + /* arbitrary; but at least one or two CD frames */ + printf("writeout of at least 14 VATs failed\n"); + return error; + } + } + + /* NOTE the disc is in a (minimal) valid state now; no erroring out */ + + /* finish closing of session */ + if (ump->lvclose & UDF_CLOSE_SESSION) { + error = udf_validate_session_start(ump); + if (error) + return error; + + (void) udf_synchronise_caches(ump); + + /* close all associated tracks */ + tracknr = ump->discinfo.first_track_last_session; + error = 0; + while (tracknr <= ump->discinfo.last_track_last_session) { + DPRINTF(VOLUMES, ("\tclosing possible open " + "track %d\n", tracknr)); + memset(&mmc_op, 0, sizeof(mmc_op)); + mmc_op.operation = MMC_OP_CLOSETRACK; + mmc_op.mmc_profile = ump->discinfo.mmc_profile; + mmc_op.tracknr = tracknr; + error = VOP_IOCTL(devvp, MMCOP, &mmc_op, + FKIOCTL, NOCRED); + if (error) + printf("udf_close_logvol: closing of " + "track %d failed\n", tracknr); + tracknr ++; + } + if (!error) { + DPRINTF(VOLUMES, ("closing session\n")); + memset(&mmc_op, 0, sizeof(mmc_op)); + mmc_op.operation = MMC_OP_CLOSESESSION; + mmc_op.mmc_profile = ump->discinfo.mmc_profile; + mmc_op.sessionnr = ump->discinfo.num_sessions; + error = VOP_IOCTL(devvp, MMCOP, &mmc_op, + FKIOCTL, NOCRED); + if (error) + printf("udf_close_logvol: closing of session" + "failed\n"); + } + if (!error) + ump->lvopen |= UDF_OPEN_SESSION; + if (error) { + printf("udf_close_logvol: leaving disc as it is\n"); + ump->lvclose &= ~UDF_FINALISE_DISC; + } + } + + if (ump->lvclose & UDF_FINALISE_DISC) { + memset(&mmc_op, 0, sizeof(mmc_op)); + mmc_op.operation = MMC_OP_FINALISEDISC; + mmc_op.mmc_profile = ump->discinfo.mmc_profile; + mmc_op.sessionnr = ump->discinfo.num_sessions; + error = VOP_IOCTL(devvp, MMCOP, &mmc_op, + FKIOCTL, NOCRED); + if (error) + printf("udf_close_logvol: finalising disc" + "failed\n"); + } + + /* write out partition bitmaps if requested */ + if (ump->lvclose & UDF_WRITE_PART_BITMAPS) { + /* sync writeout metadata spacetable if existing */ + error1 = udf_write_metadata_partition_spacetable(ump, true); + if (error1) + printf( "udf_close_logvol: writeout of metadata space " + "bitmap failed\n"); + + /* sync writeout partition spacetables */ + error2 = udf_write_physical_partition_spacetables(ump, true); + if (error2) + printf( "udf_close_logvol: writeout of space tables " + "failed\n"); + + if (error1 || error2) + return (error1 | error2); + + ump->lvclose &= ~UDF_WRITE_PART_BITMAPS; + } + + /* write out metadata partition nodes if requested */ + if (ump->lvclose & UDF_WRITE_METAPART_NODES) { + /* sync writeout metadata descriptor node */ + error1 = udf_writeout_node(ump->metadata_node, FSYNC_WAIT); + if (error1) + printf( "udf_close_logvol: writeout of metadata partition " + "node failed\n"); + + /* duplicate metadata partition descriptor if needed */ + udf_synchronise_metadatamirror_node(ump); + + /* sync writeout metadatamirror descriptor node */ + error2 = udf_writeout_node(ump->metadatamirror_node, FSYNC_WAIT); + if (error2) + printf( "udf_close_logvol: writeout of metadata partition " + "mirror node failed\n"); + + if (error1 || error2) + return (error1 | error2); + + ump->lvclose &= ~UDF_WRITE_METAPART_NODES; + } + + /* mark it closed */ + ump->logvol_integrity->integrity_type = udf_rw32(UDF_INTEGRITY_CLOSED); + + /* do we need to write out the logical volume integrity? */ + if (ump->lvclose & UDF_WRITE_LVINT) + error = udf_writeout_lvint(ump, ump->lvopen); + if (error) { + /* HELP now what? mark it open again for now */ + ump->logvol_integrity->integrity_type = + udf_rw32(UDF_INTEGRITY_OPEN); + return error; + } + + (void) udf_synchronise_caches(ump); + + return 0; +} + +/* --------------------------------------------------------------------- */ + +/* + * Genfs interfacing + * + * static const struct genfs_ops udf_genfsops = { + * .gop_size = genfs_size, + * size of transfers + * .gop_alloc = udf_gop_alloc, + * allocate len bytes at offset + * .gop_write = genfs_gop_write, + * putpages interface code + * .gop_markupdate = udf_gop_markupdate, + * set update/modify flags etc. + * } + */ + +/* + * Genfs interface. These four functions are the only ones defined though not + * documented... great.... + */ + +/* + * Called for allocating an extent of the file either by VOP_WRITE() or by + * genfs filling up gaps. + */ +static int +udf_gop_alloc(struct vnode *vp, off_t off, + off_t len, int flags, kauth_cred_t cred) +{ + struct udf_node *udf_node = VTOI(vp); + struct udf_mount *ump = udf_node->ump; + uint64_t lb_start, lb_end; + uint32_t lb_size, num_lb; + int udf_c_type, vpart_num, can_fail; + int error; + + DPRINTF(ALLOC, ("udf_gop_alloc called for offset %"PRIu64" for %"PRIu64" bytes, %s\n", + off, len, flags? "SYNC":"NONE")); + + /* + * request the pages of our vnode and see how many pages will need to + * be allocated and reserve that space + */ + lb_size = udf_rw32(udf_node->ump->logical_vol->lb_size); + lb_start = off / lb_size; + lb_end = (off + len + lb_size -1) / lb_size; + num_lb = lb_end - lb_start; + + udf_c_type = udf_get_c_type(udf_node); + vpart_num = udf_get_record_vpart(ump, udf_c_type); + + /* all requests can fail */ + can_fail = true; + + /* fid's (directories) can't fail */ + if (udf_c_type == UDF_C_FIDS) + can_fail = false; + + /* system files can't fail */ + if (vp->v_vflag & VV_SYSTEM) + can_fail = false; + + error = udf_reserve_space(ump, udf_node, udf_c_type, + vpart_num, num_lb, can_fail); + + DPRINTF(ALLOC, ("\tlb_start %"PRIu64", lb_end %"PRIu64", num_lb %d\n", + lb_start, lb_end, num_lb)); + + return error; +} + + +/* + * callback from genfs to update our flags + */ +static void +udf_gop_markupdate(struct vnode *vp, int flags) +{ + struct udf_node *udf_node = VTOI(vp); + u_long mask = 0; + + if ((flags & GOP_UPDATE_ACCESSED) != 0) { + mask = IN_ACCESS; + } + if ((flags & GOP_UPDATE_MODIFIED) != 0) { + if (vp->v_type == VREG) { + mask |= IN_CHANGE | IN_UPDATE; + } else { + mask |= IN_MODIFY; + } + } + if (mask) { + udf_node->i_flags |= mask; + } +} + + +static const struct genfs_ops udf_genfsops = { + .gop_size = genfs_size, + .gop_alloc = udf_gop_alloc, + .gop_write = genfs_gop_write_rwmap, + .gop_markupdate = udf_gop_markupdate, +}; + + +/* --------------------------------------------------------------------- */ + +int +udf_write_terminator(struct udf_mount *ump, uint32_t sector) +{ + union dscrptr *dscr; + int error; + + dscr = malloc(ump->discinfo.sector_size, M_TEMP, M_WAITOK|M_ZERO); + udf_inittag(ump, &dscr->tag, TAGID_TERM, sector); + + /* CRC length for an anchor is 512 - tag length; defined in Ecma 167 */ + dscr->tag.desc_crc_len = udf_rw16(512-UDF_DESC_TAG_LENGTH); + (void) udf_validate_tag_and_crc_sums(dscr); + + error = udf_write_phys_dscr_sync(ump, NULL, UDF_C_DSCR, + dscr, sector, sector); + + free(dscr, M_TEMP); + + return error; +} + + +/* --------------------------------------------------------------------- */ + +/* UDF<->unix converters */ + +/* --------------------------------------------------------------------- */ + +static mode_t +udf_perm_to_unix_mode(uint32_t perm) +{ + mode_t mode; + + mode = ((perm & UDF_FENTRY_PERM_USER_MASK) ); + mode |= ((perm & UDF_FENTRY_PERM_GRP_MASK ) >> 2); + mode |= ((perm & UDF_FENTRY_PERM_OWNER_MASK) >> 4); + + return mode; +} + +/* --------------------------------------------------------------------- */ + +static uint32_t +unix_mode_to_udf_perm(mode_t mode) +{ + uint32_t perm; + + perm = ((mode & S_IRWXO) ); + perm |= ((mode & S_IRWXG) << 2); + perm |= ((mode & S_IRWXU) << 4); + perm |= ((mode & S_IWOTH) << 3); + perm |= ((mode & S_IWGRP) << 5); + perm |= ((mode & S_IWUSR) << 7); + + return perm; +} + +/* --------------------------------------------------------------------- */ + +static uint32_t +udf_icb_to_unix_filetype(uint32_t icbftype) +{ + switch (icbftype) { + case UDF_ICB_FILETYPE_DIRECTORY : + case UDF_ICB_FILETYPE_STREAMDIR : + return S_IFDIR; + case UDF_ICB_FILETYPE_FIFO : + return S_IFIFO; + case UDF_ICB_FILETYPE_CHARDEVICE : + return S_IFCHR; + case UDF_ICB_FILETYPE_BLOCKDEVICE : + return S_IFBLK; + case UDF_ICB_FILETYPE_RANDOMACCESS : + case UDF_ICB_FILETYPE_REALTIME : + return S_IFREG; + case UDF_ICB_FILETYPE_SYMLINK : + return S_IFLNK; + case UDF_ICB_FILETYPE_SOCKET : + return S_IFSOCK; + } + /* no idea what this is */ + return 0; +} + +/* --------------------------------------------------------------------- */ + +void +udf_to_unix_name(char *result, int result_len, char *id, int len, + struct charspec *chsp) +{ + uint16_t *raw_name, *unix_name; + uint16_t *inchp, ch; + uint8_t *outchp; + const char *osta_id = "OSTA Compressed Unicode"; + int ucode_chars, nice_uchars, is_osta_typ0, nout; + + raw_name = malloc(2048 * sizeof(uint16_t), M_UDFTEMP, M_WAITOK); + unix_name = raw_name + 1024; /* split space in half */ + assert(sizeof(char) == sizeof(uint8_t)); + outchp = (uint8_t *) result; + + is_osta_typ0 = (chsp->type == 0); + is_osta_typ0 &= (strcmp((char *) chsp->inf, osta_id) == 0); + if (is_osta_typ0) { + /* TODO clean up */ + *raw_name = *unix_name = 0; + ucode_chars = udf_UncompressUnicode(len, (uint8_t *) id, raw_name); + ucode_chars = MIN(ucode_chars, UnicodeLength((unicode_t *) raw_name)); + nice_uchars = UDFTransName(unix_name, raw_name, ucode_chars); + /* output UTF8 */ + for (inchp = unix_name; nice_uchars>0; inchp++, nice_uchars--) { + ch = *inchp; + nout = wput_utf8(outchp, result_len, ch); + outchp += nout; result_len -= nout; + if (!ch) break; + } + *outchp++ = 0; + } else { + /* assume 8bit char length byte latin-1 */ + assert(*id == 8); + assert(strlen((char *) (id+1)) <= NAME_MAX); + strncpy((char *) result, (char *) (id+1), strlen((char *) (id+1))); + } + free(raw_name, M_UDFTEMP); +} + +/* --------------------------------------------------------------------- */ + +void +unix_to_udf_name(char *result, uint8_t *result_len, char const *name, int name_len, + struct charspec *chsp) +{ + uint16_t *raw_name; + uint16_t *outchp; + const char *inchp; + const char *osta_id = "OSTA Compressed Unicode"; + int udf_chars, is_osta_typ0, bits; + size_t cnt; + + /* allocate temporary unicode-16 buffer */ + raw_name = malloc(1024, M_UDFTEMP, M_WAITOK); + + /* convert utf8 to unicode-16 */ + *raw_name = 0; + inchp = name; + outchp = raw_name; + bits = 8; + for (cnt = name_len, udf_chars = 0; cnt;) { + *outchp = wget_utf8(&inchp, &cnt); + if (*outchp > 0xff) + bits=16; + outchp++; + udf_chars++; + } + /* null terminate just in case */ + *outchp++ = 0; + + is_osta_typ0 = (chsp->type == 0); + is_osta_typ0 &= (strcmp((char *) chsp->inf, osta_id) == 0); + if (is_osta_typ0) { + udf_chars = udf_CompressUnicode(udf_chars, bits, + (unicode_t *) raw_name, + (byte *) result); + } else { + printf("unix to udf name: no CHSP0 ?\n"); + /* XXX assume 8bit char length byte latin-1 */ + *result++ = 8; udf_chars = 1; + strncpy(result, name + 1, name_len); + udf_chars += name_len; + } + *result_len = udf_chars; + free(raw_name, M_UDFTEMP); +} + +/* --------------------------------------------------------------------- */ + +void +udf_timestamp_to_timespec(struct udf_mount *ump, + struct timestamp *timestamp, + struct timespec *timespec) +{ + struct clock_ymdhms ymdhms; + uint32_t usecs, secs, nsecs; + uint16_t tz; + + /* fill in ymdhms structure from timestamp */ + memset(&ymdhms, 0, sizeof(ymdhms)); + ymdhms.dt_year = udf_rw16(timestamp->year); + ymdhms.dt_mon = timestamp->month; + ymdhms.dt_day = timestamp->day; + ymdhms.dt_wday = 0; /* ? */ + ymdhms.dt_hour = timestamp->hour; + ymdhms.dt_min = timestamp->minute; + ymdhms.dt_sec = timestamp->second; + + secs = clock_ymdhms_to_secs(&ymdhms); + usecs = timestamp->usec + + 100*timestamp->hund_usec + 10000*timestamp->centisec; + nsecs = usecs * 1000; + + /* + * Calculate the time zone. The timezone is 12 bit signed 2's + * compliment, so we gotta do some extra magic to handle it right. + */ + tz = udf_rw16(timestamp->type_tz); + tz &= 0x0fff; /* only lower 12 bits are significant */ + if (tz & 0x0800) /* sign extention */ + tz |= 0xf000; + + /* TODO check timezone conversion */ + /* check if we are specified a timezone to convert */ + if (udf_rw16(timestamp->type_tz) & 0x1000) { + if ((int16_t) tz != -2047) + secs -= (int16_t) tz * 60; + } else { + secs -= ump->mount_args.gmtoff; + } + + timespec->tv_sec = secs; + timespec->tv_nsec = nsecs; +} + + +void +udf_timespec_to_timestamp(struct timespec *timespec, struct timestamp *timestamp) +{ + struct clock_ymdhms ymdhms; + uint32_t husec, usec, csec; + + (void) clock_secs_to_ymdhms(timespec->tv_sec, &ymdhms); + + usec = timespec->tv_nsec / 1000; + husec = usec / 100; + usec -= husec * 100; /* only 0-99 in usec */ + csec = husec / 100; /* only 0-99 in csec */ + husec -= csec * 100; /* only 0-99 in husec */ + + /* set method 1 for CUT/GMT */ + timestamp->type_tz = udf_rw16((1<<12) + 0); + timestamp->year = udf_rw16(ymdhms.dt_year); + timestamp->month = ymdhms.dt_mon; + timestamp->day = ymdhms.dt_day; + timestamp->hour = ymdhms.dt_hour; + timestamp->minute = ymdhms.dt_min; + timestamp->second = ymdhms.dt_sec; + timestamp->centisec = csec; + timestamp->hund_usec = husec; + timestamp->usec = usec; +} + +/* --------------------------------------------------------------------- */ + +/* + * Attribute and filetypes converters with get/set pairs + */ + +uint32_t +udf_getaccessmode(struct udf_node *udf_node) +{ + struct file_entry *fe = udf_node->fe; + struct extfile_entry *efe = udf_node->efe; + uint32_t udf_perm, icbftype; + uint32_t mode, ftype; + uint16_t icbflags; + + UDF_LOCK_NODE(udf_node, 0); + if (fe) { + udf_perm = udf_rw32(fe->perm); + icbftype = fe->icbtag.file_type; + icbflags = udf_rw16(fe->icbtag.flags); + } else { + assert(udf_node->efe); + udf_perm = udf_rw32(efe->perm); + icbftype = efe->icbtag.file_type; + icbflags = udf_rw16(efe->icbtag.flags); + } + + mode = udf_perm_to_unix_mode(udf_perm); + ftype = udf_icb_to_unix_filetype(icbftype); + + /* set suid, sgid, sticky from flags in fe/efe */ + if (icbflags & UDF_ICB_TAG_FLAGS_SETUID) + mode |= S_ISUID; + if (icbflags & UDF_ICB_TAG_FLAGS_SETGID) + mode |= S_ISGID; + if (icbflags & UDF_ICB_TAG_FLAGS_STICKY) + mode |= S_ISVTX; + + UDF_UNLOCK_NODE(udf_node, 0); + + return mode | ftype; +} + + +void +udf_setaccessmode(struct udf_node *udf_node, mode_t mode) +{ + struct file_entry *fe = udf_node->fe; + struct extfile_entry *efe = udf_node->efe; + uint32_t udf_perm; + uint16_t icbflags; + + UDF_LOCK_NODE(udf_node, 0); + udf_perm = unix_mode_to_udf_perm(mode & ALLPERMS); + if (fe) { + icbflags = udf_rw16(fe->icbtag.flags); + } else { + icbflags = udf_rw16(efe->icbtag.flags); + } + + icbflags &= ~UDF_ICB_TAG_FLAGS_SETUID; + icbflags &= ~UDF_ICB_TAG_FLAGS_SETGID; + icbflags &= ~UDF_ICB_TAG_FLAGS_STICKY; + if (mode & S_ISUID) + icbflags |= UDF_ICB_TAG_FLAGS_SETUID; + if (mode & S_ISGID) + icbflags |= UDF_ICB_TAG_FLAGS_SETGID; + if (mode & S_ISVTX) + icbflags |= UDF_ICB_TAG_FLAGS_STICKY; + + if (fe) { + fe->perm = udf_rw32(udf_perm); + fe->icbtag.flags = udf_rw16(icbflags); + } else { + efe->perm = udf_rw32(udf_perm); + efe->icbtag.flags = udf_rw16(icbflags); + } + + UDF_UNLOCK_NODE(udf_node, 0); +} + + +void +udf_getownership(struct udf_node *udf_node, uid_t *uidp, gid_t *gidp) +{ + struct udf_mount *ump = udf_node->ump; + struct file_entry *fe = udf_node->fe; + struct extfile_entry *efe = udf_node->efe; + uid_t uid; + gid_t gid; + + UDF_LOCK_NODE(udf_node, 0); + if (fe) { + uid = (uid_t)udf_rw32(fe->uid); + gid = (gid_t)udf_rw32(fe->gid); + } else { + assert(udf_node->efe); + uid = (uid_t)udf_rw32(efe->uid); + gid = (gid_t)udf_rw32(efe->gid); + } + + /* do the uid/gid translation game */ + if (uid == (uid_t) -1) + uid = ump->mount_args.anon_uid; + if (gid == (gid_t) -1) + gid = ump->mount_args.anon_gid; + + *uidp = uid; + *gidp = gid; + + UDF_UNLOCK_NODE(udf_node, 0); +} + + +void +udf_setownership(struct udf_node *udf_node, uid_t uid, gid_t gid) +{ + struct udf_mount *ump = udf_node->ump; + struct file_entry *fe = udf_node->fe; + struct extfile_entry *efe = udf_node->efe; + uid_t nobody_uid; + gid_t nobody_gid; + + UDF_LOCK_NODE(udf_node, 0); + + /* do the uid/gid translation game */ + nobody_uid = ump->mount_args.nobody_uid; + nobody_gid = ump->mount_args.nobody_gid; + if (uid == nobody_uid) + uid = (uid_t) -1; + if (gid == nobody_gid) + gid = (gid_t) -1; + + if (fe) { + fe->uid = udf_rw32((uint32_t) uid); + fe->gid = udf_rw32((uint32_t) gid); + } else { + efe->uid = udf_rw32((uint32_t) uid); + efe->gid = udf_rw32((uint32_t) gid); + } + + UDF_UNLOCK_NODE(udf_node, 0); +} + + +/* --------------------------------------------------------------------- */ + + +int +udf_dirhash_fill(struct udf_node *dir_node) +{ + struct vnode *dvp = dir_node->vnode; + struct dirhash *dirh; + struct file_entry *fe = dir_node->fe; + struct extfile_entry *efe = dir_node->efe; + struct fileid_desc *fid; + struct dirent *dirent; + uint64_t file_size, pre_diroffset, diroffset; + uint32_t lb_size; + int error; + + /* make sure we have a dirhash to work on */ + dirh = dir_node->dir_hash; + KASSERT(dirh); + KASSERT(dirh->refcnt > 0); + + if (dirh->flags & DIRH_BROKEN) + return EIO; + if (dirh->flags & DIRH_COMPLETE) + return 0; + + /* make sure we have a clean dirhash to add to */ + dirhash_purge_entries(dirh); + + /* get directory filesize */ + if (fe) { + file_size = udf_rw64(fe->inf_len); + } else { + assert(efe); + file_size = udf_rw64(efe->inf_len); + } + + /* allocate temporary space for fid */ + lb_size = udf_rw32(dir_node->ump->logical_vol->lb_size); + fid = malloc(lb_size, M_UDFTEMP, M_WAITOK); + + /* allocate temporary space for dirent */ + dirent = malloc(sizeof(struct dirent), M_UDFTEMP, M_WAITOK); + + error = 0; + diroffset = 0; + while (diroffset < file_size) { + /* transfer a new fid/dirent */ + pre_diroffset = diroffset; + error = udf_read_fid_stream(dvp, &diroffset, fid, dirent); + if (error) { + /* TODO what to do? continue but not add? */ + dirh->flags |= DIRH_BROKEN; + dirhash_purge_entries(dirh); + break; + } + + if ((fid->file_char & UDF_FILE_CHAR_DEL)) { + /* register deleted extent for reuse */ + dirhash_enter_freed(dirh, pre_diroffset, + udf_fidsize(fid)); + } else { + /* append to the dirhash */ + dirhash_enter(dirh, dirent, pre_diroffset, + udf_fidsize(fid), 0); + } + } + dirh->flags |= DIRH_COMPLETE; + + free(fid, M_UDFTEMP); + free(dirent, M_UDFTEMP); + + return error; +} + + +/* --------------------------------------------------------------------- */ + +/* + * Directory read and manipulation functions. + * + */ + +int +udf_lookup_name_in_dir(struct vnode *vp, const char *name, int namelen, + struct long_ad *icb_loc, int *found) +{ + struct udf_node *dir_node = VTOI(vp); + struct dirhash *dirh; + struct dirhash_entry *dirh_ep; + struct fileid_desc *fid; + struct dirent *dirent; + uint64_t diroffset; + uint32_t lb_size; + int hit, error; + + /* set default return */ + *found = 0; + + /* get our dirhash and make sure its read in */ + dirhash_get(&dir_node->dir_hash); + error = udf_dirhash_fill(dir_node); + if (error) { + dirhash_put(dir_node->dir_hash); + return error; + } + dirh = dir_node->dir_hash; + + /* allocate temporary space for fid */ + lb_size = udf_rw32(dir_node->ump->logical_vol->lb_size); + fid = malloc(lb_size, M_UDFTEMP, M_WAITOK); + dirent = malloc(sizeof(struct dirent), M_UDFTEMP, M_WAITOK); + + DPRINTF(DIRHASH, ("dirhash_lookup looking for `%*.*s`\n", + namelen, namelen, name)); + + /* search our dirhash hits */ + memset(icb_loc, 0, sizeof(*icb_loc)); + dirh_ep = NULL; + for (;;) { + hit = dirhash_lookup(dirh, name, namelen, &dirh_ep); + /* if no hit, abort the search */ + if (!hit) + break; + + /* check this hit */ + diroffset = dirh_ep->offset; + + /* transfer a new fid/dirent */ + error = udf_read_fid_stream(vp, &diroffset, fid, dirent); + if (error) + break; + + DPRINTF(DIRHASH, ("dirhash_lookup\tchecking `%*.*s`\n", + dirent->d_namlen, dirent->d_namlen, dirent->d_name)); + + /* see if its our entry */ +#ifdef DIAGNOSTIC + if (dirent->d_namlen != namelen) { + printf("WARNING: dirhash_lookup() returned wrong " + "d_namelen: %d and ought to be %d\n", + dirent->d_namlen, namelen); + printf("\tlooked for `%s' and got `%s'\n", + name, dirent->d_name); + } +#endif + if (strncmp(dirent->d_name, name, namelen) == 0) { + *found = 1; + *icb_loc = fid->icb; + break; + } + } + free(fid, M_UDFTEMP); + free(dirent, M_UDFTEMP); + + dirhash_put(dir_node->dir_hash); + + return error; +} + +/* --------------------------------------------------------------------- */ + +static int +udf_create_new_fe(struct udf_mount *ump, struct file_entry *fe, int file_type, + struct long_ad *node_icb, struct long_ad *parent_icb, + uint64_t parent_unique_id) +{ + struct timespec now; + struct icb_tag *icb; + struct filetimes_extattr_entry *ft_extattr; + uint64_t unique_id; + uint32_t fidsize, lb_num; + uint8_t *bpos; + int crclen, attrlen; + + lb_num = udf_rw32(node_icb->loc.lb_num); + udf_inittag(ump, &fe->tag, TAGID_FENTRY, lb_num); + icb = &fe->icbtag; + + /* + * Always use strategy type 4 unless on WORM wich we don't support + * (yet). Fill in defaults and set for internal allocation of data. + */ + icb->strat_type = udf_rw16(4); + icb->max_num_entries = udf_rw16(1); + icb->file_type = file_type; /* 8 bit */ + icb->flags = udf_rw16(UDF_ICB_INTERN_ALLOC); + + fe->perm = udf_rw32(0x7fff); /* all is allowed */ + fe->link_cnt = udf_rw16(0); /* explicit setting */ + + fe->ckpoint = udf_rw32(1); /* user supplied file version */ + + vfs_timestamp(&now); + udf_timespec_to_timestamp(&now, &fe->atime); + udf_timespec_to_timestamp(&now, &fe->attrtime); + udf_timespec_to_timestamp(&now, &fe->mtime); + + udf_set_regid(&fe->imp_id, IMPL_NAME); + udf_add_impl_regid(ump, &fe->imp_id); + + unique_id = udf_advance_uniqueid(ump); + fe->unique_id = udf_rw64(unique_id); + fe->l_ea = udf_rw32(0); + + /* create extended attribute to record our creation time */ + attrlen = UDF_FILETIMES_ATTR_SIZE(1); + ft_extattr = malloc(attrlen, M_UDFTEMP, M_WAITOK); + memset(ft_extattr, 0, attrlen); + ft_extattr->hdr.type = udf_rw32(UDF_FILETIMES_ATTR_NO); + ft_extattr->hdr.subtype = 1; /* [4/48.10.5] */ + ft_extattr->hdr.a_l = udf_rw32(UDF_FILETIMES_ATTR_SIZE(1)); + ft_extattr->d_l = udf_rw32(UDF_TIMESTAMP_SIZE); /* one item */ + ft_extattr->existence = UDF_FILETIMES_FILE_CREATION; + udf_timespec_to_timestamp(&now, &ft_extattr->times[0]); + + udf_extattr_insert_internal(ump, (union dscrptr *) fe, + (struct extattr_entry *) ft_extattr); + free(ft_extattr, M_UDFTEMP); + + /* if its a directory, create '..' */ + bpos = (uint8_t *) fe->data + udf_rw32(fe->l_ea); + fidsize = 0; + if (file_type == UDF_ICB_FILETYPE_DIRECTORY) { + fidsize = udf_create_parentfid(ump, + (struct fileid_desc *) bpos, parent_icb, + parent_unique_id); + } + + /* record fidlength information */ + fe->inf_len = udf_rw64(fidsize); + fe->l_ad = udf_rw32(fidsize); + fe->logblks_rec = udf_rw64(0); /* intern */ + + crclen = sizeof(struct file_entry) - 1 - UDF_DESC_TAG_LENGTH; + crclen += udf_rw32(fe->l_ea) + fidsize; + fe->tag.desc_crc_len = udf_rw16(crclen); + + (void) udf_validate_tag_and_crc_sums((union dscrptr *) fe); + + return fidsize; +} + +/* --------------------------------------------------------------------- */ + +static int +udf_create_new_efe(struct udf_mount *ump, struct extfile_entry *efe, + int file_type, struct long_ad *node_icb, struct long_ad *parent_icb, + uint64_t parent_unique_id) +{ + struct timespec now; + struct icb_tag *icb; + uint64_t unique_id; + uint32_t fidsize, lb_num; + uint8_t *bpos; + int crclen; + + lb_num = udf_rw32(node_icb->loc.lb_num); + udf_inittag(ump, &efe->tag, TAGID_EXTFENTRY, lb_num); + icb = &efe->icbtag; + + /* + * Always use strategy type 4 unless on WORM wich we don't support + * (yet). Fill in defaults and set for internal allocation of data. + */ + icb->strat_type = udf_rw16(4); + icb->max_num_entries = udf_rw16(1); + icb->file_type = file_type; /* 8 bit */ + icb->flags = udf_rw16(UDF_ICB_INTERN_ALLOC); + + efe->perm = udf_rw32(0x7fff); /* all is allowed */ + efe->link_cnt = udf_rw16(0); /* explicit setting */ + + efe->ckpoint = udf_rw32(1); /* user supplied file version */ + + vfs_timestamp(&now); + udf_timespec_to_timestamp(&now, &efe->ctime); + udf_timespec_to_timestamp(&now, &efe->atime); + udf_timespec_to_timestamp(&now, &efe->attrtime); + udf_timespec_to_timestamp(&now, &efe->mtime); + + udf_set_regid(&efe->imp_id, IMPL_NAME); + udf_add_impl_regid(ump, &efe->imp_id); + + unique_id = udf_advance_uniqueid(ump); + efe->unique_id = udf_rw64(unique_id); + efe->l_ea = udf_rw32(0); + + /* if its a directory, create '..' */ + bpos = (uint8_t *) efe->data + udf_rw32(efe->l_ea); + fidsize = 0; + if (file_type == UDF_ICB_FILETYPE_DIRECTORY) { + fidsize = udf_create_parentfid(ump, + (struct fileid_desc *) bpos, parent_icb, + parent_unique_id); + } + + /* record fidlength information */ + efe->obj_size = udf_rw64(fidsize); + efe->inf_len = udf_rw64(fidsize); + efe->l_ad = udf_rw32(fidsize); + efe->logblks_rec = udf_rw64(0); /* intern */ + + crclen = sizeof(struct extfile_entry) - 1 - UDF_DESC_TAG_LENGTH; + crclen += udf_rw32(efe->l_ea) + fidsize; + efe->tag.desc_crc_len = udf_rw16(crclen); + + (void) udf_validate_tag_and_crc_sums((union dscrptr *) efe); + + return fidsize; +} + +/* --------------------------------------------------------------------- */ + +int +udf_dir_detach(struct udf_mount *ump, struct udf_node *dir_node, + struct udf_node *udf_node, struct componentname *cnp) +{ + struct vnode *dvp = dir_node->vnode; + struct dirhash *dirh; + struct dirhash_entry *dirh_ep; + struct file_entry *fe = dir_node->fe; + struct fileid_desc *fid; + struct dirent *dirent; + uint64_t diroffset; + uint32_t lb_size, fidsize; + int found, error; + char const *name = cnp->cn_nameptr; + int namelen = cnp->cn_namelen; + int hit, refcnt; + + /* get our dirhash and make sure its read in */ + dirhash_get(&dir_node->dir_hash); + error = udf_dirhash_fill(dir_node); + if (error) { + dirhash_put(dir_node->dir_hash); + return error; + } + dirh = dir_node->dir_hash; + + /* get directory filesize */ + if (!fe) { + assert(dir_node->efe); + } + + /* allocate temporary space for fid */ + lb_size = udf_rw32(dir_node->ump->logical_vol->lb_size); + fid = malloc(lb_size, M_UDFTEMP, M_WAITOK); + dirent = malloc(sizeof(struct dirent), M_UDFTEMP, M_WAITOK); + + /* search our dirhash hits */ + found = 0; + dirh_ep = NULL; + for (;;) { + hit = dirhash_lookup(dirh, name, namelen, &dirh_ep); + /* if no hit, abort the search */ + if (!hit) + break; + + /* check this hit */ + diroffset = dirh_ep->offset; + + /* transfer a new fid/dirent */ + error = udf_read_fid_stream(dvp, &diroffset, fid, dirent); + if (error) + break; + + /* see if its our entry */ + KASSERT(dirent->d_namlen == namelen); + if (strncmp(dirent->d_name, name, namelen) == 0) { + found = 1; + break; + } + } + + if (!found) + error = ENOENT; + if (error) + goto error_out; + + /* mark deleted */ + fid->file_char |= UDF_FILE_CHAR_DEL; +#ifdef UDF_COMPLETE_DELETE + memset(&fid->icb, 0, sizeof(fid->icb)); +#endif + (void) udf_validate_tag_and_crc_sums((union dscrptr *) fid); + + /* get size of fid and compensate for the read_fid_stream advance */ + fidsize = udf_fidsize(fid); + diroffset -= fidsize; + + /* write out */ + error = vn_rdwr(UIO_WRITE, dir_node->vnode, + fid, fidsize, diroffset, + UIO_SYSSPACE, IO_ALTSEMANTICS | IO_NODELOCKED, + FSCRED, NULL, NULL); + if (error) + goto error_out; + + /* get reference count of attached node */ + if (udf_node->fe) { + refcnt = udf_rw16(udf_node->fe->link_cnt); + } else { + KASSERT(udf_node->efe); + refcnt = udf_rw16(udf_node->efe->link_cnt); + } +#ifdef UDF_COMPLETE_DELETE + /* substract reference counter in attached node */ + refcnt -= 1; + if (udf_node->fe) { + udf_node->fe->link_cnt = udf_rw16(refcnt); + } else { + udf_node->efe->link_cnt = udf_rw16(refcnt); + } + + /* prevent writeout when refcnt == 0 */ + if (refcnt == 0) + udf_node->i_flags |= IN_DELETED; + + if (fid->file_char & UDF_FILE_CHAR_DIR) { + int drefcnt; + + /* substract reference counter in directory node */ + /* note subtract 2 (?) for its was also backreferenced */ + if (dir_node->fe) { + drefcnt = udf_rw16(dir_node->fe->link_cnt); + drefcnt -= 1; + dir_node->fe->link_cnt = udf_rw16(drefcnt); + } else { + KASSERT(dir_node->efe); + drefcnt = udf_rw16(dir_node->efe->link_cnt); + drefcnt -= 1; + dir_node->efe->link_cnt = udf_rw16(drefcnt); + } + } + + udf_node->i_flags |= IN_MODIFIED; + dir_node->i_flags |= IN_MODIFIED; +#endif + /* if it is/was a hardlink adjust the file count */ + if (refcnt > 0) + udf_adjust_filecount(udf_node, -1); + + /* remove from the dirhash */ + dirhash_remove(dirh, dirent, diroffset, + udf_fidsize(fid)); + +error_out: + free(fid, M_UDFTEMP); + free(dirent, M_UDFTEMP); + + dirhash_put(dir_node->dir_hash); + + return error; +} + +/* --------------------------------------------------------------------- */ + +int +udf_dir_update_rootentry(struct udf_mount *ump, struct udf_node *dir_node, + struct udf_node *new_parent_node) +{ + struct vnode *dvp = dir_node->vnode; + struct dirhash *dirh; + struct dirhash_entry *dirh_ep; + struct file_entry *fe; + struct extfile_entry *efe; + struct fileid_desc *fid; + struct dirent *dirent; + uint64_t diroffset; + uint64_t new_parent_unique_id; + uint32_t lb_size, fidsize; + int found, error; + char const *name = ".."; + int namelen = 2; + int hit; + + /* get our dirhash and make sure its read in */ + dirhash_get(&dir_node->dir_hash); + error = udf_dirhash_fill(dir_node); + if (error) { + dirhash_put(dir_node->dir_hash); + return error; + } + dirh = dir_node->dir_hash; + + /* get new parent's unique ID */ + fe = new_parent_node->fe; + efe = new_parent_node->efe; + if (fe) { + new_parent_unique_id = udf_rw64(fe->unique_id); + } else { + assert(efe); + new_parent_unique_id = udf_rw64(efe->unique_id); + } + + /* get directory filesize */ + fe = dir_node->fe; + efe = dir_node->efe; + if (!fe) { + assert(efe); + } + + /* allocate temporary space for fid */ + lb_size = udf_rw32(dir_node->ump->logical_vol->lb_size); + fid = malloc(lb_size, M_UDFTEMP, M_WAITOK); + dirent = malloc(sizeof(struct dirent), M_UDFTEMP, M_WAITOK); + + /* + * NOTE the standard does not dictate the FID entry '..' should be + * first, though in practice it will most likely be. + */ + + /* search our dirhash hits */ + found = 0; + dirh_ep = NULL; + for (;;) { + hit = dirhash_lookup(dirh, name, namelen, &dirh_ep); + /* if no hit, abort the search */ + if (!hit) + break; + + /* check this hit */ + diroffset = dirh_ep->offset; + + /* transfer a new fid/dirent */ + error = udf_read_fid_stream(dvp, &diroffset, fid, dirent); + if (error) + break; + + /* see if its our entry */ + KASSERT(dirent->d_namlen == namelen); + if (strncmp(dirent->d_name, name, namelen) == 0) { + found = 1; + break; + } + } + + if (!found) + error = ENOENT; + if (error) + goto error_out; + + /* update our ICB to the new parent, hit of lower 32 bits of uniqueid */ + fid->icb = new_parent_node->write_loc; + fid->icb.longad_uniqueid = udf_rw32(new_parent_unique_id); + + (void) udf_validate_tag_and_crc_sums((union dscrptr *) fid); + + /* get size of fid and compensate for the read_fid_stream advance */ + fidsize = udf_fidsize(fid); + diroffset -= fidsize; + + /* write out */ + error = vn_rdwr(UIO_WRITE, dir_node->vnode, + fid, fidsize, diroffset, + UIO_SYSSPACE, IO_ALTSEMANTICS | IO_NODELOCKED, + FSCRED, NULL, NULL); + + /* nothing to be done in the dirhash */ + +error_out: + free(fid, M_UDFTEMP); + free(dirent, M_UDFTEMP); + + dirhash_put(dir_node->dir_hash); + + return error; +} + +/* --------------------------------------------------------------------- */ + +/* + * We are not allowed to split the fid tag itself over an logical block so + * check the space remaining in the logical block. + * + * We try to select the smallest candidate for recycling or when none is + * found, append a new one at the end of the directory. + */ + +int +udf_dir_attach(struct udf_mount *ump, struct udf_node *dir_node, + struct udf_node *udf_node, struct vattr *vap, struct componentname *cnp) +{ + struct vnode *dvp = dir_node->vnode; + struct dirhash *dirh; + struct dirhash_entry *dirh_ep; + struct fileid_desc *fid; + struct icb_tag *icbtag; + struct charspec osta_charspec; + struct dirent dirent; + uint64_t unique_id, dir_size; + uint64_t fid_pos, end_fid_pos, chosen_fid_pos; + uint32_t chosen_size, chosen_size_diff; + int lb_size, lb_rest, fidsize, this_fidsize, size_diff; + int file_char, refcnt, icbflags, addr_type, hit, error; + + /* get our dirhash and make sure its read in */ + dirhash_get(&dir_node->dir_hash); + error = udf_dirhash_fill(dir_node); + if (error) { + dirhash_put(dir_node->dir_hash); + return error; + } + dirh = dir_node->dir_hash; + + /* get info */ + lb_size = udf_rw32(ump->logical_vol->lb_size); + udf_osta_charset(&osta_charspec); + + if (dir_node->fe) { + dir_size = udf_rw64(dir_node->fe->inf_len); + icbtag = &dir_node->fe->icbtag; + } else { + dir_size = udf_rw64(dir_node->efe->inf_len); + icbtag = &dir_node->efe->icbtag; + } + + icbflags = udf_rw16(icbtag->flags); + addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK; + + if (udf_node->fe) { + unique_id = udf_rw64(udf_node->fe->unique_id); + refcnt = udf_rw16(udf_node->fe->link_cnt); + } else { + unique_id = udf_rw64(udf_node->efe->unique_id); + refcnt = udf_rw16(udf_node->efe->link_cnt); + } + + if (refcnt > 0) { + unique_id = udf_advance_uniqueid(ump); + udf_adjust_filecount(udf_node, 1); + } + + /* determine file characteristics */ + file_char = 0; /* visible non deleted file and not stream metadata */ + if (vap->va_type == VDIR) + file_char = UDF_FILE_CHAR_DIR; + + /* malloc scrap buffer */ + fid = malloc(lb_size, M_TEMP, M_WAITOK|M_ZERO); + + /* calculate _minimum_ fid size */ + unix_to_udf_name((char *) fid->data, &fid->l_fi, + cnp->cn_nameptr, cnp->cn_namelen, &osta_charspec); + fidsize = UDF_FID_SIZE + fid->l_fi; + fidsize = (fidsize + 3) & ~3; /* multiple of 4 */ + + /* find position that will fit the FID */ + chosen_fid_pos = dir_size; + chosen_size = 0; + chosen_size_diff = UINT_MAX; + + /* shut up gcc */ + dirent.d_namlen = 0; + + /* search our dirhash hits */ + error = 0; + dirh_ep = NULL; + for (;;) { + hit = dirhash_lookup_freed(dirh, fidsize, &dirh_ep); + /* if no hit, abort the search */ + if (!hit) + break; + + /* check this hit for size */ + this_fidsize = dirh_ep->entry_size; + + /* check this hit */ + fid_pos = dirh_ep->offset; + end_fid_pos = fid_pos + this_fidsize; + size_diff = this_fidsize - fidsize; + lb_rest = lb_size - (end_fid_pos % lb_size); + +#ifndef UDF_COMPLETE_DELETE + /* transfer a new fid/dirent */ + error = udf_read_fid_stream(vp, &fid_pos, fid, dirent); + if (error) + goto error_out; + + /* only reuse entries that are wiped */ + /* check if the len + loc are marked zero */ + if (udf_rw32(fid->icb.len) != 0) + continue; + if (udf_rw32(fid->icb.loc.lb_num) != 0) + continue; + if (udf_rw16(fid->icb.loc.part_num) != 0) + continue; +#endif /* UDF_COMPLETE_DELETE */ + + /* select if not splitting the tag and its smaller */ + if ((size_diff >= 0) && + (size_diff < chosen_size_diff) && + (lb_rest >= sizeof(struct desc_tag))) + { + /* UDF 2.3.4.2+3 specifies rules for iu size */ + if ((size_diff == 0) || (size_diff >= 32)) { + chosen_fid_pos = fid_pos; + chosen_size = this_fidsize; + chosen_size_diff = size_diff; + } + } + } + + + /* extend directory if no other candidate found */ + if (chosen_size == 0) { + chosen_fid_pos = dir_size; + chosen_size = fidsize; + chosen_size_diff = 0; + + /* special case UDF 2.00+ 2.3.4.4, no splitting up fid tag */ + if (addr_type == UDF_ICB_INTERN_ALLOC) { + /* pre-grow directory to see if we're to switch */ + udf_grow_node(dir_node, dir_size + chosen_size); + + icbflags = udf_rw16(icbtag->flags); + addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK; + } + + /* make sure the next fid desc_tag won't be splitted */ + if (addr_type != UDF_ICB_INTERN_ALLOC) { + end_fid_pos = chosen_fid_pos + chosen_size; + lb_rest = lb_size - (end_fid_pos % lb_size); + + /* pad with implementation use regid if needed */ + if (lb_rest < sizeof(struct desc_tag)) + chosen_size += 32; + } + } + chosen_size_diff = chosen_size - fidsize; + + /* populate the FID */ + memset(fid, 0, lb_size); + udf_inittag(ump, &fid->tag, TAGID_FID, 0); + fid->file_version_num = udf_rw16(1); /* UDF 2.3.4.1 */ + fid->file_char = file_char; + fid->icb = udf_node->loc; + fid->icb.longad_uniqueid = udf_rw32((uint32_t) unique_id); + fid->l_iu = udf_rw16(0); + + if (chosen_size > fidsize) { + /* insert implementation-use regid to space it correctly */ + fid->l_iu = udf_rw16(chosen_size_diff); + + /* set implementation use */ + udf_set_regid((struct regid *) fid->data, IMPL_NAME); + udf_add_impl_regid(ump, (struct regid *) fid->data); + } + + /* fill in name */ + unix_to_udf_name((char *) fid->data + udf_rw16(fid->l_iu), + &fid->l_fi, cnp->cn_nameptr, cnp->cn_namelen, &osta_charspec); + + fid->tag.desc_crc_len = udf_rw16(chosen_size - UDF_DESC_TAG_LENGTH); + (void) udf_validate_tag_and_crc_sums((union dscrptr *) fid); + + /* writeout FID/update parent directory */ + error = vn_rdwr(UIO_WRITE, dvp, + fid, chosen_size, chosen_fid_pos, + UIO_SYSSPACE, IO_ALTSEMANTICS | IO_NODELOCKED, + FSCRED, NULL, NULL); + + if (error) + goto error_out; + + /* add reference counter in attached node */ + if (udf_node->fe) { + refcnt = udf_rw16(udf_node->fe->link_cnt); + udf_node->fe->link_cnt = udf_rw16(refcnt+1); + } else { + KASSERT(udf_node->efe); + refcnt = udf_rw16(udf_node->efe->link_cnt); + udf_node->efe->link_cnt = udf_rw16(refcnt+1); + } + + /* mark not deleted if it was... just in case, but do warn */ + if (udf_node->i_flags & IN_DELETED) { + printf("udf: warning, marking a file undeleted\n"); + udf_node->i_flags &= ~IN_DELETED; + } + + if (file_char & UDF_FILE_CHAR_DIR) { + /* add reference counter in directory node for '..' */ + if (dir_node->fe) { + refcnt = udf_rw16(dir_node->fe->link_cnt); + refcnt++; + dir_node->fe->link_cnt = udf_rw16(refcnt); + } else { + KASSERT(dir_node->efe); + refcnt = udf_rw16(dir_node->efe->link_cnt); + refcnt++; + dir_node->efe->link_cnt = udf_rw16(refcnt); + } + } + + /* append to the dirhash */ + /* NOTE do not use dirent anymore or it won't match later! */ + udf_to_unix_name(dirent.d_name, NAME_MAX, + (char *) fid->data + udf_rw16(fid->l_iu), fid->l_fi, &osta_charspec); + dirent.d_namlen = strlen(dirent.d_name); + dirhash_enter(dirh, &dirent, chosen_fid_pos, + udf_fidsize(fid), 1); + + /* note updates */ + udf_node->i_flags |= IN_CHANGE | IN_MODIFY; /* | IN_CREATE? */ + /* VN_KNOTE(udf_node, ...) */ + udf_update(udf_node->vnode, NULL, NULL, NULL, 0); + +error_out: + free(fid, M_TEMP); + + dirhash_put(dir_node->dir_hash); + + return error; +} + +/* --------------------------------------------------------------------- */ + +/* + * Each node can have an attached streamdir node though not recursively. These + * are otherwise known as named substreams/named extended attributes that have + * no size limitations. + * + * `Normal' extended attributes are indicated with a number and are recorded + * in either the fe/efe descriptor itself for small descriptors or recorded in + * the attached extended attribute file. Since these spaces can get + * fragmented, care ought to be taken. + * + * Since the size of the space reserved for allocation descriptors is limited, + * there is a mechanim provided for extending this space; this is done by a + * special extent to allow schrinking of the allocations without breaking the + * linkage to the allocation extent descriptor. + */ + +int +udf_get_node(struct udf_mount *ump, struct long_ad *node_icb_loc, + struct udf_node **udf_noderes) +{ + union dscrptr *dscr; + struct udf_node *udf_node; + struct vnode *nvp; + struct long_ad icb_loc, last_fe_icb_loc; + uint64_t file_size; + uint32_t lb_size, sector, dummy; + int udf_file_type, dscr_type, strat, strat4096, needs_indirect; + int slot, eof, error; + + DPRINTF(NODE, ("udf_get_node called\n")); + *udf_noderes = udf_node = NULL; + + /* lock to disallow simultanious creation of same udf_node */ + mutex_enter(&ump->get_node_lock); + + DPRINTF(NODE, ("\tlookup in hash table\n")); + /* lookup in hash table */ + assert(ump); + assert(node_icb_loc); + udf_node = udf_node_lookup(ump, node_icb_loc); + if (udf_node) { + DPRINTF(NODE, ("\tgot it from the hash!\n")); + /* vnode is returned locked */ + *udf_noderes = udf_node; + mutex_exit(&ump->get_node_lock); + return 0; + } + + /* garbage check: translate udf_node_icb_loc to sectornr */ + error = udf_translate_vtop(ump, node_icb_loc, §or, &dummy); + if (error) { + DPRINTF(NODE, ("\tcan't translate icb address!\n")); + /* no use, this will fail anyway */ + mutex_exit(&ump->get_node_lock); + return EINVAL; + } + + /* build udf_node (do initialise!) */ + udf_node = pool_get(&udf_node_pool, PR_WAITOK); + memset(udf_node, 0, sizeof(struct udf_node)); + + DPRINTF(NODE, ("\tget new vnode\n")); + /* give it a vnode */ + error = getnewvnode(VT_UDF, ump->vfs_mountp, udf_vnodeop_p, NULL, &nvp); + if (error) { + pool_put(&udf_node_pool, udf_node); + mutex_exit(&ump->get_node_lock); + return error; + } + + /* always return locked vnode */ + if ((error = vn_lock(nvp, LK_EXCLUSIVE | LK_RETRY))) { + /* recycle vnode and unlock; simultanious will fail too */ + ungetnewvnode(nvp); + mutex_exit(&ump->get_node_lock); + return error; + } + + /* initialise crosslinks, note location of fe/efe for hashing */ + udf_node->ump = ump; + udf_node->vnode = nvp; + nvp->v_data = udf_node; + udf_node->loc = *node_icb_loc; + udf_node->lockf = 0; + mutex_init(&udf_node->node_mutex, MUTEX_DEFAULT, IPL_NONE); + cv_init(&udf_node->node_lock, "udf_nlk"); + genfs_node_init(nvp, &udf_genfsops); /* inititise genfs */ + udf_node->outstanding_bufs = 0; + udf_node->outstanding_nodedscr = 0; + udf_node->uncommitted_lbs = 0; + + /* check if we're fetching the root */ + if (ump->fileset_desc) + if (memcmp(&udf_node->loc, &ump->fileset_desc->rootdir_icb, + sizeof(struct long_ad)) == 0) + nvp->v_vflag |= VV_ROOT; + + /* insert into the hash lookup */ + udf_register_node(udf_node); + + /* safe to unlock, the entry is in the hash table, vnode is locked */ + mutex_exit(&ump->get_node_lock); + + icb_loc = *node_icb_loc; + needs_indirect = 0; + strat4096 = 0; + udf_file_type = UDF_ICB_FILETYPE_UNKNOWN; + file_size = 0; + lb_size = udf_rw32(ump->logical_vol->lb_size); + + DPRINTF(NODE, ("\tstart reading descriptors\n")); + do { + /* try to read in fe/efe */ + error = udf_read_logvol_dscr(ump, &icb_loc, &dscr); + + /* blank sector marks end of sequence, check this */ + if ((dscr == NULL) && (!strat4096)) + error = ENOENT; + + /* break if read error or blank sector */ + if (error || (dscr == NULL)) + break; + + /* process descriptor based on the descriptor type */ + dscr_type = udf_rw16(dscr->tag.id); + DPRINTF(NODE, ("\tread descriptor %d\n", dscr_type)); + + /* if dealing with an indirect entry, follow the link */ + if (dscr_type == TAGID_INDIRECTENTRY) { + needs_indirect = 0; + udf_free_logvol_dscr(ump, &icb_loc, dscr); + icb_loc = dscr->inde.indirect_icb; + continue; + } + + /* only file entries and extended file entries allowed here */ + if ((dscr_type != TAGID_FENTRY) && + (dscr_type != TAGID_EXTFENTRY)) { + udf_free_logvol_dscr(ump, &icb_loc, dscr); + error = ENOENT; + break; + } + + KASSERT(udf_tagsize(dscr, lb_size) == lb_size); + + /* choose this one */ + last_fe_icb_loc = icb_loc; + + /* record and process/update (ext)fentry */ + if (dscr_type == TAGID_FENTRY) { + if (udf_node->fe) + udf_free_logvol_dscr(ump, &last_fe_icb_loc, + udf_node->fe); + udf_node->fe = &dscr->fe; + strat = udf_rw16(udf_node->fe->icbtag.strat_type); + udf_file_type = udf_node->fe->icbtag.file_type; + file_size = udf_rw64(udf_node->fe->inf_len); + } else { + if (udf_node->efe) + udf_free_logvol_dscr(ump, &last_fe_icb_loc, + udf_node->efe); + udf_node->efe = &dscr->efe; + strat = udf_rw16(udf_node->efe->icbtag.strat_type); + udf_file_type = udf_node->efe->icbtag.file_type; + file_size = udf_rw64(udf_node->efe->inf_len); + } + + /* check recording strategy (structure) */ + + /* + * Strategy 4096 is a daisy linked chain terminating with an + * unrecorded sector or a TERM descriptor. The next + * descriptor is to be found in the sector that follows the + * current sector. + */ + if (strat == 4096) { + strat4096 = 1; + needs_indirect = 1; + + icb_loc.loc.lb_num = udf_rw32(icb_loc.loc.lb_num) + 1; + } + + /* + * Strategy 4 is the normal strategy and terminates, but if + * we're in strategy 4096, we can't have strategy 4 mixed in + */ + + if (strat == 4) { + if (strat4096) { + error = EINVAL; + break; + } + break; /* done */ + } + } while (!error); + + /* first round of cleanup code */ + if (error) { + DPRINTF(NODE, ("\tnode fe/efe failed!\n")); + /* recycle udf_node */ + udf_dispose_node(udf_node); + + VOP_UNLOCK(nvp); + nvp->v_data = NULL; + ungetnewvnode(nvp); + + return EINVAL; /* error code ok? */ + } + DPRINTF(NODE, ("\tnode fe/efe read in fine\n")); + + /* assert no references to dscr anymore beyong this point */ + assert((udf_node->fe) || (udf_node->efe)); + dscr = NULL; + + /* + * Remember where to record an updated version of the descriptor. If + * there is a sequence of indirect entries, icb_loc will have been + * updated. Its the write disipline to allocate new space and to make + * sure the chain is maintained. + * + * `needs_indirect' flags if the next location is to be filled with + * with an indirect entry. + */ + udf_node->write_loc = icb_loc; + udf_node->needs_indirect = needs_indirect; + + /* + * Go trough all allocations extents of this descriptor and when + * encountering a redirect read in the allocation extension. These are + * daisy-chained. + */ + UDF_LOCK_NODE(udf_node, 0); + udf_node->num_extensions = 0; + + error = 0; + slot = 0; + for (;;) { + udf_get_adslot(udf_node, slot, &icb_loc, &eof); + DPRINTF(ADWLK, ("slot %d, eof = %d, flags = %d, len = %d, " + "lb_num = %d, part = %d\n", slot, eof, + UDF_EXT_FLAGS(udf_rw32(icb_loc.len)), + UDF_EXT_LEN(udf_rw32(icb_loc.len)), + udf_rw32(icb_loc.loc.lb_num), + udf_rw16(icb_loc.loc.part_num))); + if (eof) + break; + slot++; + + if (UDF_EXT_FLAGS(udf_rw32(icb_loc.len)) != UDF_EXT_REDIRECT) + continue; + + DPRINTF(NODE, ("\tgot redirect extent\n")); + if (udf_node->num_extensions >= UDF_MAX_ALLOC_EXTENTS) { + DPRINTF(ALLOC, ("udf_get_node: implementation limit, " + "too many allocation extensions on " + "udf_node\n")); + error = EINVAL; + break; + } + + /* length can only be *one* lb : UDF 2.50/2.3.7.1 */ + if (UDF_EXT_LEN(udf_rw32(icb_loc.len)) != lb_size) { + DPRINTF(ALLOC, ("udf_get_node: bad allocation " + "extension size in udf_node\n")); + error = EINVAL; + break; + } + + DPRINTF(NODE, ("read allocation extent at lb_num %d\n", + UDF_EXT_LEN(udf_rw32(icb_loc.loc.lb_num)))); + /* load in allocation extent */ + error = udf_read_logvol_dscr(ump, &icb_loc, &dscr); + if (error || (dscr == NULL)) + break; + + /* process read-in descriptor */ + dscr_type = udf_rw16(dscr->tag.id); + + if (dscr_type != TAGID_ALLOCEXTENT) { + udf_free_logvol_dscr(ump, &icb_loc, dscr); + error = ENOENT; + break; + } + + DPRINTF(NODE, ("\trecording redirect extent\n")); + udf_node->ext[udf_node->num_extensions] = &dscr->aee; + udf_node->ext_loc[udf_node->num_extensions] = icb_loc; + + udf_node->num_extensions++; + + } /* while */ + UDF_UNLOCK_NODE(udf_node, 0); + + /* second round of cleanup code */ + if (error) { + /* recycle udf_node */ + udf_dispose_node(udf_node); + + VOP_UNLOCK(nvp); + nvp->v_data = NULL; + ungetnewvnode(nvp); + + return EINVAL; /* error code ok? */ + } + + DPRINTF(NODE, ("\tnode read in fine\n")); + + /* + * Translate UDF filetypes into vnode types. + * + * Systemfiles like the meta main and mirror files are not treated as + * normal files, so we type them as having no type. UDF dictates that + * they are not allowed to be visible. + */ + + switch (udf_file_type) { + case UDF_ICB_FILETYPE_DIRECTORY : + case UDF_ICB_FILETYPE_STREAMDIR : + nvp->v_type = VDIR; + break; + case UDF_ICB_FILETYPE_BLOCKDEVICE : + nvp->v_type = VBLK; + break; + case UDF_ICB_FILETYPE_CHARDEVICE : + nvp->v_type = VCHR; + break; + case UDF_ICB_FILETYPE_SOCKET : + nvp->v_type = VSOCK; + break; + case UDF_ICB_FILETYPE_FIFO : + nvp->v_type = VFIFO; + break; + case UDF_ICB_FILETYPE_SYMLINK : + nvp->v_type = VLNK; + break; + case UDF_ICB_FILETYPE_VAT : + case UDF_ICB_FILETYPE_META_MAIN : + case UDF_ICB_FILETYPE_META_MIRROR : + nvp->v_type = VNON; + break; + case UDF_ICB_FILETYPE_RANDOMACCESS : + case UDF_ICB_FILETYPE_REALTIME : + nvp->v_type = VREG; + break; + default: + /* YIKES, something else */ + nvp->v_type = VNON; + } + + /* TODO specfs, fifofs etc etc. vnops setting */ + + /* don't forget to set vnode's v_size */ + uvm_vnp_setsize(nvp, file_size); + + /* TODO ext attr and streamdir udf_nodes */ + + *udf_noderes = udf_node; + + return 0; +} + +/* --------------------------------------------------------------------- */ + +int +udf_writeout_node(struct udf_node *udf_node, int waitfor) +{ + union dscrptr *dscr; + struct long_ad *loc; + int extnr, error; + + DPRINTF(NODE, ("udf_writeout_node called\n")); + + KASSERT(udf_node->outstanding_bufs == 0); + KASSERT(udf_node->outstanding_nodedscr == 0); + + KASSERT(LIST_EMPTY(&udf_node->vnode->v_dirtyblkhd)); + + if (udf_node->i_flags & IN_DELETED) { + DPRINTF(NODE, ("\tnode deleted; not writing out\n")); + udf_cleanup_reservation(udf_node); + return 0; + } + + /* lock node; unlocked in callback */ + UDF_LOCK_NODE(udf_node, 0); + + /* remove pending reservations, we're written out */ + udf_cleanup_reservation(udf_node); + + /* at least one descriptor writeout */ + udf_node->outstanding_nodedscr = 1; + + /* we're going to write out the descriptor so clear the flags */ + udf_node->i_flags &= ~(IN_MODIFIED | IN_ACCESSED); + + /* if we were rebuild, write out the allocation extents */ + if (udf_node->i_flags & IN_NODE_REBUILD) { + /* mark outstanding node descriptors and issue them */ + udf_node->outstanding_nodedscr += udf_node->num_extensions; + for (extnr = 0; extnr < udf_node->num_extensions; extnr++) { + loc = &udf_node->ext_loc[extnr]; + dscr = (union dscrptr *) udf_node->ext[extnr]; + error = udf_write_logvol_dscr(udf_node, dscr, loc, 0); + if (error) + return error; + } + /* mark allocation extents written out */ + udf_node->i_flags &= ~(IN_NODE_REBUILD); + } + + if (udf_node->fe) { + KASSERT(udf_node->efe == NULL); + dscr = (union dscrptr *) udf_node->fe; + } else { + KASSERT(udf_node->efe); + KASSERT(udf_node->fe == NULL); + dscr = (union dscrptr *) udf_node->efe; + } + KASSERT(dscr); + + loc = &udf_node->write_loc; + error = udf_write_logvol_dscr(udf_node, dscr, loc, waitfor); + + return error; +} + +/* --------------------------------------------------------------------- */ + +int +udf_dispose_node(struct udf_node *udf_node) +{ + struct vnode *vp; + int extnr; + + DPRINTF(NODE, ("udf_dispose_node called on node %p\n", udf_node)); + if (!udf_node) { + DPRINTF(NODE, ("UDF: Dispose node on node NULL, ignoring\n")); + return 0; + } + + vp = udf_node->vnode; +#ifdef DIAGNOSTIC + if (vp->v_numoutput) + panic("disposing UDF node with pending I/O's, udf_node = %p, " + "v_numoutput = %d", udf_node, vp->v_numoutput); +#endif + + udf_cleanup_reservation(udf_node); + + /* TODO extended attributes and streamdir */ + + /* remove dirhash if present */ + dirhash_purge(&udf_node->dir_hash); + + /* remove from our hash lookup table */ + udf_deregister_node(udf_node); + + /* destroy our lock */ + mutex_destroy(&udf_node->node_mutex); + cv_destroy(&udf_node->node_lock); + + /* dissociate our udf_node from the vnode */ + genfs_node_destroy(udf_node->vnode); + vp->v_data = NULL; + + /* free associated memory and the node itself */ + for (extnr = 0; extnr < udf_node->num_extensions; extnr++) { + udf_free_logvol_dscr(udf_node->ump, &udf_node->ext_loc[extnr], + udf_node->ext[extnr]); + udf_node->ext[extnr] = (void *) 0xdeadcccc; + } + + if (udf_node->fe) + udf_free_logvol_dscr(udf_node->ump, &udf_node->loc, + udf_node->fe); + if (udf_node->efe) + udf_free_logvol_dscr(udf_node->ump, &udf_node->loc, + udf_node->efe); + + udf_node->fe = (void *) 0xdeadaaaa; + udf_node->efe = (void *) 0xdeadbbbb; + udf_node->ump = (void *) 0xdeadbeef; + pool_put(&udf_node_pool, udf_node); + + return 0; +} + + + +/* + * create a new node using the specified vnodeops, vap and cnp but with the + * udf_file_type. This allows special files to be created. Use with care. + */ + +static int +udf_create_node_raw(struct vnode *dvp, struct vnode **vpp, int udf_file_type, + int (**vnodeops)(void *), struct vattr *vap, struct componentname *cnp) +{ + union dscrptr *dscr; + struct udf_node *dir_node = VTOI(dvp); + struct udf_node *udf_node; + struct udf_mount *ump = dir_node->ump; + struct vnode *nvp; + struct long_ad node_icb_loc; + uint64_t parent_unique_id; + uint64_t lmapping; + uint32_t lb_size, lb_num; + uint16_t vpart_num; + uid_t uid; + gid_t gid, parent_gid; + int fid_size, error; + + lb_size = udf_rw32(ump->logical_vol->lb_size); + *vpp = NULL; + + /* allocate vnode */ + error = getnewvnode(VT_UDF, ump->vfs_mountp, vnodeops, NULL, &nvp); + if (error) + return error; + + /* lock node */ + error = vn_lock(nvp, LK_EXCLUSIVE | LK_RETRY); + if (error) + goto error_out_unget; + + /* reserve space for one logical block */ + vpart_num = ump->node_part; + error = udf_reserve_space(ump, NULL, UDF_C_NODE, + vpart_num, 1, /* can_fail */ true); + if (error) + goto error_out_unlock; + + /* allocate node */ + error = udf_allocate_space(ump, NULL, UDF_C_NODE, + vpart_num, 1, &lmapping); + if (error) + goto error_out_unreserve; + lb_num = lmapping; + + /* initialise pointer to location */ + memset(&node_icb_loc, 0, sizeof(struct long_ad)); + node_icb_loc.len = udf_rw32(lb_size); + node_icb_loc.loc.lb_num = udf_rw32(lb_num); + node_icb_loc.loc.part_num = udf_rw16(vpart_num); + + /* build udf_node (do initialise!) */ + udf_node = pool_get(&udf_node_pool, PR_WAITOK); + memset(udf_node, 0, sizeof(struct udf_node)); + + /* initialise crosslinks, note location of fe/efe for hashing */ + /* bugalert: synchronise with udf_get_node() */ + udf_node->ump = ump; + udf_node->vnode = nvp; + nvp->v_data = udf_node; + udf_node->loc = node_icb_loc; + udf_node->write_loc = node_icb_loc; + udf_node->lockf = 0; + mutex_init(&udf_node->node_mutex, MUTEX_DEFAULT, IPL_NONE); + cv_init(&udf_node->node_lock, "udf_nlk"); + udf_node->outstanding_bufs = 0; + udf_node->outstanding_nodedscr = 0; + udf_node->uncommitted_lbs = 0; + + /* initialise genfs */ + genfs_node_init(nvp, &udf_genfsops); + + /* insert into the hash lookup */ + udf_register_node(udf_node); + + /* get parent's unique ID for refering '..' if its a directory */ + if (dir_node->fe) { + parent_unique_id = udf_rw64(dir_node->fe->unique_id); + parent_gid = (gid_t) udf_rw32(dir_node->fe->gid); + } else { + parent_unique_id = udf_rw64(dir_node->efe->unique_id); + parent_gid = (gid_t) udf_rw32(dir_node->efe->gid); + } + + /* get descriptor */ + udf_create_logvol_dscr(ump, udf_node, &node_icb_loc, &dscr); + + /* choose a fe or an efe for it */ + if (udf_rw16(ump->logical_vol->tag.descriptor_ver) == 2) { + udf_node->fe = &dscr->fe; + fid_size = udf_create_new_fe(ump, udf_node->fe, + udf_file_type, &udf_node->loc, + &dir_node->loc, parent_unique_id); + /* TODO add extended attribute for creation time */ + } else { + udf_node->efe = &dscr->efe; + fid_size = udf_create_new_efe(ump, udf_node->efe, + udf_file_type, &udf_node->loc, + &dir_node->loc, parent_unique_id); + } + KASSERT(dscr->tag.tag_loc == udf_node->loc.loc.lb_num); + + /* update vnode's size and type */ + nvp->v_type = vap->va_type; + uvm_vnp_setsize(nvp, fid_size); + + /* set access mode */ + udf_setaccessmode(udf_node, vap->va_mode); + + /* set ownership */ + uid = kauth_cred_geteuid(cnp->cn_cred); + gid = parent_gid; + udf_setownership(udf_node, uid, gid); + + error = udf_dir_attach(ump, dir_node, udf_node, vap, cnp); + if (error) { + /* free disc allocation for node */ + udf_free_allocated_space(ump, lb_num, vpart_num, 1); + + /* recycle udf_node */ + udf_dispose_node(udf_node); + vput(nvp); + + *vpp = NULL; + return error; + } + + /* adjust file count */ + udf_adjust_filecount(udf_node, 1); + + /* return result */ + *vpp = nvp; + + return 0; + +error_out_unreserve: + udf_do_unreserve_space(ump, NULL, vpart_num, 1); + +error_out_unlock: + VOP_UNLOCK(nvp); + +error_out_unget: + nvp->v_data = NULL; + ungetnewvnode(nvp); + + return error; +} + + +int +udf_create_node(struct vnode *dvp, struct vnode **vpp, struct vattr *vap, + struct componentname *cnp) +{ + int (**vnodeops)(void *); + int udf_file_type; + + DPRINTF(NODE, ("udf_create_node called\n")); + + /* what type are we creating ? */ + vnodeops = udf_vnodeop_p; + /* start with a default */ + udf_file_type = UDF_ICB_FILETYPE_RANDOMACCESS; + + *vpp = NULL; + + switch (vap->va_type) { + case VREG : + udf_file_type = UDF_ICB_FILETYPE_RANDOMACCESS; + break; + case VDIR : + udf_file_type = UDF_ICB_FILETYPE_DIRECTORY; + break; + case VLNK : + udf_file_type = UDF_ICB_FILETYPE_SYMLINK; + break; + case VBLK : + udf_file_type = UDF_ICB_FILETYPE_BLOCKDEVICE; + /* specfs */ + return ENOTSUP; + break; + case VCHR : + udf_file_type = UDF_ICB_FILETYPE_CHARDEVICE; + /* specfs */ + return ENOTSUP; + break; + case VFIFO : + udf_file_type = UDF_ICB_FILETYPE_FIFO; + /* specfs */ + return ENOTSUP; + break; + case VSOCK : + udf_file_type = UDF_ICB_FILETYPE_SOCKET; + /* specfs */ + return ENOTSUP; + break; + case VNON : + case VBAD : + default : + /* nothing; can we even create these? */ + return EINVAL; + } + + return udf_create_node_raw(dvp, vpp, udf_file_type, vnodeops, vap, cnp); +} + +/* --------------------------------------------------------------------- */ + +static void +udf_free_descriptor_space(struct udf_node *udf_node, struct long_ad *loc, void *mem) +{ + struct udf_mount *ump = udf_node->ump; + uint32_t lb_size, lb_num, len, num_lb; + uint16_t vpart_num; + + /* is there really one? */ + if (mem == NULL) + return; + + /* got a descriptor here */ + len = UDF_EXT_LEN(udf_rw32(loc->len)); + lb_num = udf_rw32(loc->loc.lb_num); + vpart_num = udf_rw16(loc->loc.part_num); + + lb_size = udf_rw32(ump->logical_vol->lb_size); + num_lb = (len + lb_size -1) / lb_size; + + udf_free_allocated_space(ump, lb_num, vpart_num, num_lb); +} + +void +udf_delete_node(struct udf_node *udf_node) +{ + void *dscr; + struct long_ad *loc; + int extnr, lvint, dummy; + + /* paranoia check on integrity; should be open!; we could panic */ + lvint = udf_rw32(udf_node->ump->logvol_integrity->integrity_type); + if (lvint == UDF_INTEGRITY_CLOSED) + printf("\tIntegrity was CLOSED!\n"); + + /* whatever the node type, change its size to zero */ + (void) udf_resize_node(udf_node, 0, &dummy); + + /* force it to be `clean'; no use writing it out */ + udf_node->i_flags &= ~(IN_MODIFIED | IN_ACCESSED | IN_ACCESS | + IN_CHANGE | IN_UPDATE | IN_MODIFY); + + /* adjust file count */ + udf_adjust_filecount(udf_node, -1); + + /* + * Free its allocated descriptors; memory will be released when + * vop_reclaim() is called. + */ + loc = &udf_node->loc; + + dscr = udf_node->fe; + udf_free_descriptor_space(udf_node, loc, dscr); + dscr = udf_node->efe; + udf_free_descriptor_space(udf_node, loc, dscr); + + for (extnr = 0; extnr < UDF_MAX_ALLOC_EXTENTS; extnr++) { + dscr = udf_node->ext[extnr]; + loc = &udf_node->ext_loc[extnr]; + udf_free_descriptor_space(udf_node, loc, dscr); + } +} + +/* --------------------------------------------------------------------- */ + +/* set new filesize; node but be LOCKED on entry and is locked on exit */ +int +udf_resize_node(struct udf_node *udf_node, uint64_t new_size, int *extended) +{ + struct file_entry *fe = udf_node->fe; + struct extfile_entry *efe = udf_node->efe; + uint64_t file_size; + int error; + + if (fe) { + file_size = udf_rw64(fe->inf_len); + } else { + assert(udf_node->efe); + file_size = udf_rw64(efe->inf_len); + } + + DPRINTF(ATTR, ("\tchanging file length from %"PRIu64" to %"PRIu64"\n", + file_size, new_size)); + + /* if not changing, we're done */ + if (file_size == new_size) + return 0; + + *extended = (new_size > file_size); + if (*extended) { + error = udf_grow_node(udf_node, new_size); + } else { + error = udf_shrink_node(udf_node, new_size); + } + + return error; +} + + +/* --------------------------------------------------------------------- */ + +void +udf_itimes(struct udf_node *udf_node, struct timespec *acc, + struct timespec *mod, struct timespec *birth) +{ + struct timespec now; + struct file_entry *fe; + struct extfile_entry *efe; + struct filetimes_extattr_entry *ft_extattr; + struct timestamp *atime, *mtime, *attrtime, *ctime; + struct timestamp fe_ctime; + struct timespec cur_birth; + uint32_t offset, a_l; + uint8_t *filedata; + int error; + + /* protect against rogue values */ + if (!udf_node) + return; + + fe = udf_node->fe; + efe = udf_node->efe; + + if (!(udf_node->i_flags & (IN_ACCESS|IN_CHANGE|IN_UPDATE|IN_MODIFY))) + return; + + /* get descriptor information */ + if (fe) { + atime = &fe->atime; + mtime = &fe->mtime; + attrtime = &fe->attrtime; + filedata = fe->data; + + /* initial save dummy setting */ + ctime = &fe_ctime; + + /* check our extended attribute if present */ + error = udf_extattr_search_intern(udf_node, + UDF_FILETIMES_ATTR_NO, "", &offset, &a_l); + if (!error) { + ft_extattr = (struct filetimes_extattr_entry *) + (filedata + offset); + if (ft_extattr->existence & UDF_FILETIMES_FILE_CREATION) + ctime = &ft_extattr->times[0]; + } + /* TODO create the extended attribute if not found ? */ + } else { + assert(udf_node->efe); + atime = &efe->atime; + mtime = &efe->mtime; + attrtime = &efe->attrtime; + ctime = &efe->ctime; + } + + vfs_timestamp(&now); + + /* set access time */ + if (udf_node->i_flags & IN_ACCESS) { + if (acc == NULL) + acc = &now; + udf_timespec_to_timestamp(acc, atime); + } + + /* set modification time */ + if (udf_node->i_flags & (IN_UPDATE | IN_MODIFY)) { + if (mod == NULL) + mod = &now; + udf_timespec_to_timestamp(mod, mtime); + + /* ensure birthtime is older than set modification! */ + udf_timestamp_to_timespec(udf_node->ump, ctime, &cur_birth); + if ((cur_birth.tv_sec > mod->tv_sec) || + ((cur_birth.tv_sec == mod->tv_sec) && + (cur_birth.tv_nsec > mod->tv_nsec))) { + udf_timespec_to_timestamp(mod, ctime); + } + } + + /* update birthtime if specified */ + /* XXX we assume here that given birthtime is older than mod */ + if (birth && (birth->tv_sec != VNOVAL)) { + udf_timespec_to_timestamp(birth, ctime); + } + + /* set change time */ + if (udf_node->i_flags & (IN_CHANGE | IN_MODIFY)) + udf_timespec_to_timestamp(&now, attrtime); + + /* notify updates to the node itself */ + if (udf_node->i_flags & (IN_ACCESS | IN_MODIFY)) + udf_node->i_flags |= IN_ACCESSED; + if (udf_node->i_flags & (IN_UPDATE | IN_CHANGE)) + udf_node->i_flags |= IN_MODIFIED; + + /* clear modification flags */ + udf_node->i_flags &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE | IN_MODIFY); +} + +/* --------------------------------------------------------------------- */ + +int +udf_update(struct vnode *vp, struct timespec *acc, + struct timespec *mod, struct timespec *birth, int updflags) +{ + union dscrptr *dscrptr; + struct udf_node *udf_node = VTOI(vp); + struct udf_mount *ump = udf_node->ump; + struct regid *impl_id; + int mnt_async = (vp->v_mount->mnt_flag & MNT_ASYNC); + int waitfor, flags; + +#ifdef DEBUG + char bits[128]; + DPRINTF(CALL, ("udf_update(node, %p, %p, %p, %d)\n", acc, mod, birth, + updflags)); + snprintb(bits, sizeof(bits), IN_FLAGBITS, udf_node->i_flags); + DPRINTF(CALL, ("\tnode flags %s\n", bits)); + DPRINTF(CALL, ("\t\tmnt_async = %d\n", mnt_async)); +#endif + + /* set our times */ + udf_itimes(udf_node, acc, mod, birth); + + /* set our implementation id */ + if (udf_node->fe) { + dscrptr = (union dscrptr *) udf_node->fe; + impl_id = &udf_node->fe->imp_id; + } else { + dscrptr = (union dscrptr *) udf_node->efe; + impl_id = &udf_node->efe->imp_id; + } + + /* set our ID */ + udf_set_regid(impl_id, IMPL_NAME); + udf_add_impl_regid(ump, impl_id); + + /* update our crc! on RMW we are not allowed to change a thing */ + udf_validate_tag_and_crc_sums(dscrptr); + + /* if called when mounted readonly, never write back */ + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return 0; + + /* check if the node is dirty 'enough'*/ + if (updflags & UPDATE_CLOSE) { + flags = udf_node->i_flags & (IN_MODIFIED | IN_ACCESSED); + } else { + flags = udf_node->i_flags & IN_MODIFIED; + } + if (flags == 0) + return 0; + + /* determine if we need to write sync or async */ + waitfor = 0; + if ((flags & IN_MODIFIED) && (mnt_async == 0)) { + /* sync mounted */ + waitfor = updflags & UPDATE_WAIT; + if (updflags & UPDATE_DIROP) + waitfor |= UPDATE_WAIT; + } + if (waitfor) + return VOP_FSYNC(vp, FSCRED, FSYNC_WAIT, 0,0); + + return 0; +} + + +/* --------------------------------------------------------------------- */ + + +/* + * Read one fid and process it into a dirent and advance to the next (*fid) + * has to be allocated a logical block in size, (*dirent) struct dirent length + */ + +int +udf_read_fid_stream(struct vnode *vp, uint64_t *offset, + struct fileid_desc *fid, struct dirent *dirent) +{ + struct udf_node *dir_node = VTOI(vp); + struct udf_mount *ump = dir_node->ump; + struct file_entry *fe = dir_node->fe; + struct extfile_entry *efe = dir_node->efe; + uint32_t fid_size, lb_size; + uint64_t file_size; + char *fid_name; + int enough, error; + + assert(fid); + assert(dirent); + assert(dir_node); + assert(offset); + assert(*offset != 1); + + DPRINTF(FIDS, ("read_fid_stream called at offset %"PRIu64"\n", *offset)); + /* check if we're past the end of the directory */ + if (fe) { + file_size = udf_rw64(fe->inf_len); + } else { + assert(dir_node->efe); + file_size = udf_rw64(efe->inf_len); + } + if (*offset >= file_size) + return EINVAL; + + /* get maximum length of FID descriptor */ + lb_size = udf_rw32(ump->logical_vol->lb_size); + + /* initialise return values */ + fid_size = 0; + memset(dirent, 0, sizeof(struct dirent)); + memset(fid, 0, lb_size); + + enough = (file_size - (*offset) >= UDF_FID_SIZE); + if (!enough) { + /* short dir ... */ + return EIO; + } + + error = vn_rdwr(UIO_READ, vp, + fid, MIN(file_size - (*offset), lb_size), *offset, + UIO_SYSSPACE, IO_ALTSEMANTICS | IO_NODELOCKED, FSCRED, + NULL, NULL); + if (error) + return error; + + DPRINTF(FIDS, ("\tfid piece read in fine\n")); + /* + * Check if we got a whole descriptor. + * TODO Try to `resync' directory stream when something is very wrong. + */ + + /* check if our FID header is OK */ + error = udf_check_tag(fid); + if (error) { + goto brokendir; + } + DPRINTF(FIDS, ("\ttag check ok\n")); + + if (udf_rw16(fid->tag.id) != TAGID_FID) { + error = EIO; + goto brokendir; + } + DPRINTF(FIDS, ("\ttag checked ok: got TAGID_FID\n")); + + /* check for length */ + fid_size = udf_fidsize(fid); + enough = (file_size - (*offset) >= fid_size); + if (!enough) { + error = EIO; + goto brokendir; + } + DPRINTF(FIDS, ("\tthe complete fid is read in\n")); + + /* check FID contents */ + error = udf_check_tag_payload((union dscrptr *) fid, lb_size); +brokendir: + if (error) { + /* note that is sometimes a bit quick to report */ + printf("UDF: BROKEN DIRECTORY ENTRY\n"); + /* RESYNC? */ + /* TODO: use udf_resync_fid_stream */ + return EIO; + } + DPRINTF(FIDS, ("\tpayload checked ok\n")); + + /* we got a whole and valid descriptor! */ + DPRINTF(FIDS, ("\tinterpret FID\n")); + + /* create resulting dirent structure */ + fid_name = (char *) fid->data + udf_rw16(fid->l_iu); + udf_to_unix_name(dirent->d_name, NAME_MAX, + fid_name, fid->l_fi, &ump->logical_vol->desc_charset); + + /* '..' has no name, so provide one */ + if (fid->file_char & UDF_FILE_CHAR_PAR) + strcpy(dirent->d_name, ".."); + + dirent->d_fileno = udf_get_node_id(&fid->icb); /* inode hash XXX */ + dirent->d_namlen = strlen(dirent->d_name); + dirent->d_reclen = _DIRENT_SIZE(dirent); + + /* + * Note that its not worth trying to go for the filetypes now... its + * too expensive too + */ + dirent->d_type = DT_UNKNOWN; + + /* initial guess for filetype we can make */ + if (fid->file_char & UDF_FILE_CHAR_DIR) + dirent->d_type = DT_DIR; + + /* advance */ + *offset += fid_size; + + return error; +} + + +/* --------------------------------------------------------------------- */ + +static void +udf_sync_pass(struct udf_mount *ump, kauth_cred_t cred, int waitfor, + int pass, int *ndirty) +{ + struct udf_node *udf_node, *n_udf_node; + struct vnode *vp; + int vdirty, error; + int on_type, on_flags, on_vnode; + +derailed: + KASSERT(mutex_owned(&mntvnode_lock)); + + DPRINTF(SYNC, ("sync_pass %d\n", pass)); + udf_node = RB_TREE_MIN(&ump->udf_node_tree); + for (;udf_node; udf_node = n_udf_node) { + DPRINTF(SYNC, (".")); + + udf_node->i_flags &= ~IN_SYNCED; + vp = udf_node->vnode; + + mutex_enter(vp->v_interlock); + n_udf_node = rb_tree_iterate(&ump->udf_node_tree, + udf_node, RB_DIR_RIGHT); + + if (n_udf_node) + n_udf_node->i_flags |= IN_SYNCED; + + /* system nodes are not synced this way */ + if (vp->v_vflag & VV_SYSTEM) { + mutex_exit(vp->v_interlock); + continue; + } + + /* check if its dirty enough to even try */ + on_type = (waitfor == MNT_LAZY || vp->v_type == VNON); + on_flags = ((udf_node->i_flags & + (IN_ACCESSED | IN_UPDATE | IN_MODIFIED)) == 0); + on_vnode = LIST_EMPTY(&vp->v_dirtyblkhd) + && UVM_OBJ_IS_CLEAN(&vp->v_uobj); + if (on_type || (on_flags || on_vnode)) { /* XXX */ + /* not dirty (enough?) */ + mutex_exit(vp->v_interlock); + continue; + } + + mutex_exit(&mntvnode_lock); + error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT); + if (error) { + mutex_enter(&mntvnode_lock); + if (error == ENOENT) + goto derailed; + *ndirty += 1; + continue; + } + + switch (pass) { + case 1: + VOP_FSYNC(vp, cred, 0 | FSYNC_DATAONLY,0,0); + break; + case 2: + vdirty = vp->v_numoutput; + if (vp->v_tag == VT_UDF) + vdirty += udf_node->outstanding_bufs + + udf_node->outstanding_nodedscr; + if (vdirty == 0) + VOP_FSYNC(vp, cred, 0,0,0); + *ndirty += vdirty; + break; + case 3: + vdirty = vp->v_numoutput; + if (vp->v_tag == VT_UDF) + vdirty += udf_node->outstanding_bufs + + udf_node->outstanding_nodedscr; + *ndirty += vdirty; + break; + } + + vput(vp); + mutex_enter(&mntvnode_lock); + } + DPRINTF(SYNC, ("END sync_pass %d\n", pass)); +} + + +void +udf_do_sync(struct udf_mount *ump, kauth_cred_t cred, int waitfor) +{ + int dummy, ndirty; + + mutex_enter(&mntvnode_lock); +recount: + dummy = 0; + DPRINTF(CALL, ("issue VOP_FSYNC(DATA only) on all nodes\n")); + DPRINTF(SYNC, ("issue VOP_FSYNC(DATA only) on all nodes\n")); + udf_sync_pass(ump, cred, waitfor, 1, &dummy); + + DPRINTF(CALL, ("issue VOP_FSYNC(COMPLETE) on all finished nodes\n")); + DPRINTF(SYNC, ("issue VOP_FSYNC(COMPLETE) on all finished nodes\n")); + udf_sync_pass(ump, cred, waitfor, 2, &dummy); + + if (waitfor == MNT_WAIT) { + ndirty = ump->devvp->v_numoutput; + DPRINTF(SYNC, ("counting pending blocks: on devvp %d\n", + ndirty)); + udf_sync_pass(ump, cred, waitfor, 3, &ndirty); + DPRINTF(SYNC, ("counted num dirty pending blocks %d\n", + ndirty)); + + if (ndirty) { + /* 1/4 second wait */ + cv_timedwait(&ump->dirtynodes_cv, &mntvnode_lock, + hz/4); + goto recount; + } + } + + mutex_exit(&mntvnode_lock); +} + +/* --------------------------------------------------------------------- */ + +/* + * Read and write file extent in/from the buffer. + * + * The splitup of the extent into seperate request-buffers is to minimise + * copying around as much as possible. + * + * block based file reading and writing + */ + +static int +udf_read_internal(struct udf_node *node, uint8_t *blob) +{ + struct udf_mount *ump; + struct file_entry *fe = node->fe; + struct extfile_entry *efe = node->efe; + uint64_t inflen; + uint32_t sector_size; + uint8_t *pos; + int icbflags, addr_type; + + /* get extent and do some paranoia checks */ + ump = node->ump; + sector_size = ump->discinfo.sector_size; + + if (fe) { + inflen = udf_rw64(fe->inf_len); + pos = &fe->data[0] + udf_rw32(fe->l_ea); + icbflags = udf_rw16(fe->icbtag.flags); + } else { + assert(node->efe); + inflen = udf_rw64(efe->inf_len); + pos = &efe->data[0] + udf_rw32(efe->l_ea); + icbflags = udf_rw16(efe->icbtag.flags); + } + addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK; + + assert(addr_type == UDF_ICB_INTERN_ALLOC); + assert(inflen < sector_size); + + /* copy out info */ + memset(blob, 0, sector_size); + memcpy(blob, pos, inflen); + + return 0; +} + + +static int +udf_write_internal(struct udf_node *node, uint8_t *blob) +{ + struct udf_mount *ump; + struct file_entry *fe = node->fe; + struct extfile_entry *efe = node->efe; + uint64_t inflen; + uint32_t sector_size; + uint8_t *pos; + int icbflags, addr_type; + + /* get extent and do some paranoia checks */ + ump = node->ump; + sector_size = ump->discinfo.sector_size; + + if (fe) { + inflen = udf_rw64(fe->inf_len); + pos = &fe->data[0] + udf_rw32(fe->l_ea); + icbflags = udf_rw16(fe->icbtag.flags); + } else { + assert(node->efe); + inflen = udf_rw64(efe->inf_len); + pos = &efe->data[0] + udf_rw32(efe->l_ea); + icbflags = udf_rw16(efe->icbtag.flags); + } + addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK; + + assert(addr_type == UDF_ICB_INTERN_ALLOC); + assert(inflen < sector_size); + + /* copy in blob */ + /* memset(pos, 0, inflen); */ + memcpy(pos, blob, inflen); + + return 0; +} + + +void +udf_read_filebuf(struct udf_node *udf_node, struct buf *buf) +{ + struct buf *nestbuf; + struct udf_mount *ump = udf_node->ump; + uint64_t *mapping; + uint64_t run_start; + uint32_t sector_size; + uint32_t buf_offset, sector, rbuflen, rblk; + uint32_t from, lblkno; + uint32_t sectors; + uint8_t *buf_pos; + int error, run_length, what; + + sector_size = udf_node->ump->discinfo.sector_size; + + from = buf->b_blkno; + sectors = buf->b_bcount / sector_size; + + what = udf_get_c_type(udf_node); + + /* assure we have enough translation slots */ + KASSERT(buf->b_bcount / sector_size <= UDF_MAX_MAPPINGS); + KASSERT(MAXPHYS / sector_size <= UDF_MAX_MAPPINGS); + + if (sectors > UDF_MAX_MAPPINGS) { + printf("udf_read_filebuf: implementation limit on bufsize\n"); + buf->b_error = EIO; + biodone(buf); + return; + } + + mapping = malloc(sizeof(*mapping) * UDF_MAX_MAPPINGS, M_TEMP, M_WAITOK); + + error = 0; + DPRINTF(READ, ("\ttranslate %d-%d\n", from, sectors)); + error = udf_translate_file_extent(udf_node, from, sectors, mapping); + if (error) { + buf->b_error = error; + biodone(buf); + goto out; + } + DPRINTF(READ, ("\ttranslate extent went OK\n")); + + /* pre-check if its an internal */ + if (*mapping == UDF_TRANS_INTERN) { + error = udf_read_internal(udf_node, (uint8_t *) buf->b_data); + if (error) + buf->b_error = error; + biodone(buf); + goto out; + } + DPRINTF(READ, ("\tnot intern\n")); + +#ifdef DEBUG + if (udf_verbose & UDF_DEBUG_TRANSLATE) { + printf("Returned translation table:\n"); + for (sector = 0; sector < sectors; sector++) { + printf("%d : %"PRIu64"\n", sector, mapping[sector]); + } + } +#endif + + /* request read-in of data from disc sheduler */ + buf->b_resid = buf->b_bcount; + for (sector = 0; sector < sectors; sector++) { + buf_offset = sector * sector_size; + buf_pos = (uint8_t *) buf->b_data + buf_offset; + DPRINTF(READ, ("\tprocessing rel sector %d\n", sector)); + + /* check if its zero or unmapped to stop reading */ + switch (mapping[sector]) { + case UDF_TRANS_UNMAPPED: + case UDF_TRANS_ZERO: + /* copy zero sector TODO runlength like below */ + memset(buf_pos, 0, sector_size); + DPRINTF(READ, ("\treturning zero sector\n")); + nestiobuf_done(buf, sector_size, 0); + break; + default : + DPRINTF(READ, ("\tread sector " + "%"PRIu64"\n", mapping[sector])); + + lblkno = from + sector; + run_start = mapping[sector]; + run_length = 1; + while (sector < sectors-1) { + if (mapping[sector+1] != mapping[sector]+1) + break; + run_length++; + sector++; + } + + /* + * nest an iobuf and mark it for async reading. Since + * we're using nested buffers, they can't be cached by + * design. + */ + rbuflen = run_length * sector_size; + rblk = run_start * (sector_size/DEV_BSIZE); + + nestbuf = getiobuf(NULL, true); + nestiobuf_setup(buf, nestbuf, buf_offset, rbuflen); + /* nestbuf is B_ASYNC */ + + /* identify this nestbuf */ + nestbuf->b_lblkno = lblkno; + assert(nestbuf->b_vp == udf_node->vnode); + + /* CD shedules on raw blkno */ + nestbuf->b_blkno = rblk; + nestbuf->b_proc = NULL; + nestbuf->b_rawblkno = rblk; + nestbuf->b_udf_c_type = what; + + udf_discstrat_queuebuf(ump, nestbuf); + } + } +out: + /* if we're synchronously reading, wait for the completion */ + if ((buf->b_flags & B_ASYNC) == 0) + biowait(buf); + + DPRINTF(READ, ("\tend of read_filebuf\n")); + free(mapping, M_TEMP); + return; +} + + +void +udf_write_filebuf(struct udf_node *udf_node, struct buf *buf) +{ + struct buf *nestbuf; + struct udf_mount *ump = udf_node->ump; + uint64_t *mapping; + uint64_t run_start; + uint32_t lb_size; + uint32_t buf_offset, lb_num, rbuflen, rblk; + uint32_t from, lblkno; + uint32_t num_lb; + int error, run_length, what, s; + + lb_size = udf_rw32(udf_node->ump->logical_vol->lb_size); + + from = buf->b_blkno; + num_lb = buf->b_bcount / lb_size; + + what = udf_get_c_type(udf_node); + + /* assure we have enough translation slots */ + KASSERT(buf->b_bcount / lb_size <= UDF_MAX_MAPPINGS); + KASSERT(MAXPHYS / lb_size <= UDF_MAX_MAPPINGS); + + if (num_lb > UDF_MAX_MAPPINGS) { + printf("udf_write_filebuf: implementation limit on bufsize\n"); + buf->b_error = EIO; + biodone(buf); + return; + } + + mapping = malloc(sizeof(*mapping) * UDF_MAX_MAPPINGS, M_TEMP, M_WAITOK); + + error = 0; + DPRINTF(WRITE, ("\ttranslate %d-%d\n", from, num_lb)); + error = udf_translate_file_extent(udf_node, from, num_lb, mapping); + if (error) { + buf->b_error = error; + biodone(buf); + goto out; + } + DPRINTF(WRITE, ("\ttranslate extent went OK\n")); + + /* if its internally mapped, we can write it in the descriptor itself */ + if (*mapping == UDF_TRANS_INTERN) { + /* TODO paranoia check if we ARE going to have enough space */ + error = udf_write_internal(udf_node, (uint8_t *) buf->b_data); + if (error) + buf->b_error = error; + biodone(buf); + goto out; + } + DPRINTF(WRITE, ("\tnot intern\n")); + + /* request write out of data to disc sheduler */ + buf->b_resid = buf->b_bcount; + for (lb_num = 0; lb_num < num_lb; lb_num++) { + buf_offset = lb_num * lb_size; + DPRINTF(WRITE, ("\tprocessing rel lb_num %d\n", lb_num)); + + /* + * Mappings are not that important here. Just before we write + * the lb_num we late-allocate them when needed and update the + * mapping in the udf_node. + */ + + /* XXX why not ignore the mapping altogether ? */ + DPRINTF(WRITE, ("\twrite lb_num " + "%"PRIu64, mapping[lb_num])); + + lblkno = from + lb_num; + run_start = mapping[lb_num]; + run_length = 1; + while (lb_num < num_lb-1) { + if (mapping[lb_num+1] != mapping[lb_num]+1) + if (mapping[lb_num+1] != mapping[lb_num]) + break; + run_length++; + lb_num++; + } + DPRINTF(WRITE, ("+ %d\n", run_length)); + + /* nest an iobuf on the master buffer for the extent */ + rbuflen = run_length * lb_size; + rblk = run_start * (lb_size/DEV_BSIZE); + + nestbuf = getiobuf(NULL, true); + nestiobuf_setup(buf, nestbuf, buf_offset, rbuflen); + /* nestbuf is B_ASYNC */ + + /* identify this nestbuf */ + nestbuf->b_lblkno = lblkno; + KASSERT(nestbuf->b_vp == udf_node->vnode); + + /* CD shedules on raw blkno */ + nestbuf->b_blkno = rblk; + nestbuf->b_proc = NULL; + nestbuf->b_rawblkno = rblk; + nestbuf->b_udf_c_type = what; + + /* increment our outstanding bufs counter */ + s = splbio(); + udf_node->outstanding_bufs++; + splx(s); + + udf_discstrat_queuebuf(ump, nestbuf); + } +out: + /* if we're synchronously writing, wait for the completion */ + if ((buf->b_flags & B_ASYNC) == 0) + biowait(buf); + + DPRINTF(WRITE, ("\tend of write_filebuf\n")); + free(mapping, M_TEMP); + return; +} + +/* --------------------------------------------------------------------- */ diff --git a/sys/fs/udf/udf_subr.h b/sys/fs/udf/udf_subr.h new file mode 100644 index 000000000..7146c1353 --- /dev/null +++ b/sys/fs/udf/udf_subr.h @@ -0,0 +1,229 @@ +/* $NetBSD: udf_subr.h,v 1.19 2013/07/07 19:49:44 reinoud Exp $ */ + +/* + * Copyright (c) 2006, 2008 Reinoud Zandijk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _FS_UDF_UDF_SUBR_H_ +#define _FS_UDF_UDF_SUBR_H_ + +/* handies */ +#define VFSTOUDF(mp) ((struct udf_mount *)mp->mnt_data) + + +/* device information updating */ +int udf_update_trackinfo(struct udf_mount *ump, struct mmc_trackinfo *trackinfo); +int udf_update_discinfo(struct udf_mount *ump); +int udf_search_tracks(struct udf_mount *ump, struct udf_args *args, + int *first_tracknr, int *last_tracknr); +int udf_search_writing_tracks(struct udf_mount *ump); +int udf_setup_writeparams(struct udf_mount *ump); +int udf_synchronise_caches(struct udf_mount *ump); + +/* tags operations */ +int udf_fidsize(struct fileid_desc *fid); +int udf_check_tag(void *blob); +int udf_check_tag_payload(void *blob, uint32_t max_length); +void udf_validate_tag_sum(void *blob); +void udf_validate_tag_and_crc_sums(void *blob); +int udf_tagsize(union dscrptr *dscr, uint32_t udf_sector_size); + +/* read/write descriptors */ +int udf_read_phys_sectors(struct udf_mount *ump, int what, void *blob, + uint32_t start, uint32_t sectors); +int udf_write_phys_sectors(struct udf_mount *ump, int what, void *blob, + uint32_t start, uint32_t sectors); +int udf_read_phys_dscr( + struct udf_mount *ump, + uint32_t sector, + struct malloc_type *mtype, /* where to allocate */ + union dscrptr **dstp); /* out */ + +int udf_write_phys_dscr_sync(struct udf_mount *ump, struct udf_node *udf_node, + int what, union dscrptr *dscr, + uint32_t sector, uint32_t logsector); +int udf_write_phys_dscr_async(struct udf_mount *ump, struct udf_node *udf_node, + int what, union dscrptr *dscr, + uint32_t sector, uint32_t logsector, + void (*dscrwr_callback)(struct buf *)); + +/* read/write node descriptors */ +int udf_create_logvol_dscr(struct udf_mount *ump, struct udf_node *udf_node, + struct long_ad *icb, union dscrptr **dscrptr); +void udf_free_logvol_dscr(struct udf_mount *ump, struct long_ad *icb_loc, + void *dscr); +int udf_read_logvol_dscr(struct udf_mount *ump, struct long_ad *icb, + union dscrptr **dscrptr); +int udf_write_logvol_dscr(struct udf_node *udf_node, union dscrptr *dscr, + struct long_ad *icb, int waitfor); + + +/* volume descriptors readers and checkers */ +int udf_read_anchors(struct udf_mount *ump); +int udf_read_vds_space(struct udf_mount *ump); +int udf_process_vds(struct udf_mount *ump); +int udf_read_vds_tables(struct udf_mount *ump); +int udf_read_rootdirs(struct udf_mount *ump); + +/* open/close and sync volumes */ +int udf_open_logvol(struct udf_mount *ump); +int udf_close_logvol(struct udf_mount *ump, int mntflags); +int udf_writeout_vat(struct udf_mount *ump); +int udf_write_physical_partition_spacetables(struct udf_mount *ump, int waitfor); +int udf_write_metadata_partition_spacetable(struct udf_mount *ump, int waitfor); +void udf_do_sync(struct udf_mount *ump, kauth_cred_t cred, int waitfor); +void udf_synchronise_metadatamirror_node(struct udf_mount *ump); + +/* translation services */ +int udf_translate_vtop(struct udf_mount *ump, struct long_ad *icb_loc, + uint32_t *lb_numres, uint32_t *extres); +void udf_translate_vtop_list(struct udf_mount *ump, uint32_t sectors, + uint16_t vpart_num, uint64_t *lmapping, uint64_t *pmapping); +int udf_translate_file_extent(struct udf_node *node, + uint32_t from, uint32_t num_lb, uint64_t *map); +void udf_get_adslot(struct udf_node *udf_node, int slot, struct long_ad *icb, int *eof); +int udf_append_adslot(struct udf_node *udf_node, int *slot, struct long_ad *icb); + +int udf_vat_read(struct udf_node *vat_node, uint8_t *blob, int size, uint32_t offset); +int udf_vat_write(struct udf_node *vat_node, uint8_t *blob, int size, uint32_t offset); + +/* disc allocation */ +int udf_get_c_type(struct udf_node *udf_node); +int udf_get_record_vpart(struct udf_mount *ump, int udf_c_type); +void udf_do_reserve_space(struct udf_mount *ump, struct udf_node *udf_node, uint16_t vpart_num, uint32_t num_lb); +void udf_do_unreserve_space(struct udf_mount *ump, struct udf_node *udf_node, uint16_t vpart_num, uint32_t num_lb); +int udf_reserve_space(struct udf_mount *ump, struct udf_node *udf_node, int udf_c_type, uint16_t vpart_num, uint32_t num_lb, int can_fail); +void udf_cleanup_reservation(struct udf_node *udf_node); +int udf_allocate_space(struct udf_mount *ump, struct udf_node *udf_node, int udf_c_type, uint16_t vpart_num, uint32_t num_lb, uint64_t *lmapping); +void udf_free_allocated_space(struct udf_mount *ump, uint32_t lb_num, uint16_t vpart_num, uint32_t num_lb); +void udf_late_allocate_buf(struct udf_mount *ump, struct buf *buf, uint64_t *lmapping, struct long_ad *node_ad_cpy, uint16_t *vpart_num); +int udf_grow_node(struct udf_node *node, uint64_t new_size); +int udf_shrink_node(struct udf_node *node, uint64_t new_size); +void udf_calc_freespace(struct udf_mount *ump, uint64_t *sizeblks, uint64_t *freeblks); + +/* node readers and writers */ +uint64_t udf_advance_uniqueid(struct udf_mount *ump); + +#define UDF_LOCK_NODE(udf_node, flag) udf_lock_node(udf_node, (flag), __FILE__, __LINE__) +#define UDF_UNLOCK_NODE(udf_node, flag) udf_unlock_node(udf_node, (flag)) +void udf_lock_node(struct udf_node *udf_node, int flag, char const *fname, const int lineno); +void udf_unlock_node(struct udf_node *udf_node, int flag); + +int udf_get_node(struct udf_mount *ump, struct long_ad *icbloc, struct udf_node **noderes); +int udf_writeout_node(struct udf_node *udf_node, int waitfor); +int udf_dispose_node(struct udf_node *node); + +/* node ops */ +int udf_resize_node(struct udf_node *node, uint64_t new_size, int *extended); +int udf_extattr_search_intern(struct udf_node *node, uint32_t sattr, char const *sattrname, uint32_t *offsetp, uint32_t *lengthp); + +/* node data buffer read/write */ +void udf_read_filebuf(struct udf_node *node, struct buf *buf); +void udf_write_filebuf(struct udf_node *node, struct buf *buf); +void udf_fixup_fid_block(uint8_t *blob, int lb_size, int rfix_pos, int max_rfix_pos, uint32_t lb_num); +void udf_fixup_internal_extattr(uint8_t *blob, uint32_t lb_num); +void udf_fixup_node_internals(struct udf_mount *ump, uint8_t *blob, int udf_c_type); + +/* device strategy */ +void udf_discstrat_init(struct udf_mount *ump); +void udf_discstrat_finish(struct udf_mount *ump); +void udf_discstrat_queuebuf(struct udf_mount *ump, struct buf *nestbuf); + +/* structure writers */ +int udf_write_terminator(struct udf_mount *ump, uint32_t sector); + +/* structure creators */ +void udf_inittag(struct udf_mount *ump, struct desc_tag *tag, int tagid, uint32_t sector); +void udf_set_regid(struct regid *regid, char const *name); +void udf_add_domain_regid(struct udf_mount *ump, struct regid *regid); +void udf_add_udf_regid(struct udf_mount *ump, struct regid *regid); +void udf_add_impl_regid(struct udf_mount *ump, struct regid *regid); +void udf_add_app_regid(struct udf_mount *ump, struct regid *regid); + +/* directory operations and helpers */ +void udf_osta_charset(struct charspec *charspec); +int udf_read_fid_stream(struct vnode *vp, uint64_t *offset, struct fileid_desc *fid, struct dirent *dirent); +int udf_lookup_name_in_dir(struct vnode *vp, const char *name, int namelen, struct long_ad *icb_loc, int *found); +int udf_create_node(struct vnode *dvp, struct vnode **vpp, struct vattr *vap, struct componentname *cnp); +void udf_delete_node(struct udf_node *udf_node); + +int udf_chsize(struct vnode *vp, u_quad_t newsize, kauth_cred_t cred); + +int udf_dir_detach(struct udf_mount *ump, struct udf_node *dir_node, struct udf_node *udf_node, struct componentname *cnp); +int udf_dir_attach(struct udf_mount *ump, struct udf_node *dir_node, struct udf_node *udf_node, struct vattr *vap, struct componentname *cnp); +int udf_dir_update_rootentry(struct udf_mount *ump, struct udf_node *dir_node, struct udf_node *new_parent_node); +int udf_dirhash_fill(struct udf_node *dir_node); + +/* update and times */ +void udf_add_to_dirtylist(struct udf_node *udf_node); +void udf_remove_from_dirtylist(struct udf_node *udf_node); +void udf_itimes(struct udf_node *udf_node, struct timespec *acc, + struct timespec *mod, struct timespec *birth); +int udf_update(struct vnode *node, struct timespec *acc, + struct timespec *mod, struct timespec *birth, int updflags); + +/* helpers and converters */ +void udf_init_nodes_tree(struct udf_mount *ump); +long udf_get_node_id(const struct long_ad *icbptr); /* for `inode' numbering */ +int udf_compare_icb(const struct long_ad *a, const struct long_ad *b); +uint32_t udf_getaccessmode(struct udf_node *node); +void udf_setaccessmode(struct udf_node *udf_node, mode_t mode); +void udf_getownership(struct udf_node *udf_node, uid_t *uidp, gid_t *gidp); +void udf_setownership(struct udf_node *udf_node, uid_t uid, gid_t gid); + +void udf_to_unix_name(char *result, int result_len, char *id, int len, struct charspec *chsp); +void unix_to_udf_name(char *result, uint8_t *result_len, char const *name, int name_len, struct charspec *chsp); + +void udf_timestamp_to_timespec(struct udf_mount *ump, struct timestamp *timestamp, struct timespec *timespec); +void udf_timespec_to_timestamp(struct timespec *timespec, struct timestamp *timestamp); + +/* vnode operations */ +int udf_inactive(void *v); +int udf_reclaim(void *v); +int udf_readdir(void *v); +int udf_getattr(void *v); +int udf_setattr(void *v); +int udf_pathconf(void *v); +int udf_open(void *v); +int udf_close(void *v); +int udf_access(void *v); +int udf_read(void *v); +int udf_write(void *v); +int udf_trivial_bmap(void *v); +int udf_vfsstrategy(void *v); +int udf_lookup(void *v); +int udf_create(void *v); +int udf_mknod(void *v); +int udf_link(void *); +int udf_symlink(void *v); +int udf_readlink(void *v); +int udf_rename(void *v); +int udf_remove(void *v); +int udf_mkdir(void *v); +int udf_rmdir(void *v); +int udf_fsync(void *v); +int udf_advlock(void *v); + +#endif /* !_FS_UDF_UDF_SUBR_H_ */ diff --git a/sys/fs/udf/udf_vfsops.c b/sys/fs/udf/udf_vfsops.c new file mode 100644 index 000000000..7fb4467af --- /dev/null +++ b/sys/fs/udf/udf_vfsops.c @@ -0,0 +1,949 @@ +/* $NetBSD: udf_vfsops.c,v 1.64 2013/09/30 18:58:00 hannken Exp $ */ + +/* + * Copyright (c) 2006, 2008 Reinoud Zandijk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#ifndef lint +__KERNEL_RCSID(0, "$NetBSD: udf_vfsops.c,v 1.64 2013/09/30 18:58:00 hannken Exp $"); +#endif /* not lint */ + + +#if defined(_KERNEL_OPT) +#include "opt_compat_netbsd.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "udf.h" +#include "udf_subr.h" +#include "udf_bswap.h" + +MODULE(MODULE_CLASS_VFS, udf, NULL); + +#define VTOI(vnode) ((struct udf_node *) vnode->v_data) + +/* verbose levels of the udf filingsystem */ +int udf_verbose = UDF_DEBUGGING; + +/* malloc regions */ +MALLOC_JUSTDEFINE(M_UDFMNT, "UDF mount", "UDF mount structures"); +MALLOC_JUSTDEFINE(M_UDFVOLD, "UDF volspace", "UDF volume space descriptors"); +MALLOC_JUSTDEFINE(M_UDFTEMP, "UDF temp", "UDF scrap space"); +struct pool udf_node_pool; + +/* supported functions predefined */ +VFS_PROTOS(udf); + +static struct sysctllog *udf_sysctl_log; + +/* internal functions */ +static int udf_mountfs(struct vnode *, struct mount *, struct lwp *, struct udf_args *); + + +/* --------------------------------------------------------------------- */ + +/* predefine vnode-op list descriptor */ +extern const struct vnodeopv_desc udf_vnodeop_opv_desc; + +const struct vnodeopv_desc * const udf_vnodeopv_descs[] = { + &udf_vnodeop_opv_desc, + NULL, +}; + + +/* vfsops descriptor linked in as anchor point for the filingsystem */ +struct vfsops udf_vfsops = { + MOUNT_UDF, /* vfs_name */ + sizeof (struct udf_args), + udf_mount, + udf_start, + udf_unmount, + udf_root, + (void *)eopnotsupp, /* vfs_quotactl */ + udf_statvfs, + udf_sync, + udf_vget, + udf_fhtovp, + udf_vptofh, + udf_init, + udf_reinit, + udf_done, + udf_mountroot, + udf_snapshot, + vfs_stdextattrctl, + (void *)eopnotsupp, /* vfs_suspendctl */ + genfs_renamelock_enter, + genfs_renamelock_exit, + (void *)eopnotsupp, + udf_vnodeopv_descs, + 0, /* int vfs_refcount */ + { NULL, NULL, }, /* LIST_ENTRY(vfsops) */ +}; + +/* --------------------------------------------------------------------- */ + +/* file system starts here */ +void +udf_init(void) +{ + size_t size; + + /* setup memory types */ + malloc_type_attach(M_UDFMNT); + malloc_type_attach(M_UDFVOLD); + malloc_type_attach(M_UDFTEMP); + + /* init node pools */ + size = sizeof(struct udf_node); + pool_init(&udf_node_pool, size, 0, 0, 0, + "udf_node_pool", NULL, IPL_NONE); +} + + +void +udf_reinit(void) +{ + /* nothing to do */ +} + + +void +udf_done(void) +{ + /* remove pools */ + pool_destroy(&udf_node_pool); + + malloc_type_detach(M_UDFMNT); + malloc_type_detach(M_UDFVOLD); + malloc_type_detach(M_UDFTEMP); +} + +/* + * If running a DEBUG kernel, provide an easy way to set the debug flags when + * running into a problem. + */ +#define UDF_VERBOSE_SYSCTLOPT 1 + +static int +udf_modcmd(modcmd_t cmd, void *arg) +{ + const struct sysctlnode *node; + int error; + + switch (cmd) { + case MODULE_CMD_INIT: + error = vfs_attach(&udf_vfsops); + if (error != 0) + break; + /* + * XXX the "24" below could be dynamic, thereby eliminating one + * more instance of the "number to vfs" mapping problem, but + * "24" is the order as taken from sys/mount.h + */ + sysctl_createv(&udf_sysctl_log, 0, NULL, NULL, + CTLFLAG_PERMANENT, + CTLTYPE_NODE, "vfs", NULL, + NULL, 0, NULL, 0, + CTL_VFS, CTL_EOL); + sysctl_createv(&udf_sysctl_log, 0, NULL, &node, + CTLFLAG_PERMANENT, + CTLTYPE_NODE, "udf", + SYSCTL_DESCR("OSTA Universal File System"), + NULL, 0, NULL, 0, + CTL_VFS, 24, CTL_EOL); +#ifdef DEBUG + sysctl_createv(&udf_sysctl_log, 0, NULL, &node, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "verbose", + SYSCTL_DESCR("Bitmask for filesystem debugging"), + NULL, 0, &udf_verbose, 0, + CTL_VFS, 24, UDF_VERBOSE_SYSCTLOPT, CTL_EOL); +#endif + break; + case MODULE_CMD_FINI: + error = vfs_detach(&udf_vfsops); + if (error != 0) + break; + sysctl_teardown(&udf_sysctl_log); + break; + default: + error = ENOTTY; + break; + } + + return (error); +} + +/* --------------------------------------------------------------------- */ + +int +udf_mountroot(void) +{ + return EOPNOTSUPP; +} + +/* --------------------------------------------------------------------- */ + +#define MPFREE(a, lst) \ + if ((a)) free((a), lst); +static void +free_udf_mountinfo(struct mount *mp) +{ + struct udf_mount *ump; + int i; + + if (!mp) + return; + + ump = VFSTOUDF(mp); + if (ump) { + /* clear our data */ + for (i = 0; i < UDF_ANCHORS; i++) + MPFREE(ump->anchors[i], M_UDFVOLD); + MPFREE(ump->primary_vol, M_UDFVOLD); + MPFREE(ump->logical_vol, M_UDFVOLD); + MPFREE(ump->unallocated, M_UDFVOLD); + MPFREE(ump->implementation, M_UDFVOLD); + MPFREE(ump->logvol_integrity, M_UDFVOLD); + for (i = 0; i < UDF_PARTITIONS; i++) { + MPFREE(ump->partitions[i], M_UDFVOLD); + MPFREE(ump->part_unalloc_dscr[i], M_UDFVOLD); + MPFREE(ump->part_freed_dscr[i], M_UDFVOLD); + } + MPFREE(ump->metadata_unalloc_dscr, M_UDFVOLD); + + MPFREE(ump->fileset_desc, M_UDFVOLD); + MPFREE(ump->sparing_table, M_UDFVOLD); + + MPFREE(ump->la_node_ad_cpy, M_UDFMNT); + MPFREE(ump->la_pmapping, M_TEMP); + MPFREE(ump->la_lmapping, M_TEMP); + + mutex_destroy(&ump->ihash_lock); + mutex_destroy(&ump->get_node_lock); + mutex_destroy(&ump->logvol_mutex); + mutex_destroy(&ump->allocate_mutex); + cv_destroy(&ump->dirtynodes_cv); + + MPFREE(ump->vat_table, M_UDFVOLD); + + free(ump, M_UDFMNT); + } +} +#undef MPFREE + +/* --------------------------------------------------------------------- */ + +/* if the system nodes exist, release them */ +static void +udf_release_system_nodes(struct mount *mp) +{ + struct udf_mount *ump = VFSTOUDF(mp); + int error; + + /* if we haven't even got an ump, dont bother */ + if (!ump) + return; + + /* VAT partition support */ + if (ump->vat_node) + vrele(ump->vat_node->vnode); + + /* Metadata partition support */ + if (ump->metadata_node) + vrele(ump->metadata_node->vnode); + if (ump->metadatamirror_node) + vrele(ump->metadatamirror_node->vnode); + if (ump->metadatabitmap_node) + vrele(ump->metadatabitmap_node->vnode); + + /* This flush should NOT write anything nor allow any node to remain */ + if ((error = vflush(ump->vfs_mountp, NULLVP, 0)) != 0) + panic("Failure to flush UDF system vnodes\n"); +} + + +int +udf_mount(struct mount *mp, const char *path, + void *data, size_t *data_len) +{ + struct lwp *l = curlwp; + struct udf_args *args = data; + struct udf_mount *ump; + struct vnode *devvp; + int openflags, accessmode, error; + + DPRINTF(CALL, ("udf_mount called\n")); + + if (*data_len < sizeof *args) + return EINVAL; + + if (mp->mnt_flag & MNT_GETARGS) { + /* request for the mount arguments */ + ump = VFSTOUDF(mp); + if (ump == NULL) + return EINVAL; + *args = ump->mount_args; + *data_len = sizeof *args; + return 0; + } + + /* handle request for updating mount parameters */ + /* TODO can't update my mountpoint yet */ + if (mp->mnt_flag & MNT_UPDATE) { + return EOPNOTSUPP; + } + + /* OK, so we are asked to mount the device */ + + /* check/translate struct version */ + /* TODO sanity checking other mount arguments */ + if (args->version != 1) { + printf("mount_udf: unrecognized argument structure version\n"); + return EINVAL; + } + + /* lookup name to get its vnode */ + error = namei_simple_user(args->fspec, + NSM_FOLLOW_NOEMULROOT, &devvp); + if (error) + return error; + +#ifdef DEBUG + if (udf_verbose & UDF_DEBUG_VOLUMES) + vprint("UDF mount, trying to mount \n", devvp); +#endif + + /* check if its a block device specified */ + if (devvp->v_type != VBLK) { + vrele(devvp); + return ENOTBLK; + } + if (bdevsw_lookup(devvp->v_rdev) == NULL) { + vrele(devvp); + return ENXIO; + } + + /* + * If mount by non-root, then verify that user has necessary + * permissions on the device. + */ + accessmode = VREAD; + if ((mp->mnt_flag & MNT_RDONLY) == 0) + accessmode |= VWRITE; + vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); + error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_MOUNT, + KAUTH_REQ_SYSTEM_MOUNT_DEVICE, mp, devvp, KAUTH_ARG(accessmode)); + VOP_UNLOCK(devvp); + if (error) { + vrele(devvp); + return error; + } + + /* + * Open device and try to mount it! + */ + if (mp->mnt_flag & MNT_RDONLY) { + openflags = FREAD; + } else { + openflags = FREAD | FWRITE; + } + vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); + error = VOP_OPEN(devvp, openflags, FSCRED); + VOP_UNLOCK(devvp); + if (error == 0) { + /* opened ok, try mounting */ + error = udf_mountfs(devvp, mp, l, args); + if (error) { + udf_release_system_nodes(mp); + /* cleanup */ + udf_discstrat_finish(VFSTOUDF(mp)); + free_udf_mountinfo(mp); + vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); + (void) VOP_CLOSE(devvp, openflags, NOCRED); + VOP_UNLOCK(devvp); + } + } + if (error) { + /* devvp is still locked */ + vrele(devvp); + return error; + } + + /* register our mountpoint being on this device */ + spec_node_setmountedfs(devvp, mp); + + /* successfully mounted */ + DPRINTF(VOLUMES, ("udf_mount() successfull\n")); + + error = set_statvfs_info(path, UIO_USERSPACE, args->fspec, UIO_USERSPACE, + mp->mnt_op->vfs_name, mp, l); + if (error) + return error; + + /* If we're not opened read-only, open its logical volume */ + if ((mp->mnt_flag & MNT_RDONLY) == 0) { + if ((error = udf_open_logvol(VFSTOUDF(mp))) != 0) { + printf( "mount_udf: can't open logical volume for " + "writing, downgrading access to read-only\n"); + mp->mnt_flag |= MNT_RDONLY; + /* FIXME we can't return error now on open failure */ + return 0; + } + } + + return 0; +} + +/* --------------------------------------------------------------------- */ + +#ifdef DEBUG +static void +udf_unmount_sanity_check(struct mount *mp) +{ + struct vnode *vp; + + printf("On unmount, i found the following nodes:\n"); + TAILQ_FOREACH(vp, &mp->mnt_vnodelist, v_mntvnodes) { + vprint("", vp); + if (VOP_ISLOCKED(vp) == LK_EXCLUSIVE) { + printf(" is locked\n"); + } + if (vp->v_usecount > 1) + printf(" more than one usecount %d\n", vp->v_usecount); + } +} +#endif + + +int +udf_unmount(struct mount *mp, int mntflags) +{ + struct udf_mount *ump; + int error, flags, closeflags; + + DPRINTF(CALL, ("udf_umount called\n")); + + ump = VFSTOUDF(mp); + if (!ump) + panic("UDF unmount: empty ump\n"); + + flags = (mntflags & MNT_FORCE) ? FORCECLOSE : 0; + /* TODO remove these paranoid functions */ +#ifdef DEBUG + if (udf_verbose & UDF_DEBUG_LOCKING) + udf_unmount_sanity_check(mp); +#endif + + /* + * By specifying SKIPSYSTEM we can skip vnodes marked with VV_SYSTEM. + * This hardly documented feature allows us to exempt certain files + * from being flushed. + */ + if ((error = vflush(mp, NULLVP, flags | SKIPSYSTEM)) != 0) + return error; + + /* update nodes and wait for completion of writeout of system nodes */ + udf_sync(mp, FSYNC_WAIT, NOCRED); + +#ifdef DEBUG + if (udf_verbose & UDF_DEBUG_LOCKING) + udf_unmount_sanity_check(mp); +#endif + + /* flush again, to check if we are still busy for something else */ + if ((error = vflush(ump->vfs_mountp, NULLVP, flags | SKIPSYSTEM)) != 0) + return error; + + DPRINTF(VOLUMES, ("flush OK on unmount\n")); + + /* close logical volume and close session if requested */ + if ((error = udf_close_logvol(ump, mntflags)) != 0) + return error; + +#ifdef DEBUG + DPRINTF(VOLUMES, ("FINAL sanity check\n")); + if (udf_verbose & UDF_DEBUG_LOCKING) + udf_unmount_sanity_check(mp); +#endif + + /* NOTE release system nodes should NOT write anything */ + udf_release_system_nodes(mp); + + /* finalise disc strategy */ + udf_discstrat_finish(ump); + + /* synchronise device caches */ + (void) udf_synchronise_caches(ump); + + /* close device */ + DPRINTF(VOLUMES, ("closing device\n")); + if (mp->mnt_flag & MNT_RDONLY) { + closeflags = FREAD; + } else { + closeflags = FREAD | FWRITE; + } + + /* devvp is still locked by us */ + vn_lock(ump->devvp, LK_EXCLUSIVE | LK_RETRY); + error = VOP_CLOSE(ump->devvp, closeflags, NOCRED); + if (error) + printf("Error during closure of device! error %d, " + "device might stay locked\n", error); + DPRINTF(VOLUMES, ("device close ok\n")); + + /* clear our mount reference and release device node */ + spec_node_setmountedfs(ump->devvp, NULL); + vput(ump->devvp); + + /* free our ump */ + free_udf_mountinfo(mp); + + /* free ump struct references */ + mp->mnt_data = NULL; + mp->mnt_flag &= ~MNT_LOCAL; + + DPRINTF(VOLUMES, ("Fin unmount\n")); + return error; +} + +/* --------------------------------------------------------------------- */ + +/* + * Helper function of udf_mount() that actually mounts the disc. + */ + +static int +udf_mountfs(struct vnode *devvp, struct mount *mp, + struct lwp *l, struct udf_args *args) +{ + struct udf_mount *ump; + uint32_t sector_size, lb_size, bshift; + uint32_t logvol_integrity; + int num_anchors, error; + + /* flush out any old buffers remaining from a previous use. */ + if ((error = vinvalbuf(devvp, V_SAVE, l->l_cred, l, 0, 0))) + return error; + + /* setup basic mount information */ + mp->mnt_data = NULL; + mp->mnt_stat.f_fsidx.__fsid_val[0] = (uint32_t) devvp->v_rdev; + mp->mnt_stat.f_fsidx.__fsid_val[1] = makefstype(MOUNT_UDF); + mp->mnt_stat.f_fsid = mp->mnt_stat.f_fsidx.__fsid_val[0]; + mp->mnt_stat.f_namemax = UDF_MAXNAMLEN; + mp->mnt_flag |= MNT_LOCAL; +// mp->mnt_iflag |= IMNT_MPSAFE; + + /* allocate udf part of mount structure; malloc always succeeds */ + ump = malloc(sizeof(struct udf_mount), M_UDFMNT, M_WAITOK | M_ZERO); + + /* init locks */ + mutex_init(&ump->logvol_mutex, MUTEX_DEFAULT, IPL_NONE); + mutex_init(&ump->ihash_lock, MUTEX_DEFAULT, IPL_NONE); + mutex_init(&ump->get_node_lock, MUTEX_DEFAULT, IPL_NONE); + mutex_init(&ump->allocate_mutex, MUTEX_DEFAULT, IPL_NONE); + cv_init(&ump->dirtynodes_cv, "udfsync2"); + + /* init rbtree for nodes, ordered by their icb address (long_ad) */ + udf_init_nodes_tree(ump); + + /* set up linkage */ + mp->mnt_data = ump; + ump->vfs_mountp = mp; + + /* set up arguments and device */ + ump->mount_args = *args; + ump->devvp = devvp; + if ((error = udf_update_discinfo(ump))) { + printf("UDF mount: error inspecting fs node\n"); + return error; + } + + /* inspect sector size */ + sector_size = ump->discinfo.sector_size; + bshift = 1; + while ((1 << bshift) < sector_size) + bshift++; + if ((1 << bshift) != sector_size) { + printf("UDF mount: " + "hit NetBSD implementation fence on sector size\n"); + return EIO; + } + + /* temporary check to overcome sectorsize >= 8192 bytes panic */ + if (sector_size >= 8192) { + printf("UDF mount: " + "hit implementation limit, sectorsize to big\n"); + return EIO; + } + + /* + * Inspect if we're asked to mount read-write on a non recordable or + * closed sequential disc. + */ + if ((mp->mnt_flag & MNT_RDONLY) == 0) { + if ((ump->discinfo.mmc_cur & MMC_CAP_RECORDABLE) == 0) { + printf("UDF mount: disc is not recordable\n"); + return EROFS; + } + if (ump->discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) { + if (ump->discinfo.disc_state == MMC_STATE_FULL) { + printf("UDF mount: disc is not appendable\n"); + return EROFS; + } + + /* + * TODO if the last session is closed check if there + * is enough space to open/close new session + */ + } + /* double check if we're not mounting a pervious session RW */ + if (args->sessionnr != 0) { + printf("UDF mount: updating a previous session " + "not yet allowed\n"); + return EROFS; + } + } + + /* initialise bootstrap disc strategy */ + ump->strategy = &udf_strat_bootstrap; + udf_discstrat_init(ump); + + /* read all anchors to get volume descriptor sequence */ + num_anchors = udf_read_anchors(ump); + if (num_anchors == 0) + return EINVAL; + + DPRINTF(VOLUMES, ("Read %d anchors on this disc, session %d\n", + num_anchors, args->sessionnr)); + + /* read in volume descriptor sequence */ + if ((error = udf_read_vds_space(ump))) { + printf("UDF mount: error reading volume space\n"); + return error; + } + + /* close down bootstrap disc strategy */ + udf_discstrat_finish(ump); + + /* check consistency and completeness */ + if ((error = udf_process_vds(ump))) { + printf( "UDF mount: disc not properly formatted" + "(bad VDS)\n"); + return error; + } + + /* switch to new disc strategy */ + KASSERT(ump->strategy != &udf_strat_bootstrap); + udf_discstrat_init(ump); + + /* initialise late allocation administration space */ + ump->la_lmapping = malloc(sizeof(uint64_t) * UDF_MAX_MAPPINGS, + M_TEMP, M_WAITOK); + ump->la_pmapping = malloc(sizeof(uint64_t) * UDF_MAX_MAPPINGS, + M_TEMP, M_WAITOK); + + /* setup node cleanup extents copy space */ + lb_size = udf_rw32(ump->logical_vol->lb_size); + ump->la_node_ad_cpy = malloc(lb_size * UDF_MAX_ALLOC_EXTENTS, + M_UDFMNT, M_WAITOK); + memset(ump->la_node_ad_cpy, 0, lb_size * UDF_MAX_ALLOC_EXTENTS); + + /* setup rest of mount information */ + mp->mnt_data = ump; + + /* bshift is allways equal to disc sector size */ + mp->mnt_dev_bshift = bshift; + mp->mnt_fs_bshift = bshift; + + /* note that the mp info needs to be initialised for reading! */ + /* read vds support tables like VAT, sparable etc. */ + if ((error = udf_read_vds_tables(ump))) { + printf( "UDF mount: error in format or damaged disc " + "(VDS tables failing)\n"); + return error; + } + + /* check if volume integrity is closed otherwise its dirty */ + logvol_integrity = udf_rw32(ump->logvol_integrity->integrity_type); + if (logvol_integrity != UDF_INTEGRITY_CLOSED) { + printf("UDF mount: file system is not clean; "); + printf("please fsck(8)\n"); + return EPERM; + } + + /* read root directory */ + if ((error = udf_read_rootdirs(ump))) { + printf( "UDF mount: " + "disc not properly formatted or damaged disc " + "(rootdirs failing)\n"); + return error; + } + + /* success! */ + return 0; +} + +/* --------------------------------------------------------------------- */ + +int +udf_start(struct mount *mp, int flags) +{ + /* do we have to do something here? */ + return 0; +} + +/* --------------------------------------------------------------------- */ + +int +udf_root(struct mount *mp, struct vnode **vpp) +{ + struct vnode *vp; + struct long_ad *dir_loc; + struct udf_mount *ump = VFSTOUDF(mp); + struct udf_node *root_dir; + int error; + + DPRINTF(CALL, ("udf_root called\n")); + + dir_loc = &ump->fileset_desc->rootdir_icb; + error = udf_get_node(ump, dir_loc, &root_dir); + + if (!root_dir) + error = ENOENT; + if (error) + return error; + + vp = root_dir->vnode; + KASSERT(vp->v_vflag & VV_ROOT); + + *vpp = vp; + return 0; +} + +/* --------------------------------------------------------------------- */ + +int +udf_statvfs(struct mount *mp, struct statvfs *sbp) +{ + struct udf_mount *ump = VFSTOUDF(mp); + struct logvol_int_desc *lvid; + struct udf_logvol_info *impl; + uint64_t freeblks, sizeblks; + int num_part; + + DPRINTF(CALL, ("udf_statvfs called\n")); + sbp->f_flag = mp->mnt_flag; + sbp->f_bsize = ump->discinfo.sector_size; + sbp->f_frsize = ump->discinfo.sector_size; + sbp->f_iosize = ump->discinfo.sector_size; + + mutex_enter(&ump->allocate_mutex); + + udf_calc_freespace(ump, &sizeblks, &freeblks); + + sbp->f_blocks = sizeblks; + sbp->f_bfree = freeblks; + sbp->f_files = 0; + + lvid = ump->logvol_integrity; + num_part = udf_rw32(lvid->num_part); + impl = (struct udf_logvol_info *) (lvid->tables + 2*num_part); + if (impl) { + sbp->f_files = udf_rw32(impl->num_files); + sbp->f_files += udf_rw32(impl->num_directories); + } + + /* XXX read only for now XXX */ + sbp->f_bavail = 0; + sbp->f_bresvd = 0; + + /* tricky, next only aplies to ffs i think, so set to zero */ + sbp->f_ffree = 0; + sbp->f_favail = 0; + sbp->f_fresvd = 0; + + mutex_exit(&ump->allocate_mutex); + + copy_statvfs_info(sbp, mp); + return 0; +} + +/* --------------------------------------------------------------------- */ + +/* + * TODO what about writing out free space maps, lvid etc? only on `waitfor' + * i.e. explicit syncing by the user? + */ + +static int +udf_sync_writeout_system_files(struct udf_mount *ump, int clearflags) +{ + int error; + + /* XXX lock for VAT en bitmaps? */ + /* metadata nodes are written synchronous */ + DPRINTF(CALL, ("udf_sync: syncing metadata\n")); + if (ump->lvclose & UDF_WRITE_VAT) + udf_writeout_vat(ump); + + error = 0; + if (ump->lvclose & UDF_WRITE_PART_BITMAPS) { + /* writeout metadata spacetable if existing */ + error = udf_write_metadata_partition_spacetable(ump, MNT_WAIT); + if (error) + printf( "udf_writeout_system_files : " + " writeout of metadata space bitmap failed\n"); + + /* writeout partition spacetables */ + error = udf_write_physical_partition_spacetables(ump, MNT_WAIT); + if (error) + printf( "udf_writeout_system_files : " + "writeout of space tables failed\n"); + if (!error && clearflags) + ump->lvclose &= ~UDF_WRITE_PART_BITMAPS; + } + + return error; +} + + +int +udf_sync(struct mount *mp, int waitfor, kauth_cred_t cred) +{ + struct udf_mount *ump = VFSTOUDF(mp); + + DPRINTF(CALL, ("udf_sync called\n")); + /* if called when mounted readonly, just ignore */ + if (mp->mnt_flag & MNT_RDONLY) + return 0; + + if (ump->syncing && !waitfor) { + printf("UDF: skipping autosync\n"); + return 0; + } + + /* get sync lock */ + ump->syncing = 1; + + /* pre-sync */ + udf_do_sync(ump, cred, waitfor); + + if (waitfor == MNT_WAIT) + udf_sync_writeout_system_files(ump, true); + + DPRINTF(CALL, ("end of udf_sync()\n")); + ump->syncing = 0; + + return 0; +} + +/* --------------------------------------------------------------------- */ + +/* + * Get vnode for the file system type specific file id ino for the fs. Its + * used for reference to files by unique ID and for NFSv3. + * (optional) TODO lookup why some sources state NFSv3 + */ +int +udf_vget(struct mount *mp, ino_t ino, + struct vnode **vpp) +{ + DPRINTF(NOTIMPL, ("udf_vget called\n")); + return EOPNOTSUPP; +} + +/* --------------------------------------------------------------------- */ + +/* + * Lookup vnode for file handle specified + */ +int +udf_fhtovp(struct mount *mp, struct fid *fhp, + struct vnode **vpp) +{ + DPRINTF(NOTIMPL, ("udf_fhtovp called\n")); + return EOPNOTSUPP; +} + +/* --------------------------------------------------------------------- */ + +/* + * Create an unique file handle. Its structure is opaque and won't be used by + * other subsystems. It should uniquely identify the file in the filingsystem + * and enough information to know if a file has been removed and/or resources + * have been recycled. + */ +int +udf_vptofh(struct vnode *vp, struct fid *fid, + size_t *fh_size) +{ + DPRINTF(NOTIMPL, ("udf_vptofh called\n")); + return EOPNOTSUPP; +} + +/* --------------------------------------------------------------------- */ + +/* + * Create a filingsystem snapshot at the specified timestamp. Could be + * implemented by explicitly creating a new session or with spare room in the + * integrity descriptor space + */ +int +udf_snapshot(struct mount *mp, struct vnode *vp, + struct timespec *tm) +{ + DPRINTF(NOTIMPL, ("udf_snapshot called\n")); + return EOPNOTSUPP; +} + +/* --------------------------------------------------------------------- */ diff --git a/sys/fs/udf/udf_vnops.c b/sys/fs/udf/udf_vnops.c new file mode 100644 index 000000000..697d89162 --- /dev/null +++ b/sys/fs/udf/udf_vnops.c @@ -0,0 +1,2183 @@ +/* $NetBSD: udf_vnops.c,v 1.87 2013/10/18 19:56:55 christos Exp $ */ + +/* + * Copyright (c) 2006, 2008 Reinoud Zandijk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Generic parts are derived from software contributed to The NetBSD Foundation + * by Julio M. Merino Vidal, developed as part of Google's Summer of Code + * 2005 program. + * + */ + +#include +#ifndef lint +__KERNEL_RCSID(0, "$NetBSD: udf_vnops.c,v 1.87 2013/10/18 19:56:55 christos Exp $"); +#endif /* not lint */ + + +#include +#include +#include +#include /* defines plimit structure in proc struct */ +#include +#include /* define FWRITE ... */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "udf.h" +#include "udf_subr.h" +#include "udf_bswap.h" + + +#define VTOI(vnode) ((struct udf_node *) (vnode)->v_data) + + +/* externs */ +extern int prtactive; + +/* implementations of vnode functions; table follows at end */ +/* --------------------------------------------------------------------- */ + +int +udf_inactive(void *v) +{ + struct vop_inactive_args /* { + struct vnode *a_vp; + bool *a_recycle; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct udf_node *udf_node = VTOI(vp); + int refcnt; + + DPRINTF(NODE, ("udf_inactive called for udf_node %p\n", VTOI(vp))); + + if (udf_node == NULL) { + DPRINTF(NODE, ("udf_inactive: inactive NULL UDF node\n")); + VOP_UNLOCK(vp); + return 0; + } + + /* + * Optionally flush metadata to disc. If the file has not been + * referenced anymore in a directory we ought to free up the resources + * on disc if applicable. + */ + if (udf_node->fe) { + refcnt = udf_rw16(udf_node->fe->link_cnt); + } else { + assert(udf_node->efe); + refcnt = udf_rw16(udf_node->efe->link_cnt); + } + + if ((refcnt == 0) && (vp->v_vflag & VV_SYSTEM)) { + DPRINTF(VOLUMES, ("UDF_INACTIVE deleting VV_SYSTEM\n")); + /* system nodes are not writen out on inactive, so flush */ + udf_node->i_flags = 0; + } + + *ap->a_recycle = false; + if ((refcnt == 0) && ((vp->v_vflag & VV_SYSTEM) == 0)) { + /* remove this file's allocation */ + DPRINTF(NODE, ("udf_inactive deleting unlinked file\n")); + *ap->a_recycle = true; + udf_delete_node(udf_node); + VOP_UNLOCK(vp); + return 0; + } + + /* write out its node */ + if (udf_node->i_flags & (IN_CHANGE | IN_UPDATE | IN_MODIFIED)) + udf_update(vp, NULL, NULL, NULL, 0); + VOP_UNLOCK(vp); + + return 0; +} + +/* --------------------------------------------------------------------- */ + +int udf_sync(struct mount *mp, int waitfor, kauth_cred_t cred, struct lwp *lwp); + +int +udf_reclaim(void *v) +{ + struct vop_reclaim_args /* { + struct vnode *a_vp; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct udf_node *udf_node = VTOI(vp); + + DPRINTF(NODE, ("udf_reclaim called for node %p\n", udf_node)); + if (prtactive && vp->v_usecount > 1) + vprint("udf_reclaim(): pushing active", vp); + + if (udf_node == NULL) { + DPRINTF(NODE, ("udf_reclaim(): null udfnode\n")); + return 0; + } + + /* update note for closure */ + udf_update(vp, NULL, NULL, NULL, UPDATE_CLOSE); + + /* async check to see if all node descriptors are written out */ + while ((volatile int) udf_node->outstanding_nodedscr > 0) { + vprint("udf_reclaim(): waiting for writeout\n", vp); + tsleep(&udf_node->outstanding_nodedscr, PRIBIO, "recl wait", hz/8); + } + + /* dispose all node knowledge */ + udf_dispose_node(udf_node); + + return 0; +} + +/* --------------------------------------------------------------------- */ + +int +udf_read(void *v) +{ + struct vop_read_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + kauth_cred_t a_cred; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct uio *uio = ap->a_uio; + int ioflag = ap->a_ioflag; + int advice = IO_ADV_DECODE(ap->a_ioflag); + struct uvm_object *uobj; + struct udf_node *udf_node = VTOI(vp); + struct file_entry *fe; + struct extfile_entry *efe; + uint64_t file_size; + vsize_t len; + int error; + + /* + * XXX reading from extended attributes not yet implemented. FreeBSD + * has it in mind to forward the IO_EXT read call to the + * VOP_READEXTATTR(). + */ + + DPRINTF(READ, ("udf_read called\n")); + + /* can this happen? some filingsystems have this check */ + if (uio->uio_offset < 0) + return EINVAL; + if (uio->uio_resid == 0) + return 0; + + /* protect against rogue programs reading raw directories and links */ + if ((ioflag & IO_ALTSEMANTICS) == 0) { + if (vp->v_type == VDIR) + return EISDIR; + /* all but regular files just give EINVAL */ + if (vp->v_type != VREG) + return EINVAL; + } + + assert(udf_node); + assert(udf_node->fe || udf_node->efe); + + /* get file/directory filesize */ + if (udf_node->fe) { + fe = udf_node->fe; + file_size = udf_rw64(fe->inf_len); + } else { + assert(udf_node->efe); + efe = udf_node->efe; + file_size = udf_rw64(efe->inf_len); + } + + /* read contents using buffercache */ + uobj = &vp->v_uobj; + error = 0; + while (uio->uio_resid > 0) { + /* reached end? */ + if (file_size <= uio->uio_offset) + break; + + /* maximise length to file extremity */ + len = MIN(file_size - uio->uio_offset, uio->uio_resid); + if (len == 0) + break; + + /* ubc, here we come, prepare to trap */ + error = ubc_uiomove(uobj, uio, len, advice, + UBC_READ | UBC_PARTIALOK | UBC_UNMAP_FLAG(vp)); + if (error) + break; + } + + /* note access time unless not requested */ + if (!(vp->v_mount->mnt_flag & MNT_NOATIME)) { + udf_node->i_flags |= IN_ACCESS; + if ((ioflag & IO_SYNC) == IO_SYNC) + error = udf_update(vp, NULL, NULL, NULL, UPDATE_WAIT); + } + + return error; +} + +/* --------------------------------------------------------------------- */ + +int +udf_write(void *v) +{ + struct vop_write_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + kauth_cred_t a_cred; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct uio *uio = ap->a_uio; + int ioflag = ap->a_ioflag; + kauth_cred_t cred = ap->a_cred; + int advice = IO_ADV_DECODE(ap->a_ioflag); + struct uvm_object *uobj; + struct udf_node *udf_node = VTOI(vp); + struct file_entry *fe; + struct extfile_entry *efe; + uint64_t file_size, old_size, old_offset; + vsize_t len; + int aflag = ioflag & IO_SYNC ? B_SYNC : 0; + int error; + int resid, extended; + + /* + * XXX writing to extended attributes not yet implemented. FreeBSD has + * it in mind to forward the IO_EXT read call to the + * VOP_READEXTATTR(). + */ + + DPRINTF(WRITE, ("udf_write called\n")); + + /* can this happen? some filingsystems have this check */ + if (uio->uio_offset < 0) + return EINVAL; + if (uio->uio_resid == 0) + return 0; + + /* protect against rogue programs writing raw directories or links */ + if ((ioflag & IO_ALTSEMANTICS) == 0) { + if (vp->v_type == VDIR) + return EISDIR; + /* all but regular files just give EINVAL for now */ + if (vp->v_type != VREG) + return EINVAL; + } + + assert(udf_node); + assert(udf_node->fe || udf_node->efe); + + /* get file/directory filesize */ + if (udf_node->fe) { + fe = udf_node->fe; + file_size = udf_rw64(fe->inf_len); + } else { + assert(udf_node->efe); + efe = udf_node->efe; + file_size = udf_rw64(efe->inf_len); + } + old_size = file_size; + + /* if explicitly asked to append, uio_offset can be wrong? */ + if (ioflag & IO_APPEND) + uio->uio_offset = file_size; + + extended = (uio->uio_offset + uio->uio_resid > file_size); + if (extended) { + DPRINTF(WRITE, ("extending file from %"PRIu64" to %"PRIu64"\n", + file_size, uio->uio_offset + uio->uio_resid)); + error = udf_grow_node(udf_node, uio->uio_offset + uio->uio_resid); + if (error) + return error; + file_size = uio->uio_offset + uio->uio_resid; + } + + /* write contents using buffercache */ + uobj = &vp->v_uobj; + resid = uio->uio_resid; + error = 0; + + uvm_vnp_setwritesize(vp, file_size); + old_offset = uio->uio_offset; + while (uio->uio_resid > 0) { + /* maximise length to file extremity */ + len = MIN(file_size - uio->uio_offset, uio->uio_resid); + if (len == 0) + break; + + genfs_node_wrlock(vp); + error = GOP_ALLOC(vp, uio->uio_offset, len, aflag, cred); + genfs_node_unlock(vp); + if (error) + break; + + /* ubc, here we come, prepare to trap */ + error = ubc_uiomove(uobj, uio, len, advice, + UBC_WRITE | UBC_UNMAP_FLAG(vp)); + if (error) + break; + + /* + * flush what we just wrote if necessary. + * XXXUBC simplistic async flushing. + * + * Directories are excluded since its file data that we want + * to purge. + */ + if ((vp->v_type != VDIR) && + (old_offset >> 16 != uio->uio_offset >> 16)) { + mutex_enter(vp->v_interlock); + error = VOP_PUTPAGES(vp, (old_offset >> 16) << 16, + (uio->uio_offset >> 16) << 16, + PGO_CLEANIT | PGO_LAZY); + old_offset = uio->uio_offset; + } + } + uvm_vnp_setsize(vp, file_size); + + /* mark node changed and request update */ + udf_node->i_flags |= IN_CHANGE | IN_UPDATE; + if (vp->v_mount->mnt_flag & MNT_RELATIME) + udf_node->i_flags |= IN_ACCESS; + + /* + * XXX TODO FFS has code here to reset setuid & setgid when we're not + * the superuser as a precaution against tampering. + */ + + /* if we wrote a thing, note write action on vnode */ + if (resid > uio->uio_resid) + VN_KNOTE(vp, NOTE_WRITE | (extended ? NOTE_EXTEND : 0)); + + if (error) { + /* bring back file size to its former size */ + /* take notice of its errors? */ + (void) udf_chsize(vp, (u_quad_t) old_size, cred); + + /* roll back uio */ + uio->uio_offset -= resid - uio->uio_resid; + uio->uio_resid = resid; + } else { + /* if we write and we're synchronous, update node */ + if ((resid > uio->uio_resid) && ((ioflag & IO_SYNC) == IO_SYNC)) + error = udf_update(vp, NULL, NULL, NULL, UPDATE_WAIT); + } + + return error; +} + + +/* --------------------------------------------------------------------- */ + +/* + * `Special' bmap functionality that translates all incomming requests to + * translate to vop_strategy() calls with the same blocknumbers effectively + * not translating at all. + */ + +int +udf_trivial_bmap(void *v) +{ + struct vop_bmap_args /* { + struct vnode *a_vp; + daddr_t a_bn; + struct vnode **a_vpp; + daddr_t *a_bnp; + int *a_runp; + } */ *ap = v; + struct vnode *vp = ap->a_vp; /* our node */ + struct vnode **vpp = ap->a_vpp; /* return node */ + daddr_t *bnp = ap->a_bnp; /* translated */ + daddr_t bn = ap->a_bn; /* origional */ + int *runp = ap->a_runp; + struct udf_node *udf_node = VTOI(vp); + uint32_t lb_size; + + /* get logical block size */ + lb_size = udf_rw32(udf_node->ump->logical_vol->lb_size); + + /* could return `-1' to indicate holes/zeros */ + /* translate 1:1 */ + *bnp = bn; + + /* set the vnode to read the data from with strategy on itself */ + if (vpp) + *vpp = vp; + + /* set runlength of maximum block size */ + if (runp) + *runp = MAXPHYS / lb_size; /* or with -1 ? */ + + /* return success */ + return 0; +} + +/* --------------------------------------------------------------------- */ + +int +udf_vfsstrategy(void *v) +{ + struct vop_strategy_args /* { + struct vnode *a_vp; + struct buf *a_bp; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct buf *bp = ap->a_bp; + struct udf_node *udf_node = VTOI(vp); + uint32_t lb_size, sectors; + + DPRINTF(STRATEGY, ("udf_strategy called\n")); + + /* check if we ought to be here */ + if (vp->v_type == VBLK || vp->v_type == VCHR) + panic("udf_strategy: spec"); + + /* only filebuffers ought to be read/write by this, no descriptors */ + assert(bp->b_blkno >= 0); + + /* get sector size */ + lb_size = udf_rw32(udf_node->ump->logical_vol->lb_size); + + /* calculate length to fetch/store in sectors */ + sectors = bp->b_bcount / lb_size; + assert(bp->b_bcount > 0); + + /* NEVER assume later that this buffer is already translated */ + /* bp->b_lblkno = bp->b_blkno; */ + + /* check assertions: we OUGHT to always get multiples of this */ + assert(sectors * lb_size == bp->b_bcount); + + /* issue buffer */ + if (bp->b_flags & B_READ) { + DPRINTF(STRATEGY, ("\tread vp %p buf %p (blk no %"PRIu64")" + ", for %d sectors\n", + vp, bp, bp->b_blkno, sectors)); + + /* read buffer from the udf_node, translate vtop on the way*/ + udf_read_filebuf(udf_node, bp); + } else { + DPRINTF(STRATEGY, ("\twrite vp %p buf %p (blk no %"PRIu64")" + ", for %d sectors\n", + vp, bp, bp->b_blkno, sectors)); + + /* write buffer to the udf_node, translate vtop on the way*/ + udf_write_filebuf(udf_node, bp); + } + + return bp->b_error; +} + +/* --------------------------------------------------------------------- */ + +int +udf_readdir(void *v) +{ + struct vop_readdir_args /* { + struct vnode *a_vp; + struct uio *a_uio; + kauth_cred_t a_cred; + int *a_eofflag; + off_t **a_cookies; + int *a_ncookies; + } */ *ap = v; + struct uio *uio = ap->a_uio; + struct vnode *vp = ap->a_vp; + struct udf_node *udf_node = VTOI(vp); + struct file_entry *fe; + struct extfile_entry *efe; + struct fileid_desc *fid; + struct dirent *dirent; + uint64_t file_size, diroffset, transoffset; + uint32_t lb_size; + int error; + + DPRINTF(READDIR, ("udf_readdir called\n")); + + /* This operation only makes sense on directory nodes. */ + if (vp->v_type != VDIR) + return ENOTDIR; + + /* get directory filesize */ + if (udf_node->fe) { + fe = udf_node->fe; + file_size = udf_rw64(fe->inf_len); + } else { + assert(udf_node->efe); + efe = udf_node->efe; + file_size = udf_rw64(efe->inf_len); + } + + dirent = malloc(sizeof(struct dirent), M_UDFTEMP, M_WAITOK | M_ZERO); + + /* + * Add `.' pseudo entry if at offset zero since its not in the fid + * stream + */ + if (uio->uio_offset == 0) { + DPRINTF(READDIR, ("\t'.' inserted\n")); + strcpy(dirent->d_name, "."); + dirent->d_fileno = udf_get_node_id(&udf_node->loc); + dirent->d_type = DT_DIR; + dirent->d_namlen = strlen(dirent->d_name); + dirent->d_reclen = _DIRENT_SIZE(dirent); + uiomove(dirent, _DIRENT_SIZE(dirent), uio); + + /* mark with magic value that we have done the dummy */ + uio->uio_offset = UDF_DIRCOOKIE_DOT; + } + + /* we are called just as long as we keep on pushing data in */ + error = 0; + if (uio->uio_offset < file_size) { + /* allocate temporary space for fid */ + lb_size = udf_rw32(udf_node->ump->logical_vol->lb_size); + fid = malloc(lb_size, M_UDFTEMP, M_WAITOK); + + if (uio->uio_offset == UDF_DIRCOOKIE_DOT) + uio->uio_offset = 0; + + diroffset = uio->uio_offset; + transoffset = diroffset; + while (diroffset < file_size) { + DPRINTF(READDIR, ("\tread in fid stream\n")); + /* transfer a new fid/dirent */ + error = udf_read_fid_stream(vp, &diroffset, fid, dirent); + DPRINTFIF(READDIR, error, ("read error in read fid " + "stream : %d\n", error)); + if (error) + break; + + /* + * If there isn't enough space in the uio to return a + * whole dirent, break off read + */ + if (uio->uio_resid < _DIRENT_SIZE(dirent)) + break; + + /* remember the last entry we transfered */ + transoffset = diroffset; + + /* skip deleted entries */ + if (fid->file_char & UDF_FILE_CHAR_DEL) + continue; + + /* skip not visible files */ + if (fid->file_char & UDF_FILE_CHAR_VIS) + continue; + + /* copy dirent to the caller */ + DPRINTF(READDIR, ("\tread dirent `%s', type %d\n", + dirent->d_name, dirent->d_type)); + uiomove(dirent, _DIRENT_SIZE(dirent), uio); + } + + /* pass on last transfered offset */ + uio->uio_offset = transoffset; + free(fid, M_UDFTEMP); + } + + if (ap->a_eofflag) + *ap->a_eofflag = (uio->uio_offset >= file_size); + +#ifdef DEBUG + if (udf_verbose & UDF_DEBUG_READDIR) { + printf("returning offset %d\n", (uint32_t) uio->uio_offset); + if (ap->a_eofflag) + printf("returning EOF ? %d\n", *ap->a_eofflag); + if (error) + printf("readdir returning error %d\n", error); + } +#endif + + free(dirent, M_UDFTEMP); + return error; +} + +/* --------------------------------------------------------------------- */ + +int +udf_lookup(void *v) +{ + struct vop_lookup_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + } */ *ap = v; + struct vnode *dvp = ap->a_dvp; + struct vnode **vpp = ap->a_vpp; + struct componentname *cnp = ap->a_cnp; + struct udf_node *dir_node, *res_node; + struct udf_mount *ump; + struct long_ad icb_loc; + mode_t mode; + uid_t d_uid; + gid_t d_gid; + const char *name; + int namelen, nameiop, islastcn, mounted_ro; + int error, found; + + dir_node = VTOI(dvp); + ump = dir_node->ump; + *vpp = NULL; + + DPRINTF(LOOKUP, ("udf_lookup called, lookup `%s`\n", + cnp->cn_nameptr)); + + /* simplify/clarification flags */ + nameiop = cnp->cn_nameiop; + islastcn = cnp->cn_flags & ISLASTCN; + mounted_ro = dvp->v_mount->mnt_flag & MNT_RDONLY; + + /* check exec/dirread permissions first */ + error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred); + if (error) + return error; + + DPRINTF(LOOKUP, ("\taccess ok\n")); + + /* + * If requesting a modify on the last path element on a read-only + * filingsystem, reject lookup; XXX why is this repeated in every FS ? + */ + if (islastcn && mounted_ro && (nameiop == DELETE || nameiop == RENAME)) + return EROFS; + + DPRINTF(LOOKUP, ("\tlooking up cnp->cn_nameptr '%s'\n", + cnp->cn_nameptr)); + /* look in the namecache */ + if (cache_lookup(dvp, cnp->cn_nameptr, cnp->cn_namelen, + cnp->cn_nameiop, cnp->cn_flags, NULL, vpp)) { + return *vpp == NULLVP ? ENOENT : 0; + } + + DPRINTF(LOOKUP, ("\tNOT found in cache\n")); + + /* + * Obviously, the file is not (anymore) in the namecache, we have to + * search for it. There are three basic cases: '.', '..' and others. + * + * Following the guidelines of VOP_LOOKUP manpage and tmpfs. + */ + error = 0; + if ((cnp->cn_namelen == 1) && (cnp->cn_nameptr[0] == '.')) { + DPRINTF(LOOKUP, ("\tlookup '.'\n")); + /* special case 1 '.' */ + if (islastcn && cnp->cn_nameiop == RENAME) { + error = EISDIR; + goto out; + } + vref(dvp); + *vpp = dvp; + /* done */ + goto done; + } else if (cnp->cn_flags & ISDOTDOT) { + /* special case 2 '..' */ + DPRINTF(LOOKUP, ("\tlookup '..'\n")); + + if (islastcn && cnp->cn_nameiop == RENAME) { + error = EINVAL; + goto out; + } + + /* get our node */ + name = ".."; + namelen = 2; + error = udf_lookup_name_in_dir(dvp, name, namelen, + &icb_loc, &found); + if (error) + goto out; + if (!found) + error = ENOENT; + + /* first unlock parent */ + VOP_UNLOCK(dvp); + + if (error == 0) { + DPRINTF(LOOKUP, ("\tfound '..'\n")); + /* try to create/reuse the node */ + error = udf_get_node(ump, &icb_loc, &res_node); + + if (!error) { + DPRINTF(LOOKUP, + ("\tnode retrieved/created OK\n")); + *vpp = res_node->vnode; + } + } + + /* try to relock parent */ + vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY); + goto out; + } + + /* all other files */ + DPRINTF(LOOKUP, ("\tlookup file/dir in directory\n")); + + /* lookup filename in the directory; location icb_loc */ + name = cnp->cn_nameptr; + namelen = cnp->cn_namelen; + error = udf_lookup_name_in_dir(dvp, name, namelen, + &icb_loc, &found); + if (error) + goto out; + if (!found) { + DPRINTF(LOOKUP, ("\tNOT found\n")); + /* + * The entry was not found in the directory. This is + * valid if we are creating or renaming an entry and + * are working on the last component of the path name. + */ + if (islastcn && (cnp->cn_nameiop == CREATE || + cnp->cn_nameiop == RENAME)) { + error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred); + if (error) { + goto out; + } + error = EJUSTRETURN; + } else { + error = ENOENT; + } + /* done */ + goto done; + } + + /* + * XXX NOTE tmpfs has a test here that tests that intermediate + * components i.e. not the last one ought to be either a directory or + * a link. It seems to function well without this code. + */ + + /* try to create/reuse the node */ + error = udf_get_node(ump, &icb_loc, &res_node); + if (error) + goto out; + + /* check permissions */ + if (islastcn && (cnp->cn_nameiop == DELETE || + cnp->cn_nameiop == RENAME) ) { + error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred); + if (error) { + vput(res_node->vnode); + goto out; + } + + /* + * Check if the directory has its sticky bit set. If so, ask + * for clearance since only the owner of a file or directory + * can remove/rename from taht directory. + */ + mode = udf_getaccessmode(dir_node); + if ((mode & S_ISTXT) != 0) { + udf_getownership(dir_node, &d_uid, &d_gid); + error = kauth_authorize_vnode(cnp->cn_cred, + KAUTH_VNODE_DELETE, res_node->vnode, + dir_node->vnode, genfs_can_sticky(cnp->cn_cred, + d_uid, d_uid)); + if (error) { + error = EPERM; + vput(res_node->vnode); + goto out; + } + } + } + + *vpp = res_node->vnode; + +done: + /* + * Store result in the cache if requested. If we are creating a file, + * the file might not be found and thus putting it into the namecache + * might be seen as negative caching. + */ + if (nameiop != CREATE) + cache_enter(dvp, *vpp, cnp->cn_nameptr, cnp->cn_namelen, + cnp->cn_flags); + +out: + DPRINTFIF(LOOKUP, error, ("udf_lookup returing error %d\n", error)); + + return error; +} + +/* --------------------------------------------------------------------- */ + +int +udf_getattr(void *v) +{ + struct vop_getattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + kauth_cred_t a_cred; + struct lwp *a_l; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct udf_node *udf_node = VTOI(vp); + struct udf_mount *ump = udf_node->ump; + struct file_entry *fe = udf_node->fe; + struct extfile_entry *efe = udf_node->efe; + struct filetimes_extattr_entry *ft_extattr; + struct device_extattr_entry *devattr; + struct vattr *vap = ap->a_vap; + struct timestamp *atime, *mtime, *attrtime, *creatime; + uint64_t filesize, blkssize; + uint32_t nlink; + uint32_t offset, a_l; + uint8_t *filedata; + uid_t uid; + gid_t gid; + int error; + + DPRINTF(CALL, ("udf_getattr called\n")); + + /* update times before we returning values */ + udf_itimes(udf_node, NULL, NULL, NULL); + + /* get descriptor information */ + if (fe) { + nlink = udf_rw16(fe->link_cnt); + uid = (uid_t)udf_rw32(fe->uid); + gid = (gid_t)udf_rw32(fe->gid); + filesize = udf_rw64(fe->inf_len); + blkssize = udf_rw64(fe->logblks_rec); + atime = &fe->atime; + mtime = &fe->mtime; + attrtime = &fe->attrtime; + filedata = fe->data; + + /* initial guess */ + creatime = mtime; + + /* check our extended attribute if present */ + error = udf_extattr_search_intern(udf_node, + UDF_FILETIMES_ATTR_NO, "", &offset, &a_l); + if (!error) { + ft_extattr = (struct filetimes_extattr_entry *) + (filedata + offset); + if (ft_extattr->existence & UDF_FILETIMES_FILE_CREATION) + creatime = &ft_extattr->times[0]; + } + } else { + assert(udf_node->efe); + nlink = udf_rw16(efe->link_cnt); + uid = (uid_t)udf_rw32(efe->uid); + gid = (gid_t)udf_rw32(efe->gid); + filesize = udf_rw64(efe->inf_len); /* XXX or obj_size? */ + blkssize = udf_rw64(efe->logblks_rec); + atime = &efe->atime; + mtime = &efe->mtime; + attrtime = &efe->attrtime; + creatime = &efe->ctime; + filedata = efe->data; + } + + /* do the uid/gid translation game */ + if (uid == (uid_t) -1) + uid = ump->mount_args.anon_uid; + if (gid == (gid_t) -1) + gid = ump->mount_args.anon_gid; + + /* fill in struct vattr with values from the node */ + vattr_null(vap); + vap->va_type = vp->v_type; + vap->va_mode = udf_getaccessmode(udf_node); + vap->va_nlink = nlink; + vap->va_uid = uid; + vap->va_gid = gid; + vap->va_fsid = vp->v_mount->mnt_stat.f_fsidx.__fsid_val[0]; + vap->va_fileid = udf_get_node_id(&udf_node->loc); /* inode hash XXX */ + vap->va_size = filesize; + vap->va_blocksize = udf_node->ump->discinfo.sector_size; /* wise? */ + + /* + * BUG-ALERT: UDF doesn't count '.' as an entry, so we'll have to add + * 1 to the link count if its a directory we're requested attributes + * of. + */ + if (vap->va_type == VDIR) + vap->va_nlink++; + + /* access times */ + udf_timestamp_to_timespec(ump, atime, &vap->va_atime); + udf_timestamp_to_timespec(ump, mtime, &vap->va_mtime); + udf_timestamp_to_timespec(ump, attrtime, &vap->va_ctime); + udf_timestamp_to_timespec(ump, creatime, &vap->va_birthtime); + + vap->va_gen = 1; /* no multiple generations yes (!?) */ + vap->va_flags = 0; /* no flags */ + vap->va_bytes = blkssize * udf_node->ump->discinfo.sector_size; + vap->va_filerev = 1; /* TODO file revision numbers? */ + vap->va_vaflags = 0; + /* TODO get vaflags from the extended attributes? */ + + if ((vap->va_type == VBLK) || (vap->va_type == VCHR)) { + error = udf_extattr_search_intern(udf_node, + UDF_DEVICESPEC_ATTR_NO, "", + &offset, &a_l); + /* if error, deny access */ + if (error || (filedata == NULL)) { + vap->va_mode = 0; /* or v_type = VNON? */ + } else { + devattr = (struct device_extattr_entry *) + filedata + offset; + vap->va_rdev = makedev( + udf_rw32(devattr->major), + udf_rw32(devattr->minor) + ); + /* TODO we could check the implementator */ + } + } + + return 0; +} + +/* --------------------------------------------------------------------- */ + +static int +udf_chown(struct vnode *vp, uid_t new_uid, gid_t new_gid, + kauth_cred_t cred) +{ + struct udf_node *udf_node = VTOI(vp); + uid_t uid; + gid_t gid; + int error; + +#ifdef notyet + /* TODO get vaflags from the extended attributes? */ + /* Immutable or append-only files cannot be modified, either. */ + if (udf_node->flags & (IMMUTABLE | APPEND)) + return EPERM; +#endif + + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return EROFS; + + /* retrieve old values */ + udf_getownership(udf_node, &uid, &gid); + + /* only one could be specified */ + if (new_uid == VNOVAL) + new_uid = uid; + if (new_gid == VNOVAL) + new_gid = gid; + + /* check if we can fit it in an 32 bits */ + if ((uid_t) ((uint32_t) new_uid) != new_uid) + return EINVAL; + if ((gid_t) ((uint32_t) new_gid) != new_gid) + return EINVAL; + + /* check permissions */ + error = kauth_authorize_vnode(cred, KAUTH_VNODE_CHANGE_OWNERSHIP, + vp, NULL, genfs_can_chown(cred, uid, gid, new_uid, new_gid)); + if (error) + return (error); + + /* change the ownership */ + udf_setownership(udf_node, new_uid, new_gid); + + /* mark node changed */ + udf_node->i_flags |= IN_CHANGE; + if (vp->v_mount->mnt_flag & MNT_RELATIME) + udf_node->i_flags |= IN_ACCESS; + + return 0; +} + + +static int +udf_chmod(struct vnode *vp, mode_t mode, kauth_cred_t cred) +{ + struct udf_node *udf_node = VTOI(vp); + uid_t uid; + gid_t gid; + int error; + +#ifdef notyet + /* TODO get vaflags from the extended attributes? */ + /* Immutable or append-only files cannot be modified, either. */ + if (udf_node->flags & (IMMUTABLE | APPEND)) + return EPERM; +#endif + + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return EROFS; + + /* retrieve uid/gid values */ + udf_getownership(udf_node, &uid, &gid); + + /* check permissions */ + error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_SECURITY, vp, + NULL, genfs_can_chmod(vp->v_type, cred, uid, gid, mode)); + if (error) + return (error); + + /* change mode */ + udf_setaccessmode(udf_node, mode); + + /* mark node changed */ + udf_node->i_flags |= IN_CHANGE; + if (vp->v_mount->mnt_flag & MNT_RELATIME) + udf_node->i_flags |= IN_ACCESS; + + return 0; +} + + +/* exported */ +int +udf_chsize(struct vnode *vp, u_quad_t newsize, kauth_cred_t cred) +{ + struct udf_node *udf_node = VTOI(vp); + int error, extended; + + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return EROFS; + + /* Decide whether this is a valid operation based on the file type. */ + switch (vp->v_type) { + case VDIR: + return EISDIR; + case VREG: + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return EROFS; + break; + case VBLK: + /* FALLTHROUGH */ + case VCHR: + /* FALLTHROUGH */ + case VFIFO: + /* Allow modifications of special files even if in the file + * system is mounted read-only (we are not modifying the + * files themselves, but the objects they represent). */ + return 0; + default: + /* Anything else is unsupported. */ + return EOPNOTSUPP; + } + +#if notyet + /* TODO get vaflags from the extended attributes? */ + /* Immutable or append-only files cannot be modified, either. */ + if (node->flags & (IMMUTABLE | APPEND)) + return EPERM; +#endif + + /* resize file to the requested size */ + error = udf_resize_node(udf_node, newsize, &extended); + + if (error == 0) { + /* mark change */ + udf_node->i_flags |= IN_CHANGE | IN_MODIFY; + if (vp->v_mount->mnt_flag & MNT_RELATIME) + udf_node->i_flags |= IN_ACCESS; + VN_KNOTE(vp, NOTE_ATTRIB | (extended ? NOTE_EXTEND : 0)); + udf_update(vp, NULL, NULL, NULL, 0); + } + + return error; +} + + +static int +udf_chflags(struct vnode *vp, mode_t mode, kauth_cred_t cred) +{ + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return EROFS; + + /* + * XXX we can't do this yet, as its not described in the standard yet + */ + + return EOPNOTSUPP; +} + + +static int +udf_chtimes(struct vnode *vp, + struct timespec *atime, struct timespec *mtime, + struct timespec *birthtime, int setattrflags, + kauth_cred_t cred) +{ + struct udf_node *udf_node = VTOI(vp); + uid_t uid; + gid_t gid; + int error; + +#ifdef notyet + /* TODO get vaflags from the extended attributes? */ + /* Immutable or append-only files cannot be modified, either. */ + if (udf_node->flags & (IMMUTABLE | APPEND)) + return EPERM; +#endif + + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return EROFS; + + /* retrieve uid/gid values */ + udf_getownership(udf_node, &uid, &gid); + + /* check permissions */ + error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_TIMES, vp, + NULL, genfs_can_chtimes(vp, setattrflags, uid, cred)); + if (error) + return (error); + + /* update node flags depending on what times are passed */ + if (atime->tv_sec != VNOVAL) + if (!(vp->v_mount->mnt_flag & MNT_NOATIME)) + udf_node->i_flags |= IN_ACCESS; + if ((mtime->tv_sec != VNOVAL) || (birthtime->tv_sec != VNOVAL)) { + udf_node->i_flags |= IN_CHANGE | IN_UPDATE; + if (vp->v_mount->mnt_flag & MNT_RELATIME) + udf_node->i_flags |= IN_ACCESS; + } + + return udf_update(vp, atime, mtime, birthtime, 0); +} + + +int +udf_setattr(void *v) +{ + struct vop_setattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + kauth_cred_t a_cred; + struct lwp *a_l; + } */ *ap = v; + struct vnode *vp = ap->a_vp; +/* struct udf_node *udf_node = VTOI(vp); */ +/* struct udf_mount *ump = udf_node->ump; */ + kauth_cred_t cred = ap->a_cred; + struct vattr *vap = ap->a_vap; + int error; + + DPRINTF(CALL, ("udf_setattr called\n")); + + /* Abort if any unsettable attribute is given. */ + error = 0; + if (vap->va_type != VNON || + vap->va_nlink != VNOVAL || + vap->va_fsid != VNOVAL || + vap->va_fileid != VNOVAL || + vap->va_blocksize != VNOVAL || +#ifdef notyet + /* checks are debated */ + vap->va_ctime.tv_sec != VNOVAL || + vap->va_ctime.tv_nsec != VNOVAL || + vap->va_birthtime.tv_sec != VNOVAL || + vap->va_birthtime.tv_nsec != VNOVAL || +#endif + vap->va_gen != VNOVAL || + vap->va_rdev != VNOVAL || + vap->va_bytes != VNOVAL) + error = EINVAL; + + DPRINTF(ATTR, ("setattr changing:\n")); + if (error == 0 && (vap->va_flags != VNOVAL)) { + DPRINTF(ATTR, ("\tchflags\n")); + error = udf_chflags(vp, vap->va_flags, cred); + } + + if (error == 0 && (vap->va_size != VNOVAL)) { + DPRINTF(ATTR, ("\tchsize\n")); + error = udf_chsize(vp, vap->va_size, cred); + } + + if (error == 0 && (vap->va_uid != VNOVAL || vap->va_gid != VNOVAL)) { + DPRINTF(ATTR, ("\tchown\n")); + error = udf_chown(vp, vap->va_uid, vap->va_gid, cred); + } + + if (error == 0 && (vap->va_mode != VNOVAL)) { + DPRINTF(ATTR, ("\tchmod\n")); + error = udf_chmod(vp, vap->va_mode, cred); + } + + if (error == 0 && + ((vap->va_atime.tv_sec != VNOVAL && + vap->va_atime.tv_nsec != VNOVAL) || + (vap->va_mtime.tv_sec != VNOVAL && + vap->va_mtime.tv_nsec != VNOVAL)) + ) { + DPRINTF(ATTR, ("\tchtimes\n")); + error = udf_chtimes(vp, &vap->va_atime, &vap->va_mtime, + &vap->va_birthtime, vap->va_vaflags, cred); + } + VN_KNOTE(vp, NOTE_ATTRIB); + + return error; +} + +/* --------------------------------------------------------------------- */ + +/* + * Return POSIX pathconf information for UDF file systems. + */ +int +udf_pathconf(void *v) +{ + struct vop_pathconf_args /* { + struct vnode *a_vp; + int a_name; + register_t *a_retval; + } */ *ap = v; + uint32_t bits; + + DPRINTF(CALL, ("udf_pathconf called\n")); + + switch (ap->a_name) { + case _PC_LINK_MAX: + *ap->a_retval = (1<<16)-1; /* 16 bits */ + return 0; + case _PC_NAME_MAX: + *ap->a_retval = UDF_MAXNAMLEN; + return 0; + case _PC_PATH_MAX: + *ap->a_retval = PATH_MAX; + return 0; + case _PC_PIPE_BUF: + *ap->a_retval = PIPE_BUF; + return 0; + case _PC_CHOWN_RESTRICTED: + *ap->a_retval = 1; + return 0; + case _PC_NO_TRUNC: + *ap->a_retval = 1; + return 0; + case _PC_SYNC_IO: + *ap->a_retval = 0; /* synchronised is off for performance */ + return 0; + case _PC_FILESIZEBITS: + /* 64 bit file offsets -> 2+floor(2log(2^64-1)) = 2 + 63 = 65 */ + bits = 64; /* XXX ought to deliver 65 */ +#if 0 + if (udf_node) + bits = 64 * vp->v_mount->mnt_dev_bshift; +#endif + *ap->a_retval = bits; + return 0; + } + + return EINVAL; +} + + +/* --------------------------------------------------------------------- */ + +int +udf_open(void *v) +{ + struct vop_open_args /* { + struct vnode *a_vp; + int a_mode; + kauth_cred_t a_cred; + struct proc *a_p; + } */ *ap = v; + int flags; + + DPRINTF(CALL, ("udf_open called\n")); + + /* + * Files marked append-only must be opened for appending. + * TODO: get chflags(2) flags from extened attribute. + */ + flags = 0; + if ((flags & APPEND) && (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE) + return (EPERM); + + return 0; +} + + +/* --------------------------------------------------------------------- */ + +int +udf_close(void *v) +{ + struct vop_close_args /* { + struct vnode *a_vp; + int a_fflag; + kauth_cred_t a_cred; + struct proc *a_p; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct udf_node *udf_node = VTOI(vp); + int async = vp->v_mount->mnt_flag & MNT_ASYNC; + int error; + + DPRINTF(CALL, ("udf_close called\n")); + udf_node = udf_node; /* shut up gcc */ + + if (!async && (vp->v_type != VDIR)) { + mutex_enter(vp->v_interlock); + error = VOP_PUTPAGES(vp, 0, 0, PGO_CLEANIT); + if (error) + return error; + } + + mutex_enter(vp->v_interlock); + if (vp->v_usecount > 1) + udf_itimes(udf_node, NULL, NULL, NULL); + mutex_exit(vp->v_interlock); + + return 0; +} + + +/* --------------------------------------------------------------------- */ + +static int +udf_check_possible(struct vnode *vp, struct vattr *vap, mode_t mode) +{ + int flags; + + /* check if we are allowed to write */ + switch (vap->va_type) { + case VDIR: + case VLNK: + case VREG: + /* + * normal nodes: check if we're on a read-only mounted + * filingsystem and bomb out if we're trying to write. + */ + if ((mode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) + return EROFS; + break; + case VBLK: + case VCHR: + case VSOCK: + case VFIFO: + /* + * special nodes: even on read-only mounted filingsystems + * these are allowed to be written to if permissions allow. + */ + break; + default: + /* no idea what this is */ + return EINVAL; + } + + /* noone may write immutable files */ + /* TODO: get chflags(2) flags from extened attribute. */ + flags = 0; + if ((mode & VWRITE) && (flags & IMMUTABLE)) + return EPERM; + + return 0; +} + +static int +udf_check_permitted(struct vnode *vp, struct vattr *vap, mode_t mode, + kauth_cred_t cred) +{ + /* ask the generic genfs_can_access to advice on security */ + return kauth_authorize_vnode(cred, KAUTH_ACCESS_ACTION(mode, + vp->v_type, vap->va_mode), vp, NULL, genfs_can_access(vp->v_type, + vap->va_mode, vap->va_uid, vap->va_gid, mode, cred)); +} + +int +udf_access(void *v) +{ + struct vop_access_args /* { + struct vnode *a_vp; + int a_mode; + kauth_cred_t a_cred; + struct proc *a_p; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + mode_t mode = ap->a_mode; + kauth_cred_t cred = ap->a_cred; + /* struct udf_node *udf_node = VTOI(vp); */ + struct vattr vap; + int error; + + DPRINTF(CALL, ("udf_access called\n")); + + error = VOP_GETATTR(vp, &vap, NULL); + if (error) + return error; + + error = udf_check_possible(vp, &vap, mode); + if (error) + return error; + + error = udf_check_permitted(vp, &vap, mode, cred); + + return error; +} + +/* --------------------------------------------------------------------- */ + +int +udf_create(void *v) +{ + struct vop_create_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *ap = v; + struct vnode *dvp = ap->a_dvp; + struct vnode **vpp = ap->a_vpp; + struct vattr *vap = ap->a_vap; + struct componentname *cnp = ap->a_cnp; + int error; + + DPRINTF(CALL, ("udf_create called\n")); + error = udf_create_node(dvp, vpp, vap, cnp); + + vput(dvp); + return error; +} + +/* --------------------------------------------------------------------- */ + +int +udf_mknod(void *v) +{ + struct vop_mknod_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *ap = v; + struct vnode *dvp = ap->a_dvp; + struct vnode **vpp = ap->a_vpp; + struct vattr *vap = ap->a_vap; + struct componentname *cnp = ap->a_cnp; + int error; + + DPRINTF(CALL, ("udf_mknod called\n")); + error = udf_create_node(dvp, vpp, vap, cnp); + + vput(dvp); + return error; +} + +/* --------------------------------------------------------------------- */ + +int +udf_mkdir(void *v) +{ + struct vop_mkdir_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *ap = v; + struct vnode *dvp = ap->a_dvp; + struct vnode **vpp = ap->a_vpp; + struct vattr *vap = ap->a_vap; + struct componentname *cnp = ap->a_cnp; + int error; + + DPRINTF(CALL, ("udf_mkdir called\n")); + error = udf_create_node(dvp, vpp, vap, cnp); + + vput(dvp); + return error; +} + +/* --------------------------------------------------------------------- */ + +static int +udf_do_link(struct vnode *dvp, struct vnode *vp, struct componentname *cnp) +{ + struct udf_node *udf_node, *dir_node; + struct vattr vap; + int error; + + DPRINTF(CALL, ("udf_link called\n")); + KASSERT(dvp != vp); + KASSERT(vp->v_type != VDIR); + KASSERT(dvp->v_mount == vp->v_mount); + + /* get attributes */ + dir_node = VTOI(dvp); + udf_node = VTOI(vp); + + error = VOP_GETATTR(vp, &vap, FSCRED); + if (error) { + VOP_UNLOCK(vp); + return error; + } + + /* check link count overflow */ + if (vap.va_nlink >= (1<<16)-1) { /* uint16_t */ + VOP_UNLOCK(vp); + return EMLINK; + } + + error = udf_dir_attach(dir_node->ump, dir_node, udf_node, &vap, cnp); + if (error) + VOP_UNLOCK(vp); + return error; +} + +int +udf_link(void *v) +{ + struct vop_link_args /* { + struct vnode *a_dvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *ap = v; + struct vnode *dvp = ap->a_dvp; + struct vnode *vp = ap->a_vp; + struct componentname *cnp = ap->a_cnp; + int error; + + error = udf_do_link(dvp, vp, cnp); + if (error) + VOP_ABORTOP(dvp, cnp); + + VN_KNOTE(vp, NOTE_LINK); + VN_KNOTE(dvp, NOTE_WRITE); + vput(dvp); + + return error; +} + +/* --------------------------------------------------------------------- */ + +static int +udf_do_symlink(struct udf_node *udf_node, char *target) +{ + struct pathcomp pathcomp; + uint8_t *pathbuf, *pathpos, *compnamepos; + char *mntonname; + int pathlen, len, compnamelen, mntonnamelen; + int error; + + /* process `target' to an UDF structure */ + pathbuf = malloc(UDF_SYMLINKBUFLEN, M_UDFTEMP, M_WAITOK); + pathpos = pathbuf; + pathlen = 0; + + if (*target == '/') { + /* symlink starts from the root */ + len = UDF_PATH_COMP_SIZE; + memset(&pathcomp, 0, len); + pathcomp.type = UDF_PATH_COMP_ROOT; + + /* check if its mount-point relative! */ + mntonname = udf_node->ump->vfs_mountp->mnt_stat.f_mntonname; + mntonnamelen = strlen(mntonname); + if (strlen(target) >= mntonnamelen) { + if (strncmp(target, mntonname, mntonnamelen) == 0) { + pathcomp.type = UDF_PATH_COMP_MOUNTROOT; + target += mntonnamelen; + } + } else { + target++; + } + + memcpy(pathpos, &pathcomp, len); + pathpos += len; + pathlen += len; + } + + error = 0; + while (*target) { + /* ignore multiple '/' */ + while (*target == '/') { + target++; + } + if (!*target) + break; + + /* extract component name */ + compnamelen = 0; + compnamepos = target; + while ((*target) && (*target != '/')) { + target++; + compnamelen++; + } + + /* just trunc if too long ?? (security issue) */ + if (compnamelen >= 127) { + error = ENAMETOOLONG; + break; + } + + /* convert unix name to UDF name */ + len = sizeof(struct pathcomp); + memset(&pathcomp, 0, len); + pathcomp.type = UDF_PATH_COMP_NAME; + len = UDF_PATH_COMP_SIZE; + + if ((compnamelen == 2) && (strncmp(compnamepos, "..", 2) == 0)) + pathcomp.type = UDF_PATH_COMP_PARENTDIR; + if ((compnamelen == 1) && (*compnamepos == '.')) + pathcomp.type = UDF_PATH_COMP_CURDIR; + + if (pathcomp.type == UDF_PATH_COMP_NAME) { + unix_to_udf_name( + (char *) &pathcomp.ident, &pathcomp.l_ci, + compnamepos, compnamelen, + &udf_node->ump->logical_vol->desc_charset); + len = UDF_PATH_COMP_SIZE + pathcomp.l_ci; + } + + if (pathlen + len >= UDF_SYMLINKBUFLEN) { + error = ENAMETOOLONG; + break; + } + + memcpy(pathpos, &pathcomp, len); + pathpos += len; + pathlen += len; + } + + if (error) { + /* aparently too big */ + free(pathbuf, M_UDFTEMP); + return error; + } + + error = udf_grow_node(udf_node, pathlen); + if (error) { + /* failed to pregrow node */ + free(pathbuf, M_UDFTEMP); + return error; + } + + /* write out structure on the new file */ + error = vn_rdwr(UIO_WRITE, udf_node->vnode, + pathbuf, pathlen, 0, + UIO_SYSSPACE, IO_NODELOCKED | IO_ALTSEMANTICS, + FSCRED, NULL, NULL); + + /* return status of symlink contents writeout */ + free(pathbuf, M_UDFTEMP); + return error; +} + + +int +udf_symlink(void *v) +{ + struct vop_symlink_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + char *a_target; + } */ *ap = v; + struct vnode *dvp = ap->a_dvp; + struct vnode **vpp = ap->a_vpp; + struct vattr *vap = ap->a_vap; + struct componentname *cnp = ap->a_cnp; + struct udf_node *dir_node; + struct udf_node *udf_node; + int error; + + DPRINTF(CALL, ("udf_symlink called\n")); + DPRINTF(CALL, ("\tlinking to `%s`\n", ap->a_target)); + error = udf_create_node(dvp, vpp, vap, cnp); + KASSERT(((error == 0) && (*vpp != NULL)) || ((error && (*vpp == NULL)))); + if (!error) { + dir_node = VTOI(dvp); + udf_node = VTOI(*vpp); + KASSERT(udf_node); + error = udf_do_symlink(udf_node, ap->a_target); + if (error) { + /* remove node */ + udf_shrink_node(udf_node, 0); + udf_dir_detach(udf_node->ump, dir_node, udf_node, cnp); + } + } + vput(dvp); + return error; +} + +/* --------------------------------------------------------------------- */ + +int +udf_readlink(void *v) +{ + struct vop_readlink_args /* { + struct vnode *a_vp; + struct uio *a_uio; + kauth_cred_t a_cred; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct uio *uio = ap->a_uio; + kauth_cred_t cred = ap->a_cred; + struct udf_node *udf_node; + struct pathcomp pathcomp; + struct vattr vattr; + uint8_t *pathbuf, *targetbuf, *tmpname; + uint8_t *pathpos, *targetpos; + char *mntonname; + int pathlen, targetlen, namelen, mntonnamelen, len, l_ci; + int first, error; + + DPRINTF(CALL, ("udf_readlink called\n")); + + udf_node = VTOI(vp); + error = VOP_GETATTR(vp, &vattr, cred); + if (error) + return error; + + /* claim temporary buffers for translation */ + pathbuf = malloc(UDF_SYMLINKBUFLEN, M_UDFTEMP, M_WAITOK); + targetbuf = malloc(PATH_MAX+1, M_UDFTEMP, M_WAITOK); + tmpname = malloc(PATH_MAX+1, M_UDFTEMP, M_WAITOK); + memset(pathbuf, 0, UDF_SYMLINKBUFLEN); + memset(targetbuf, 0, PATH_MAX); + + /* read contents of file in our temporary buffer */ + error = vn_rdwr(UIO_READ, udf_node->vnode, + pathbuf, vattr.va_size, 0, + UIO_SYSSPACE, IO_NODELOCKED | IO_ALTSEMANTICS, + FSCRED, NULL, NULL); + if (error) { + /* failed to read in symlink contents */ + free(pathbuf, M_UDFTEMP); + free(targetbuf, M_UDFTEMP); + free(tmpname, M_UDFTEMP); + return error; + } + + /* convert to a unix path */ + pathpos = pathbuf; + pathlen = 0; + targetpos = targetbuf; + targetlen = PATH_MAX; + mntonname = udf_node->ump->vfs_mountp->mnt_stat.f_mntonname; + mntonnamelen = strlen(mntonname); + + error = 0; + first = 1; + while (vattr.va_size - pathlen >= UDF_PATH_COMP_SIZE) { + len = UDF_PATH_COMP_SIZE; + memcpy(&pathcomp, pathpos, len); + l_ci = pathcomp.l_ci; + switch (pathcomp.type) { + case UDF_PATH_COMP_ROOT : + /* XXX should check for l_ci; bugcompatible now */ + if ((targetlen < 1) || !first) { + error = EINVAL; + break; + } + *targetpos++ = '/'; targetlen--; + break; + case UDF_PATH_COMP_MOUNTROOT : + /* XXX what should it be if l_ci > 0 ? [4/48.16.1.2] */ + if (l_ci || (targetlen < mntonnamelen+1) || !first) { + error = EINVAL; + break; + } + memcpy(targetpos, mntonname, mntonnamelen); + targetpos += mntonnamelen; targetlen -= mntonnamelen; + if (vattr.va_size-pathlen > UDF_PATH_COMP_SIZE+l_ci) { + /* more follows, so must be directory */ + *targetpos++ = '/'; targetlen--; + } + break; + case UDF_PATH_COMP_PARENTDIR : + /* XXX should check for l_ci; bugcompatible now */ + if (targetlen < 3) { + error = EINVAL; + break; + } + *targetpos++ = '.'; targetlen--; + *targetpos++ = '.'; targetlen--; + *targetpos++ = '/'; targetlen--; + break; + case UDF_PATH_COMP_CURDIR : + /* XXX should check for l_ci; bugcompatible now */ + if (targetlen < 2) { + error = EINVAL; + break; + } + *targetpos++ = '.'; targetlen--; + *targetpos++ = '/'; targetlen--; + break; + case UDF_PATH_COMP_NAME : + if (l_ci == 0) { + error = EINVAL; + break; + } + memset(tmpname, 0, PATH_MAX); + memcpy(&pathcomp, pathpos, len + l_ci); + udf_to_unix_name(tmpname, MAXPATHLEN, + pathcomp.ident, l_ci, + &udf_node->ump->logical_vol->desc_charset); + namelen = strlen(tmpname); + if (targetlen < namelen + 1) { + error = EINVAL; + break; + } + memcpy(targetpos, tmpname, namelen); + targetpos += namelen; targetlen -= namelen; + if (vattr.va_size-pathlen > UDF_PATH_COMP_SIZE+l_ci) { + /* more follows, so must be directory */ + *targetpos++ = '/'; targetlen--; + } + break; + default : + error = EINVAL; + break; + } + first = 0; + if (error) + break; + pathpos += UDF_PATH_COMP_SIZE + l_ci; + pathlen += UDF_PATH_COMP_SIZE + l_ci; + + } + /* all processed? */ + if (vattr.va_size - pathlen > 0) + error = EINVAL; + + /* uiomove() to destination */ + if (!error) + uiomove(targetbuf, PATH_MAX - targetlen, uio); + + free(pathbuf, M_UDFTEMP); + free(targetbuf, M_UDFTEMP); + free(tmpname, M_UDFTEMP); + + return error; +} + +/* --------------------------------------------------------------------- */ + +/* + * udf_rename() moved to udf_rename.c + */ + +/* --------------------------------------------------------------------- */ + +int +udf_remove(void *v) +{ + struct vop_remove_args /* { + struct vnode *a_dvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *ap = v; + struct vnode *dvp = ap->a_dvp; + struct vnode *vp = ap->a_vp; + struct componentname *cnp = ap->a_cnp; + struct udf_node *dir_node = VTOI(dvp); + struct udf_node *udf_node = VTOI(vp); + struct udf_mount *ump = dir_node->ump; + int error; + + DPRINTF(CALL, ("udf_remove called\n")); + if (vp->v_type != VDIR) { + error = udf_dir_detach(ump, dir_node, udf_node, cnp); + DPRINTFIF(NODE, error, ("\tgot error removing file\n")); + } else { + DPRINTF(NODE, ("\tis a directory: perm. denied\n")); + error = EPERM; + } + + if (error == 0) { + VN_KNOTE(vp, NOTE_DELETE); + VN_KNOTE(dvp, NOTE_WRITE); + } + + if (dvp == vp) + vrele(vp); + else + vput(vp); + vput(dvp); + + return error; +} + +/* --------------------------------------------------------------------- */ + +int +udf_rmdir(void *v) +{ + struct vop_rmdir_args /* { + struct vnode *a_dvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct vnode *dvp = ap->a_dvp; + struct componentname *cnp = ap->a_cnp; + struct udf_node *dir_node = VTOI(dvp); + struct udf_node *udf_node = VTOI(vp); + struct udf_mount *ump = dir_node->ump; + int error, isempty; + + DPRINTF(NOTIMPL, ("udf_rmdir '%s' called\n", cnp->cn_nameptr)); + + /* don't allow '.' to be deleted */ + if (dir_node == udf_node) { + vrele(dvp); + vput(vp); + return EINVAL; + } + + /* make sure our `leaf' node's hash is populated */ + dirhash_get(&udf_node->dir_hash); + error = udf_dirhash_fill(udf_node); + if (error) { + dirhash_put(udf_node->dir_hash); + return error; + } + + /* check to see if the directory is empty */ + isempty = dirhash_dir_isempty(udf_node->dir_hash); + dirhash_put(udf_node->dir_hash); + + if (!isempty) { + vput(dvp); + vput(vp); + return ENOTEMPTY; + } + + /* detach the node from the directory, udf_node is an empty dir here */ + error = udf_dir_detach(ump, dir_node, udf_node, cnp); + if (error == 0) { + cache_purge(vp); +// cache_purge(dvp); /* XXX from msdosfs, why? */ + /* + * Bug alert: we need to remove '..' from the detaching + * udf_node so further lookups of this are not possible. This + * prevents a process in a deleted directory from going to its + * deleted parent. Since `udf_node' is garanteed to be empty + * here, trunc it so no fids are there. + */ + dirhash_purge(&udf_node->dir_hash); + udf_shrink_node(udf_node, 0); + VN_KNOTE(vp, NOTE_DELETE); + } + DPRINTFIF(NODE, error, ("\tgot error removing dir\n")); + + /* unput the nodes and exit */ + vput(dvp); + vput(vp); + + return error; +} + +/* --------------------------------------------------------------------- */ + +int +udf_fsync(void *v) +{ + struct vop_fsync_args /* { + struct vnode *a_vp; + kauth_cred_t a_cred; + int a_flags; + off_t offlo; + off_t offhi; + struct proc *a_p; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct udf_node *udf_node = VTOI(vp); + int error, flags, wait; + + DPRINTF(SYNC, ("udf_fsync called on %p : %s, %s\n", + udf_node, + (ap->a_flags & FSYNC_WAIT) ? "wait":"no wait", + (ap->a_flags & FSYNC_DATAONLY) ? "data_only":"complete")); + + /* flush data and wait for it when requested */ + wait = (ap->a_flags & FSYNC_WAIT) ? UPDATE_WAIT : 0; + error = vflushbuf(vp, ap->a_flags); + if (error) + return error; + + if (udf_node == NULL) { + printf("udf_fsync() called on NULL udf_node!\n"); + return 0; + } + if (vp->v_tag != VT_UDF) { + printf("udf_fsync() called on node not tagged as UDF node!\n"); + return 0; + } + + /* set our times */ + udf_itimes(udf_node, NULL, NULL, NULL); + + /* if called when mounted readonly, never write back */ + if (vp->v_mount->mnt_flag & MNT_RDONLY) + return 0; + + /* if only data is requested, return */ + if (ap->a_flags & FSYNC_DATAONLY) + return 0; + + /* check if the node is dirty 'enough'*/ + flags = udf_node->i_flags & (IN_MODIFIED | IN_ACCESSED); + if (flags == 0) + return 0; + + /* if we don't have to wait, check for IO pending */ + if (!wait) { + if (vp->v_numoutput > 0) { + DPRINTF(SYNC, ("udf_fsync %p, rejecting on v_numoutput\n", udf_node)); + return 0; + } + if (udf_node->outstanding_bufs > 0) { + DPRINTF(SYNC, ("udf_fsync %p, rejecting on outstanding_bufs\n", udf_node)); + return 0; + } + if (udf_node->outstanding_nodedscr > 0) { + DPRINTF(SYNC, ("udf_fsync %p, rejecting on outstanding_nodedscr\n", udf_node)); + return 0; + } + } + + /* wait until vp->v_numoutput reaches zero i.e. is finished */ + if (wait) { + DPRINTF(SYNC, ("udf_fsync %p, waiting\n", udf_node)); + mutex_enter(vp->v_interlock); + while (vp->v_numoutput) { + DPRINTF(SYNC, ("udf_fsync %p, v_numoutput %d\n", udf_node, vp->v_numoutput)); + cv_timedwait(&vp->v_cv, vp->v_interlock, hz/8); + } + mutex_exit(vp->v_interlock); + DPRINTF(SYNC, ("udf_fsync %p, fin wait\n", udf_node)); + } + + /* write out node and wait for it if requested */ + DPRINTF(SYNC, ("udf_fsync %p, writeout node\n", udf_node)); + error = udf_writeout_node(udf_node, wait); + if (error) + return error; + + /* TODO/XXX if ap->a_flags & FSYNC_CACHE, we ought to do a disc sync */ + + return 0; +} + +/* --------------------------------------------------------------------- */ + +int +udf_advlock(void *v) +{ + struct vop_advlock_args /* { + struct vnode *a_vp; + void *a_id; + int a_op; + struct flock *a_fl; + int a_flags; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct udf_node *udf_node = VTOI(vp); + struct file_entry *fe; + struct extfile_entry *efe; + uint64_t file_size; + + DPRINTF(LOCKING, ("udf_advlock called\n")); + + /* get directory filesize */ + if (udf_node->fe) { + fe = udf_node->fe; + file_size = udf_rw64(fe->inf_len); + } else { + assert(udf_node->efe); + efe = udf_node->efe; + file_size = udf_rw64(efe->inf_len); + } + + return lf_advlock(ap, &udf_node->lockf, file_size); +} + +/* --------------------------------------------------------------------- */ + +/* Global vfs vnode data structures for udfs */ +int (**udf_vnodeop_p)(void *); + +const struct vnodeopv_entry_desc udf_vnodeop_entries[] = { + { &vop_default_desc, vn_default_error }, + { &vop_lookup_desc, udf_lookup }, /* lookup */ + { &vop_create_desc, udf_create }, /* create */ + { &vop_mknod_desc, udf_mknod }, /* mknod */ /* TODO */ + { &vop_open_desc, udf_open }, /* open */ + { &vop_close_desc, udf_close }, /* close */ + { &vop_access_desc, udf_access }, /* access */ + { &vop_getattr_desc, udf_getattr }, /* getattr */ + { &vop_setattr_desc, udf_setattr }, /* setattr */ /* TODO chflags */ + { &vop_read_desc, udf_read }, /* read */ + { &vop_write_desc, udf_write }, /* write */ /* WRITE */ + { &vop_fcntl_desc, genfs_fcntl }, /* fcntl */ /* TODO? */ + { &vop_ioctl_desc, genfs_enoioctl }, /* ioctl */ /* TODO? */ + { &vop_poll_desc, genfs_poll }, /* poll */ /* TODO/OK? */ + { &vop_kqfilter_desc, genfs_kqfilter }, /* kqfilter */ /* ? */ + { &vop_revoke_desc, genfs_revoke }, /* revoke */ /* TODO? */ + { &vop_mmap_desc, genfs_mmap }, /* mmap */ /* OK? */ + { &vop_fsync_desc, udf_fsync }, /* fsync */ + { &vop_seek_desc, genfs_seek }, /* seek */ + { &vop_remove_desc, udf_remove }, /* remove */ + { &vop_link_desc, udf_link }, /* link */ /* TODO */ + { &vop_rename_desc, udf_rename }, /* rename */ /* TODO */ + { &vop_mkdir_desc, udf_mkdir }, /* mkdir */ + { &vop_rmdir_desc, udf_rmdir }, /* rmdir */ + { &vop_symlink_desc, udf_symlink }, /* symlink */ /* TODO */ + { &vop_readdir_desc, udf_readdir }, /* readdir */ + { &vop_readlink_desc, udf_readlink }, /* readlink */ /* TEST ME */ + { &vop_abortop_desc, genfs_abortop }, /* abortop */ /* TODO/OK? */ + { &vop_inactive_desc, udf_inactive }, /* inactive */ + { &vop_reclaim_desc, udf_reclaim }, /* reclaim */ + { &vop_lock_desc, genfs_lock }, /* lock */ + { &vop_unlock_desc, genfs_unlock }, /* unlock */ + { &vop_bmap_desc, udf_trivial_bmap }, /* bmap */ /* 1:1 bmap */ + { &vop_strategy_desc, udf_vfsstrategy },/* strategy */ +/* { &vop_print_desc, udf_print }, */ /* print */ + { &vop_islocked_desc, genfs_islocked }, /* islocked */ + { &vop_pathconf_desc, udf_pathconf }, /* pathconf */ + { &vop_advlock_desc, udf_advlock }, /* advlock */ /* TEST ME */ + { &vop_bwrite_desc, vn_bwrite }, /* bwrite */ /* ->strategy */ + { &vop_getpages_desc, genfs_getpages }, /* getpages */ + { &vop_putpages_desc, genfs_putpages }, /* putpages */ + { NULL, NULL } +}; + + +const struct vnodeopv_desc udf_vnodeop_opv_desc = { + &udf_vnodeop_p, udf_vnodeop_entries +}; diff --git a/sys/fs/v7fs/Makefile b/sys/fs/v7fs/Makefile new file mode 100644 index 000000000..e677fa395 --- /dev/null +++ b/sys/fs/v7fs/Makefile @@ -0,0 +1,7 @@ +# $NetBSD: Makefile,v 1.1 2011/06/27 11:52:24 uch Exp $ + +INCSDIR= /usr/include/fs/v7fs + +INCS= v7fs.h v7fs_args.h + +.include diff --git a/sys/fs/v7fs/files.v7fs b/sys/fs/v7fs/files.v7fs new file mode 100644 index 000000000..509253f84 --- /dev/null +++ b/sys/fs/v7fs/files.v7fs @@ -0,0 +1,22 @@ +# $NetBSD: files.v7fs,v 1.1 2011/06/27 11:52:24 uch Exp $ + +deffs V7FS +defflag opt_v7fs.h V7FS_EI + +# Core. OS independent. These files are used by userland program.(fsck,newfs) +file fs/v7fs/v7fs_endian.c v7fs +file fs/v7fs/v7fs_superblock.c v7fs +file fs/v7fs/v7fs_inode.c v7fs +file fs/v7fs/v7fs_dirent.c v7fs +file fs/v7fs/v7fs_datablock.c v7fs +file fs/v7fs/v7fs_file.c v7fs +file fs/v7fs/v7fs_io.c v7fs +# util. +file fs/v7fs/v7fs_file_util.c v7fs +file fs/v7fs/v7fs_inode_util.c v7fs +file fs/v7fs/v7fs_superblock_util.c v7fs +# OS glue +file fs/v7fs/v7fs_io_kern.c v7fs +file fs/v7fs/v7fs_extern.c v7fs +file fs/v7fs/v7fs_vnops.c v7fs +file fs/v7fs/v7fs_vfsops.c v7fs diff --git a/sys/fs/v7fs/v7fs.h b/sys/fs/v7fs/v7fs.h new file mode 100644 index 000000000..c35bacecc --- /dev/null +++ b/sys/fs/v7fs/v7fs.h @@ -0,0 +1,180 @@ +/* $NetBSD: v7fs.h,v 1.2 2011/07/16 12:35:40 uch Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _V7FS_H_ +/* 7th Edition of Unix(PDP-11) Filesystem definition. */ +#define _V7FS_H_ +#include +#ifndef _KERNEL +#include +#endif +/* + * V7 File System + * + * +------------------ + * |Boot block (512byte) sector [0] + * | + * +------------------ + * |Super block (512byte) sector [1] + * | + * +------------------ + * |v7fs_inode(64byte sector [2] + * . + * . + * | + * +------------------ + * |data block sector [datablock_start_sector] + * | + * . + * . + * | + * +------------------ + * <- [sector volume_size] + * + * | + * +------------------ volume size. + * + * Max volume size is 8GB (24bit daddr_t) + * Max file size is ~1GB + * + */ + +/* V7 type. */ +typedef uint16_t v7fs_ino_t; +typedef uint32_t v7fs_daddr_t; +typedef int32_t v7fs_time_t; +typedef uint32_t v7fs_off_t; +typedef uint16_t v7fs_dev_t; +typedef uint16_t v7fs_mode_t; +#define V7FS_DADDR_MAX 0x00ffffff +#define V7FS_INODE_MAX 0xffff + +#define V7FS_BSIZE 512 +#define V7FS_BSHIFT 9 +#define V7FS_ROUND_BSIZE(x) \ + ((((x) + (V7FS_BSIZE - 1)) & ~(V7FS_BSIZE - 1))) +#define V7FS_TRUNC_BSIZE(x) ((x) & ~(V7FS_BSIZE - 1)) + +#define V7FS_RESIDUE_BSIZE(x) \ + ((x) - ((((x) - 1) >> V7FS_BSHIFT) << V7FS_BSHIFT)) + +/* Disk location. */ +#define V7FS_BOOTBLOCK_SECTOR 0 +#define V7FS_SUPERBLOCK_SECTOR 1 +#define V7FS_ILIST_SECTOR 2 + +/* Superblock */ +/* cache. */ +#define V7FS_MAX_FREEBLOCK 50 +#define V7FS_MAX_FREEINODE 100 +struct v7fs_superblock { + /* [3 ... (datablock_start_sector-1)]are ilist */ + uint16_t datablock_start_sector; + v7fs_daddr_t volume_size; + int16_t nfreeblock; /* # of freeblock in superblock cache. */ + v7fs_daddr_t freeblock[V7FS_MAX_FREEBLOCK]; /* cache. */ + int16_t nfreeinode; /* # of free inode in superblock cache. */ + v7fs_ino_t freeinode[V7FS_MAX_FREEINODE]; /* cache. */ + int8_t lock_freeblock; + int8_t lock_freeinode; + int8_t modified; + int8_t readonly; + v7fs_time_t update_time; + v7fs_daddr_t total_freeblock; + v7fs_ino_t total_freeinode; +} __packed; + +/* Datablock */ +#define V7FS_NADDR 13 +#define V7FS_NADDR_DIRECT 10 +#define V7FS_NADDR_INDEX1 10 +#define V7FS_NADDR_INDEX2 11 +#define V7FS_NADDR_INDEX3 12 +/* daddr index. */ +#define V7FS_DADDR_PER_BLOCK (V7FS_BSIZE / sizeof(v7fs_daddr_t)) +struct v7fs_freeblock { + int16_t nfreeblock; + v7fs_daddr_t freeblock[V7FS_MAX_FREEBLOCK]; +} __packed; + + +/* Dirent */ +#define V7FS_NAME_MAX 14 +#define V7FS_PATH_MAX PATH_MAX /* No V7 limit. */ +#define V7FS_LINK_MAX LINK_MAX /* No V7 limit. */ +struct v7fs_dirent { + v7fs_ino_t inode_number; + char name[V7FS_NAME_MAX]; +} __packed; /*16byte */ + +/* Inode */ +#define V7FS_BALBLK_INODE 1 /* monument */ +#define V7FS_ROOT_INODE 2 +#define V7FS_MAX_INODE(s) \ + (((s)->datablock_start_sector - V7FS_ILIST_SECTOR) * \ + V7FS_BSIZE / sizeof(struct v7fs_inode_diskimage)) +#define V7FS_INODE_PER_BLOCK \ + (V7FS_BSIZE / sizeof(struct v7fs_inode_diskimage)) +#define V7FS_ILISTBLK_MAX (V7FS_INODE_MAX / V7FS_INODE_PER_BLOCK) + +struct v7fs_inode_diskimage { + int16_t mode; + int16_t nlink; /* [DIR] # of child directories. [REG] link count. */ + int16_t uid; + int16_t gid; + v7fs_off_t filesize; /* byte */ +#define V7FS_DINODE_ADDR_LEN 40 + /* 39 used; 13 addresses of 3 byte each. */ + uint8_t addr[V7FS_DINODE_ADDR_LEN]; + /*for device node: addr[0] is major << 8 | minor. */ + v7fs_time_t atime; + v7fs_time_t mtime; + v7fs_time_t ctime; +} __packed; /*64byte */ + +/* File type */ +#define V7FS_IFMT 0170000 /* File type mask */ +#define V7FS_IFCHR 0020000 /* charcter device */ +#define V7FS_IFDIR 0040000 /* directory */ +#define V7FS_IFBLK 0060000 /* block device */ +#define V7FS_IFREG 0100000 /* file. */ +/* Obsoleted file type. */ +#define V7FS_IFMPC 0030000 /* multiplexed char special */ +#define V7FS_IFMPB 0070000 /* multiplexed block special */ +/* Don't apear original V7 filesystem. Found at 2.10BSD. */ +#define V7FSBSD_IFLNK 0120000 /* symbolic link */ +#define V7FSBSD_IFSOCK 0140000 /* socket */ +/* Don't apear original V7 filesystem. NetBSD. */ +#define V7FSBSD_IFFIFO 0010000 /* Named pipe. */ + +#define V7FSBSD_MAXSYMLINKLEN V7FS_BSIZE + +#endif /*!_V7FS_H_ */ diff --git a/sys/fs/v7fs/v7fs_args.h b/sys/fs/v7fs/v7fs_args.h new file mode 100644 index 000000000..017518cba --- /dev/null +++ b/sys/fs/v7fs/v7fs_args.h @@ -0,0 +1,40 @@ +/* $NetBSD: v7fs_args.h,v 1.1 2011/06/27 11:52:24 uch Exp $ */ + +/*- + * Copyright (c) 2004, 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FS_V7FS_V7FS_ARGS_H_ +#define _FS_V7FS_V7FS_ARGS_H_ + +struct v7fs_args { + char *fspec; /* blocks special holding the fs to mount */ + int endian; /* target filesystem endian */ +}; + +#endif /* _FS_V7FS_V7FS_ARGS_H_ */ diff --git a/sys/fs/v7fs/v7fs_datablock.c b/sys/fs/v7fs/v7fs_datablock.c new file mode 100644 index 000000000..cd8452159 --- /dev/null +++ b/sys/fs/v7fs/v7fs_datablock.c @@ -0,0 +1,729 @@ +/* $NetBSD: v7fs_datablock.c,v 1.5 2011/08/14 09:02:07 apb Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: v7fs_datablock.c,v 1.5 2011/08/14 09:02:07 apb Exp $"); +#if defined _KERNEL_OPT +#include "opt_v7fs.h" +#endif + +#include +#ifdef _KERNEL +#include +#include +#else +#include +#include +#include +#endif + +#include "v7fs.h" +#include "v7fs_impl.h" +#include "v7fs_endian.h" +#include "v7fs_inode.h" +#include "v7fs_datablock.h" +#include "v7fs_superblock.h" + +#ifdef V7FS_DATABLOCK_DEBUG +#define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) +#else +#define DPRINTF(fmt, args...) ((void)0) +#endif + +static int v7fs_datablock_deallocate(struct v7fs_self *, v7fs_daddr_t); +static int v7fs_loop1(struct v7fs_self *, v7fs_daddr_t, size_t *, + int (*)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *); +static int v7fs_loop2(struct v7fs_self *, v7fs_daddr_t, size_t *, + int (*)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *); +static v7fs_daddr_t v7fs_link(struct v7fs_self *, v7fs_daddr_t, int); +static v7fs_daddr_t v7fs_add_leaf(struct v7fs_self *, v7fs_daddr_t, int); +static v7fs_daddr_t v7fs_unlink(struct v7fs_self *, v7fs_daddr_t, int); +static v7fs_daddr_t v7fs_remove_leaf(struct v7fs_self *, v7fs_daddr_t, int); +static v7fs_daddr_t v7fs_remove_self(struct v7fs_self *, v7fs_daddr_t); + +#ifdef V7FS_DATABLOCK_DEBUG +void daddr_map_dump(const struct v7fs_daddr_map *); +#else +#define daddr_map_dump(x) ((void)0) +#endif + +bool +datablock_number_sanity(const struct v7fs_self *fs, v7fs_daddr_t blk) +{ + const struct v7fs_superblock *sb = &fs->superblock; + bool ok = (blk >= sb->datablock_start_sector) && + (blk < sb->volume_size); + +#ifdef V7FS_DATABLOCK_DEBUG + if (!ok) { + DPRINTF("Bad data block #%d\n", blk); + } +#endif + + return ok; +} + +int +v7fs_datablock_allocate(struct v7fs_self *fs, v7fs_daddr_t *block_number) +{ + struct v7fs_superblock *sb = &fs->superblock; + v7fs_daddr_t blk; + int error = 0; + + *block_number = 0; + SUPERB_LOCK(fs); + do { + if (!sb->total_freeblock) { + DPRINTF("free block exhausted!!!\n"); + SUPERB_UNLOCK(fs); + return ENOSPC; + } + + /* Get free block from superblock cache. */ + blk = sb->freeblock[--sb->nfreeblock]; + sb->total_freeblock--; + sb->modified = 1; + + /* If nfreeblock is zero, it block is next freeblock link. */ + if (sb->nfreeblock == 0) { + if ((error = v7fs_freeblock_update(fs, blk))) { + DPRINTF("no freeblock!!!\n"); + SUPERB_UNLOCK(fs); + return error; + } + /* This freeblock link is no longer required. */ + /* use as data block. */ + } + } while (!datablock_number_sanity(fs, blk)); /* skip bogus block. */ + SUPERB_UNLOCK(fs); + + DPRINTF("Get freeblock %d\n", blk); + /* Zero clear datablock. */ + void *buf; + if (!(buf = scratch_read(fs, blk))) + return EIO; + memset(buf, 0, V7FS_BSIZE); + if (!fs->io.write(fs->io.cookie, buf, blk)) + error = EIO; + scratch_free(fs, buf); + + if (error == 0) + *block_number = blk; + + return error; +} + +static int +v7fs_datablock_deallocate(struct v7fs_self *fs, v7fs_daddr_t blk) +{ + struct v7fs_superblock *sb = &fs->superblock; + void *buf; + int error = 0; + + if (!datablock_number_sanity(fs, blk)) + return EIO; + + /* Add to in-core freelist. */ + SUPERB_LOCK(fs); + if (sb->nfreeblock < V7FS_MAX_FREEBLOCK) { + sb->freeblock[sb->nfreeblock++] = blk; + sb->total_freeblock++; + sb->modified = 1; + DPRINTF("n_freeblock=%d\n", sb->total_freeblock); + SUPERB_UNLOCK(fs); + return 0; + } + + /* No space to push. */ + /* Make this block to freeblock list.and current cache moved to this. */ + if (!(buf = scratch_read(fs, blk))) { + SUPERB_UNLOCK(fs); + return EIO; /* Fatal */ + } + + struct v7fs_freeblock *fb = (struct v7fs_freeblock *)buf; + fb->nfreeblock = V7FS_MAX_FREEBLOCK; + int i; + for (i = 0; i < V7FS_MAX_FREEBLOCK; i++) + fb->freeblock[i] = V7FS_VAL32(fs, sb->freeblock[i]); + + if (!fs->io.write(fs->io.cookie, (uint8_t *)fb, blk)) { + error = EIO; /* Fatal */ + } else { + /* Link. on next allocate, this block is used as datablock, */ + /* and swap outed freeblock list is restored. */ + sb->freeblock[0] = blk; + sb->nfreeblock = 1; + sb->total_freeblock++; + sb->modified = 1; + DPRINTF("n_freeblock=%d\n", sb->total_freeblock); + } + SUPERB_UNLOCK(fs); + scratch_free(fs, buf); + + return error; +} + +int +v7fs_datablock_addr(size_t sz, struct v7fs_daddr_map *map) +{ +#define NIDX V7FS_DADDR_PER_BLOCK +#define DIRECT_SZ (V7FS_NADDR_DIRECT * V7FS_BSIZE) +#define IDX1_SZ (NIDX * V7FS_BSIZE) +#define IDX2_SZ (NIDX * NIDX * V7FS_BSIZE) +#define ROUND(x, a) ((((x) + ((a) - 1)) & ~((a) - 1))) + if (!sz) { + map->level = 0; + map->index[0] = 0; + return 0; + } + + sz = V7FS_ROUND_BSIZE(sz); + + /* Direct */ + if (sz <= DIRECT_SZ) { + map->level = 0; + map->index[0] = (sz >> V7FS_BSHIFT) - 1; + return 0; + } + /* Index 1 */ + sz -= DIRECT_SZ; + + if (sz <= IDX1_SZ) { + map->level = 1; + map->index[0] = (sz >> V7FS_BSHIFT) - 1; + return 0; + } + sz -= IDX1_SZ; + + /* Index 2 */ + if (sz <= IDX2_SZ) { + map->level = 2; + map->index[0] = ROUND(sz, IDX1_SZ) / IDX1_SZ - 1; + map->index[1] = ((sz - (map->index[0] * IDX1_SZ)) >> + V7FS_BSHIFT) - 1; + return 0; + } + sz -= IDX2_SZ; + + /* Index 3 */ + map->level = 3; + map->index[0] = ROUND(sz, IDX2_SZ) / IDX2_SZ - 1; + sz -= map->index[0] * IDX2_SZ; + map->index[1] = ROUND(sz, IDX1_SZ) / IDX1_SZ - 1; + sz -= map->index[1] * IDX1_SZ; + map->index[2] = (sz >> V7FS_BSHIFT) - 1; + + return map->index[2] >= NIDX ? ENOSPC : 0; +} + +int +v7fs_datablock_foreach(struct v7fs_self *fs, struct v7fs_inode *p, + int (*func)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *ctx) +{ + size_t i; + v7fs_daddr_t blk, blk2; + size_t filesize; + bool last; + int ret; + + if (!(filesize = v7fs_inode_filesize(p))) + return V7FS_ITERATOR_END; +#ifdef V7FS_DATABLOCK_DEBUG + size_t sz = filesize; +#endif + + /* Direct */ + for (i = 0; i < V7FS_NADDR_DIRECT; i++, filesize -= V7FS_BSIZE) { + blk = p->addr[i]; + if (!datablock_number_sanity(fs, blk)) { + DPRINTF("inode#%d direct=%zu filesize=%zu\n", + p->inode_number, i, sz); + return EIO; + } + + last = filesize <= V7FS_BSIZE; + if ((ret = func(fs, ctx, blk, last ? filesize : V7FS_BSIZE))) + return ret; + if (last) + return V7FS_ITERATOR_END; + } + + /* Index 1 */ + blk = p->addr[V7FS_NADDR_INDEX1]; + if (!datablock_number_sanity(fs, blk)) + return EIO; + + if ((ret = v7fs_loop1(fs, blk, &filesize, func, ctx))) + return ret; + + /* Index 2 */ + blk = p->addr[V7FS_NADDR_INDEX2]; + if (!datablock_number_sanity(fs, blk)) + return EIO; + + if ((ret = v7fs_loop2(fs, blk, &filesize, func, ctx))) + return ret; + + /* Index 3 */ + blk = p->addr[V7FS_NADDR_INDEX3]; + if (!datablock_number_sanity(fs, blk)) + return EIO; + + for (i = 0; i < V7FS_DADDR_PER_BLOCK; i++) { + blk2 = v7fs_link(fs, blk, i); + if (!datablock_number_sanity(fs, blk)) + return EIO; + + if ((ret = v7fs_loop2(fs, blk2, &filesize, func, ctx))) + return ret; + } + + return EFBIG; +} + +static int +v7fs_loop2(struct v7fs_self *fs, v7fs_daddr_t listblk, size_t *filesize, + int (*func)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *ctx) +{ + v7fs_daddr_t blk; + int ret; + size_t j; + + for (j = 0; j < V7FS_DADDR_PER_BLOCK; j++) { + blk = v7fs_link(fs, listblk, j); + if (!datablock_number_sanity(fs, blk)) + return EIO; + if ((ret = v7fs_loop1(fs, blk, filesize, func, ctx))) + return ret; + } + + return 0; +} + +static int +v7fs_loop1(struct v7fs_self *fs, v7fs_daddr_t listblk, size_t *filesize, + int (*func)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *ctx) +{ + v7fs_daddr_t blk; + bool last; + int ret; + size_t k; + + for (k = 0; k < V7FS_DADDR_PER_BLOCK; k++, *filesize -= V7FS_BSIZE) { + blk = v7fs_link(fs, listblk, k); + if (!datablock_number_sanity(fs, blk)) + return EIO; + last = *filesize <= V7FS_BSIZE; + if ((ret = func(fs, ctx, blk, last ? *filesize : V7FS_BSIZE))) + return ret; + if (last) + return V7FS_ITERATOR_END; + } + + return 0; +} + +v7fs_daddr_t +v7fs_datablock_last(struct v7fs_self *fs, struct v7fs_inode *inode, + v7fs_off_t ofs) +{ + struct v7fs_daddr_map map; + v7fs_daddr_t blk = 0; + v7fs_daddr_t *addr = inode->addr; + + /* Inquire last data block location. */ + if (v7fs_datablock_addr(ofs, &map) != 0) + return 0; + + switch (map.level) + { + case 0: /*Direct */ + blk = inode->addr[map.index[0]]; + break; + case 1: /*Index1 */ + blk = v7fs_link(fs, addr[V7FS_NADDR_INDEX1], map.index[0]); + break; + case 2: /*Index2 */ + blk = v7fs_link(fs, v7fs_link(fs, + addr[V7FS_NADDR_INDEX2], map.index[0]), map.index[1]); + break; + case 3: /*Index3 */ + blk = v7fs_link(fs, v7fs_link(fs, v7fs_link(fs, + addr[V7FS_NADDR_INDEX3], map.index[0]), map.index[1]), + map.index[2]); + break; + } + + return blk; +} + +int +v7fs_datablock_expand(struct v7fs_self *fs, struct v7fs_inode *inode, size_t sz) +{ + size_t old_filesize = inode->filesize; + size_t new_filesize = old_filesize + sz; + struct v7fs_daddr_map oldmap, newmap; + v7fs_daddr_t blk, idxblk; + int error; + v7fs_daddr_t old_nblk = V7FS_ROUND_BSIZE(old_filesize) >> V7FS_BSHIFT; + v7fs_daddr_t new_nblk = V7FS_ROUND_BSIZE(new_filesize) >> V7FS_BSHIFT; + + if (old_nblk == new_nblk) { + inode->filesize += sz; + v7fs_inode_writeback(fs, inode); + return 0; /* no need to expand. */ + } + struct v7fs_inode backup = *inode; + v7fs_daddr_t required_blk = new_nblk - old_nblk; + + DPRINTF("%zu->%zu, required block=%d\n", old_filesize, new_filesize, + required_blk); + + v7fs_datablock_addr(old_filesize, &oldmap); + v7fs_daddr_t i; + for (i = 0; i < required_blk; i++) { + v7fs_datablock_addr(old_filesize + (i+1) * V7FS_BSIZE, &newmap); + daddr_map_dump(&oldmap); + daddr_map_dump(&newmap); + + if (oldmap.level != newmap.level) { + /* Allocate index area */ + if ((error = v7fs_datablock_allocate(fs, &idxblk))) + return error; + + switch (newmap.level) { + case 1: + DPRINTF("0->1\n"); + inode->addr[V7FS_NADDR_INDEX1] = idxblk; + blk = v7fs_add_leaf(fs, idxblk, 0); + break; + case 2: + DPRINTF("1->2\n"); + inode->addr[V7FS_NADDR_INDEX2] = idxblk; + blk = v7fs_add_leaf(fs, v7fs_add_leaf(fs, + idxblk, 0), 0); + break; + case 3: + DPRINTF("2->3\n"); + inode->addr[V7FS_NADDR_INDEX3] = idxblk; + blk = v7fs_add_leaf(fs, v7fs_add_leaf(fs, + v7fs_add_leaf(fs, idxblk, 0), 0), 0); + break; + } + } else { + switch (newmap.level) { + case 0: + if ((error = v7fs_datablock_allocate(fs, &blk))) + return error; + inode->addr[newmap.index[0]] = blk; + DPRINTF("direct index %d = blk%d\n", + newmap.index[0], blk); + break; + case 1: + idxblk = inode->addr[V7FS_NADDR_INDEX1]; + blk = v7fs_add_leaf(fs, idxblk, + newmap.index[0]); + break; + case 2: + idxblk = inode->addr[V7FS_NADDR_INDEX2]; + if (oldmap.index[0] != newmap.index[0]) { + v7fs_add_leaf(fs, idxblk, + newmap.index[0]); + } + blk = v7fs_add_leaf(fs, v7fs_link(fs,idxblk, + newmap.index[0]), newmap.index[1]); + break; + case 3: + idxblk = inode->addr[V7FS_NADDR_INDEX3]; + + if (oldmap.index[0] != newmap.index[0]) { + v7fs_add_leaf(fs, idxblk, + newmap.index[0]); + } + + if (oldmap.index[1] != newmap.index[1]) { + v7fs_add_leaf(fs, v7fs_link(fs, idxblk, + newmap.index[0]), newmap.index[1]); + } + blk = v7fs_add_leaf(fs, v7fs_link(fs, + v7fs_link(fs, idxblk, newmap.index[0]), + newmap.index[1]), newmap.index[2]); + break; + } + } + if (!blk) { + *inode = backup; /* structure copy; */ + return ENOSPC; + } + oldmap = newmap; + } + inode->filesize += sz; + v7fs_inode_writeback(fs, inode); + + return 0; +} + +static v7fs_daddr_t +v7fs_link(struct v7fs_self *fs, v7fs_daddr_t listblk, int n) +{ + v7fs_daddr_t *list; + v7fs_daddr_t blk; + void *buf; + + if (!datablock_number_sanity(fs, listblk)) + return 0; + if (!(buf = scratch_read(fs, listblk))) + return 0; + list = (v7fs_daddr_t *)buf; + blk = V7FS_VAL32(fs, list[n]); + scratch_free(fs, buf); + + if (!datablock_number_sanity(fs, blk)) + return 0; + + return blk; +} + +static v7fs_daddr_t +v7fs_add_leaf(struct v7fs_self *fs, v7fs_daddr_t up, int idx) +{ + v7fs_daddr_t newblk; + v7fs_daddr_t *daddr_list; + int error = 0; + void *buf; + + if (!up) + return 0; + if (!datablock_number_sanity(fs, up)) + return 0; + + if ((error = v7fs_datablock_allocate(fs, &newblk))) + return 0; + if (!(buf = scratch_read(fs, up))) + return 0; + daddr_list = (v7fs_daddr_t *)buf; + daddr_list[idx] = V7FS_VAL32(fs, newblk); + if (!fs->io.write(fs->io.cookie, buf, up)) + newblk = 0; + scratch_free(fs, buf); + + return newblk; +} + +int +v7fs_datablock_contract(struct v7fs_self *fs, struct v7fs_inode *inode, + size_t sz) +{ + size_t old_filesize = inode->filesize; + size_t new_filesize = old_filesize - sz; + struct v7fs_daddr_map oldmap, newmap; + v7fs_daddr_t blk, idxblk; + int error = 0; + v7fs_daddr_t old_nblk = V7FS_ROUND_BSIZE(old_filesize) >> V7FS_BSHIFT; + v7fs_daddr_t new_nblk = V7FS_ROUND_BSIZE(new_filesize) >> V7FS_BSHIFT; + + if (old_nblk == new_nblk) { + inode->filesize -= sz; + v7fs_inode_writeback(fs, inode); + return 0; /* no need to contract; */ + } + v7fs_daddr_t erase_blk = old_nblk - new_nblk; + + DPRINTF("%zu->%zu # of erased block=%d\n", old_filesize, new_filesize, + erase_blk); + + v7fs_datablock_addr(old_filesize, &oldmap); + v7fs_daddr_t i; + for (i = 0; i < erase_blk; i++) { + v7fs_datablock_addr(old_filesize - (i+1) * V7FS_BSIZE, &newmap); + + if (oldmap.level != newmap.level) { + switch (newmap.level) { + case 0: /*1->0 */ + DPRINTF("1->0\n"); + idxblk = inode->addr[V7FS_NADDR_INDEX1]; + inode->addr[V7FS_NADDR_INDEX1] = 0; + error = v7fs_datablock_deallocate(fs, + v7fs_remove_self(fs, idxblk)); + break; + case 1: /*2->1 */ + DPRINTF("2->1\n"); + idxblk = inode->addr[V7FS_NADDR_INDEX2]; + inode->addr[V7FS_NADDR_INDEX2] = 0; + error = v7fs_datablock_deallocate(fs, + v7fs_remove_self(fs, v7fs_remove_self(fs, + idxblk))); + break; + case 2:/*3->2 */ + DPRINTF("3->2\n"); + idxblk = inode->addr[V7FS_NADDR_INDEX3]; + inode->addr[V7FS_NADDR_INDEX3] = 0; + error = v7fs_datablock_deallocate(fs, + v7fs_remove_self(fs, v7fs_remove_self(fs, + v7fs_remove_self(fs, idxblk)))); + break; + } + } else { + switch (newmap.level) { + case 0: + DPRINTF("[0] %d\n", oldmap.index[0]); + blk = inode->addr[oldmap.index[0]]; + error = v7fs_datablock_deallocate(fs, blk); + break; + case 1: + DPRINTF("[1] %d\n", oldmap.index[0]); + idxblk = inode->addr[V7FS_NADDR_INDEX1]; + v7fs_remove_leaf(fs, idxblk, oldmap.index[0]); + + break; + case 2: + DPRINTF("[2] %d %d\n", oldmap.index[0], + oldmap.index[1]); + idxblk = inode->addr[V7FS_NADDR_INDEX2]; + v7fs_remove_leaf(fs, v7fs_link(fs, idxblk, + oldmap.index[0]), oldmap.index[1]); + if (oldmap.index[0] != newmap.index[0]) { + v7fs_remove_leaf(fs, idxblk, + oldmap.index[0]); + } + break; + case 3: + DPRINTF("[2] %d %d %d\n", oldmap.index[0], + oldmap.index[1], oldmap.index[2]); + idxblk = inode->addr[V7FS_NADDR_INDEX3]; + v7fs_remove_leaf(fs, v7fs_link(fs, + v7fs_link(fs, idxblk, oldmap.index[0]), + oldmap.index[1]), oldmap.index[2]); + + if (oldmap.index[1] != newmap.index[1]) { + v7fs_remove_leaf(fs, v7fs_link(fs, + idxblk, oldmap.index[0]), + oldmap.index[1]); + } + if (oldmap.index[0] != newmap.index[0]) { + v7fs_remove_leaf(fs, idxblk, + oldmap.index[0]); + } + break; + } + } + oldmap = newmap; + } + inode->filesize -= sz; + v7fs_inode_writeback(fs, inode); + + return error; +} + +static v7fs_daddr_t +v7fs_unlink(struct v7fs_self *fs, v7fs_daddr_t idxblk, int n) +{ + v7fs_daddr_t *daddr_list; + v7fs_daddr_t blk; + void *buf; + + if (!(buf = scratch_read(fs, idxblk))) + return 0; + daddr_list = (v7fs_daddr_t *)buf; + blk = V7FS_VAL32(fs, daddr_list[n]); + daddr_list[n] = 0; + fs->io.write(fs->io.cookie, buf, idxblk); + scratch_free(fs, buf); + + return blk; /* unlinked block. */ +} + +static v7fs_daddr_t +v7fs_remove_self(struct v7fs_self *fs, v7fs_daddr_t up) +{ + v7fs_daddr_t down; + + if (!datablock_number_sanity(fs, up)) + return 0; + + /* At 1st, remove from datablock list. */ + down = v7fs_unlink(fs, up, 0); + + /* link self to freelist. */ + v7fs_datablock_deallocate(fs, up); + + return down; +} + +static v7fs_daddr_t +v7fs_remove_leaf(struct v7fs_self *fs, v7fs_daddr_t up, int n) +{ + v7fs_daddr_t down; + + if (!datablock_number_sanity(fs, up)) + return 0; + + /* At 1st, remove from datablock list. */ + down = v7fs_unlink(fs, up, n); + + /* link leaf to freelist. */ + v7fs_datablock_deallocate(fs, down); + + return down; +} + +int +v7fs_datablock_size_change(struct v7fs_self *fs, size_t newsz, + struct v7fs_inode *inode) +{ + ssize_t diff = newsz - v7fs_inode_filesize(inode); + int error = 0; + + if (diff > 0) + error = v7fs_datablock_expand(fs, inode, diff); + else if (diff < 0) + error = v7fs_datablock_contract(fs, inode, -diff); + + return error; +} + +#ifdef V7FS_DATABLOCK_DEBUG +void +daddr_map_dump(const struct v7fs_daddr_map *map) +{ + + DPRINTF("level %d ", map->level); + int m, n = !map->level ? 1 : map->level; + for (m = 0; m < n; m++) + printf("[%d]", map->index[m]); + printf("\n"); +} +#endif diff --git a/sys/fs/v7fs/v7fs_datablock.h b/sys/fs/v7fs/v7fs_datablock.h new file mode 100644 index 000000000..42a45adef --- /dev/null +++ b/sys/fs/v7fs/v7fs_datablock.h @@ -0,0 +1,52 @@ +/* $NetBSD: v7fs_datablock.h,v 1.2 2011/07/16 12:35:32 uch Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _V7FS_DATABLOCK_H_ +#define _V7FS_DATABLOCK_H_ + +__BEGIN_DECLS +bool datablock_number_sanity(const struct v7fs_self *, v7fs_daddr_t); +int v7fs_datablock_allocate(struct v7fs_self *, v7fs_daddr_t *); +int v7fs_datablock_foreach(struct v7fs_self *, struct v7fs_inode *, + int (*)(struct v7fs_self *, void *, v7fs_daddr_t, size_t), void *); +v7fs_daddr_t v7fs_datablock_last(struct v7fs_self *, struct v7fs_inode *, + v7fs_off_t); +int v7fs_datablock_expand(struct v7fs_self *, struct v7fs_inode *, size_t); +int v7fs_datablock_contract(struct v7fs_self *, struct v7fs_inode *, size_t); +int v7fs_datablock_size_change(struct v7fs_self *, size_t, struct v7fs_inode *); + +struct v7fs_daddr_map { + int level; /* direct, index1, index2, index3 */ + v7fs_daddr_t index[3]; +}; +int v7fs_datablock_addr(size_t, struct v7fs_daddr_map *); +__END_DECLS +#endif /*!_V7FS_INODE_H_ */ diff --git a/sys/fs/v7fs/v7fs_dirent.c b/sys/fs/v7fs/v7fs_dirent.c new file mode 100644 index 000000000..99431e7b6 --- /dev/null +++ b/sys/fs/v7fs/v7fs_dirent.c @@ -0,0 +1,89 @@ +/* $NetBSD: v7fs_dirent.c,v 1.2 2011/07/18 21:51:49 apb Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: v7fs_dirent.c,v 1.2 2011/07/18 21:51:49 apb Exp $"); +#if defined _KERNEL_OPT +#include "opt_v7fs.h" +#endif + +#ifdef _KERNEL +#include +#include +#else +#include +#include +#endif + +#include "v7fs.h" +#include "v7fs_impl.h" +#include "v7fs_endian.h" +#include "v7fs_inode.h" +#include "v7fs_dirent.h" + +#ifdef V7FS_DIRENT_DEBUG +#define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) +#else +#define DPRINTF(fmt, args...) ((void)0) +#endif + +bool +v7fs_dirent_endian_convert(struct v7fs_self *fs, struct v7fs_dirent *dir, int n) +{ + struct v7fs_superblock *sb = &fs->superblock; + int i; + v7fs_ino_t ino; + bool ok = true; + + for (i = 0; i < n; i++, dir++) { + ino = V7FS_VAL16(fs, dir->inode_number); + if (v7fs_inode_number_sanity(sb, ino)) { + DPRINTF("Invalid inode# %d %s\n", ino, dir->name); + ok = false; + } + dir->inode_number = ino; + } + + return ok; +} + +void +v7fs_dirent_filename(char *dst/* size must be V7FS_NAME_MAX + 1 */, + const char *src) +{ + + strncpy(dst, src, V7FS_NAME_MAX); + dst[V7FS_NAME_MAX] = '\0'; +} diff --git a/sys/fs/v7fs/v7fs_dirent.h b/sys/fs/v7fs/v7fs_dirent.h new file mode 100644 index 000000000..46ca28d4e --- /dev/null +++ b/sys/fs/v7fs/v7fs_dirent.h @@ -0,0 +1,38 @@ +/* $NetBSD: v7fs_dirent.h,v 1.1 2011/06/27 11:52:24 uch Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _V7FS_DIRENT_H_ +#define _V7FS_DIRENT_H_ +__BEGIN_DECLS +bool v7fs_dirent_endian_convert(struct v7fs_self *, struct v7fs_dirent *, int); +void v7fs_dirent_filename(char *, const char *); +__END_DECLS +#endif /*!_V7FS_DIRENT_H_ */ diff --git a/sys/fs/v7fs/v7fs_endian.c b/sys/fs/v7fs/v7fs_endian.c new file mode 100644 index 000000000..d3cd23020 --- /dev/null +++ b/sys/fs/v7fs/v7fs_endian.c @@ -0,0 +1,231 @@ +/* $NetBSD: v7fs_endian.c,v 1.2 2011/07/18 21:51:49 apb Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: v7fs_endian.c,v 1.2 2011/07/18 21:51:49 apb Exp $"); +#if defined _KERNEL_OPT +#include "opt_v7fs.h" +#endif + +#include "v7fs.h" +#include "v7fs_endian.h" +#include "v7fs_impl.h" + +#ifndef BYTE_ORDER +#error +#endif + +/* PDP to Little */ +#define bswap32pdp_le(x) \ + ((uint32_t) \ + ((((x) & 0xffff0000) >> 16) | \ + (((x) & 0x0000ffff) << 16))) +/* PDP to Big */ +#define bswap32pdp_be(x) \ + ((uint32_t) \ + ((((x) & 0xff00ff00) >> 8) | \ + (((x) & 0x00ff00ff) << 8))) +#ifdef V7FS_EI +static uint32_t val32_normal_order(uint32_t); +static uint32_t val32_reverse_order(uint32_t); +#if BYTE_ORDER == LITTLE_ENDIAN +static uint32_t val32_pdp_to_little(uint32_t); +#else +static uint32_t val32_pdp_to_big(uint32_t); +#endif +static uint16_t val16_normal_order(uint16_t); +static uint16_t val16_reverse_order(uint16_t); +static v7fs_daddr_t val24_reverse_order_read(uint8_t *); +static void val24_reverse_order_write(v7fs_daddr_t, uint8_t *); +static v7fs_daddr_t val24_pdp_read(uint8_t *); +static void val24_pdp_write(v7fs_daddr_t, uint8_t *); + +static uint32_t +val32_normal_order(uint32_t v) +{ + + return v; +} + +static uint32_t +val32_reverse_order(uint32_t v) +{ + + return bswap32(v); +} +#if BYTE_ORDER == LITTLE_ENDIAN +static uint32_t +val32_pdp_to_little(uint32_t v) +{ + + return bswap32pdp_le(v); +} +#else +static uint32_t +val32_pdp_to_big(uint32_t v) +{ + + return bswap32pdp_be(v); +} +#endif +static uint16_t +val16_normal_order(uint16_t v) +{ + + return v; +} + +static uint16_t +val16_reverse_order(uint16_t v) +{ + + return bswap16(v); +} + +static v7fs_daddr_t +val24_reverse_order_read(uint8_t *a) +{ +#if BYTE_ORDER == LITTLE_ENDIAN + return (a[0] << 16) | (a[1] << 8) | a[2]; +#else + return (a[2] << 16) | (a[1] << 8) | a[0]; +#endif +} + +static void +val24_reverse_order_write(v7fs_daddr_t addr, uint8_t *a) +{ +#if BYTE_ORDER == LITTLE_ENDIAN + a[0] = (addr >> 16) & 0xff; + a[1] = (addr >> 8) & 0xff; + a[2] = addr & 0xff; +#else + a[0] = addr & 0xff; + a[1] = (addr >> 8) & 0xff; + a[2] = (addr >> 16) & 0xff; +#endif +} + +static v7fs_daddr_t +val24_pdp_read(uint8_t *a) +{ + + return (a[0] << 16) | a[1] | (a[2] << 8); +} + +static void +val24_pdp_write(v7fs_daddr_t addr, uint8_t *a) +{ + + a[0] = (addr >> 16) & 0xff; + a[1] = addr & 0xff; + a[2] = (addr >> 8) & 0xff; +} + +void +v7fs_endian_init(struct v7fs_self *fs) +{ + struct endian_conversion_ops *ops = &fs->val; + + switch (fs->endian) + { +#if BYTE_ORDER == LITTLE_ENDIAN + case LITTLE_ENDIAN: + ops->conv32 = val32_normal_order; + ops->conv16 = val16_normal_order; + ops->conv24read = val24_normal_order_read; + ops->conv24write = val24_normal_order_write; + break; + case BIG_ENDIAN: + ops->conv32 = val32_reverse_order; + ops->conv16 = val16_reverse_order; + ops->conv24read = val24_reverse_order_read; + ops->conv24write = val24_reverse_order_write; + break; + case PDP_ENDIAN: + ops->conv32 = val32_pdp_to_little; + ops->conv16 = val16_normal_order; + ops->conv24read = val24_pdp_read; + ops->conv24write = val24_pdp_write; + break; +#else /* BIG_ENDIAN */ + case LITTLE_ENDIAN: + ops->conv32 = val32_reverse_order; + ops->conv16 = val16_reverse_order; + ops->conv24read = val24_reverse_order_read; + ops->conv24write = val24_reverse_order_write; + break; + case BIG_ENDIAN: + ops->conv32 = val32_normal_order; + ops->conv16 = val16_normal_order; + ops->conv24read = val24_normal_order_read; + ops->conv24write = val24_normal_order_write; + break; + case PDP_ENDIAN: + ops->conv32 = val32_pdp_to_big; + ops->conv16 = val16_reverse_order; + ops->conv24read = val24_pdp_read; + ops->conv24write = val24_pdp_write; + break; +#endif + } +} +#endif /* V7FS_EI */ +v7fs_daddr_t +val24_normal_order_read(uint8_t *a) +{ + /*(v7fs_daddr_t)cast is required for int 16bit system. */ +#if BYTE_ORDER == LITTLE_ENDIAN + return ((v7fs_daddr_t)a[2] << 16) | ((v7fs_daddr_t)a[1] << 8) | + (v7fs_daddr_t)a[0]; +#else + return ((v7fs_daddr_t)a[0] << 16) | ((v7fs_daddr_t)a[1] << 8) | + (v7fs_daddr_t)a[2]; +#endif +} + +void +val24_normal_order_write(v7fs_daddr_t addr, uint8_t *a) +{ +#if BYTE_ORDER == LITTLE_ENDIAN + a[0] = addr & 0xff; + a[1] = (addr >> 8) & 0xff; + a[2] = (addr >> 16) & 0xff; +#else + a[0] = (addr >> 16) & 0xff; + a[1] = (addr >> 8) & 0xff; + a[2] = addr & 0xff; +#endif +} diff --git a/sys/fs/v7fs/v7fs_endian.h b/sys/fs/v7fs/v7fs_endian.h new file mode 100644 index 000000000..1d6411cae --- /dev/null +++ b/sys/fs/v7fs/v7fs_endian.h @@ -0,0 +1,52 @@ +/* $NetBSD: v7fs_endian.h,v 1.1 2011/06/27 11:52:24 uch Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _V7FS_ENDIAN_H_ +#define _V7FS_ENDIAN_H_ + +struct v7fs_self; +__BEGIN_DECLS; +v7fs_daddr_t val24_normal_order_read(uint8_t *); +void val24_normal_order_write(v7fs_daddr_t, uint8_t *); +__END_DECLS +#ifdef V7FS_EI +#define V7FS_VAL32(x, v) ((*(x)->val.conv32)(v)) +#define V7FS_VAL16(x, v) ((*(x)->val.conv16)(v)) +#define V7FS_VAL24_READ(x, a) ((*(x)->val.conv24read)(a)) +#define V7FS_VAL24_WRITE(x, v, a) ((*(x)->val.conv24write)(v, a)) +void v7fs_endian_init(struct v7fs_self *); +#else +#define V7FS_VAL32(x, v) (v) +#define V7FS_VAL16(x, v) (v) +#define V7FS_VAL24_READ(x, a) (val24_normal_order_read(a)) +#define V7FS_VAL24_WRITE(x, v, a) (val24_normal_order_write(v, a)) +#endif /*V7FS_EI */ +#endif /*!_V7FS_ENDIAN_H_ */ diff --git a/sys/fs/v7fs/v7fs_extern.c b/sys/fs/v7fs/v7fs_extern.c new file mode 100644 index 000000000..7d4c73706 --- /dev/null +++ b/sys/fs/v7fs/v7fs_extern.c @@ -0,0 +1,262 @@ +/* $NetBSD: v7fs_extern.c,v 1.1 2011/06/27 11:52:24 uch Exp $ */ + +/*- + * Copyright (c) 2004, 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__KERNEL_RCSID(0, "$NetBSD: v7fs_extern.c,v 1.1 2011/06/27 11:52:24 uch Exp $"); + +#if defined _KERNEL_OPT +#include "opt_v7fs.h" +#endif +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +MODULE(MODULE_CLASS_VFS, v7fs, NULL); + +/* External interfaces */ + +int (**v7fs_vnodeop_p)(void *); /* filled by getnewvnode (vnode.h) */ + +const struct vnodeopv_entry_desc v7fs_vnodeop_entries[] = { + { &vop_default_desc, vn_default_error }, + { &vop_lookup_desc, v7fs_lookup }, /* lookup */ + { &vop_create_desc, v7fs_create }, /* create */ + { &vop_mknod_desc, v7fs_mknod }, /* mknod */ + { &vop_open_desc, v7fs_open }, /* open */ + { &vop_close_desc, v7fs_close }, /* close */ + { &vop_access_desc, v7fs_access }, /* access */ + { &vop_getattr_desc, v7fs_getattr }, /* getattr */ + { &vop_setattr_desc, v7fs_setattr }, /* setattr */ + { &vop_read_desc, v7fs_read }, /* read */ + { &vop_write_desc, v7fs_write }, /* write */ + { &vop_fcntl_desc, genfs_fcntl }, /* fcntl */ + { &vop_ioctl_desc, genfs_enoioctl }, /* ioctl */ + { &vop_poll_desc, genfs_poll }, /* poll */ + { &vop_kqfilter_desc, genfs_kqfilter }, /* kqfilter */ + { &vop_revoke_desc, genfs_revoke }, /* revoke */ + { &vop_mmap_desc, genfs_mmap }, /* mmap */ + { &vop_fsync_desc, v7fs_fsync }, /* fsync */ + { &vop_seek_desc, genfs_seek }, /* seek */ + { &vop_remove_desc, v7fs_remove }, /* remove */ + { &vop_link_desc, v7fs_link }, /* link */ + { &vop_rename_desc, v7fs_rename }, /* rename */ + { &vop_mkdir_desc, v7fs_mkdir }, /* mkdir */ + { &vop_rmdir_desc, v7fs_rmdir }, /* rmdir */ + { &vop_symlink_desc, v7fs_symlink }, /* symlink */ + { &vop_readdir_desc, v7fs_readdir }, /* readdir */ + { &vop_readlink_desc, v7fs_readlink }, /* readlink */ + { &vop_abortop_desc, genfs_abortop }, /* abortop */ + { &vop_inactive_desc, v7fs_inactive }, /* inactive */ + { &vop_reclaim_desc, v7fs_reclaim }, /* reclaim */ + { &vop_lock_desc, genfs_lock }, /* lock */ + { &vop_unlock_desc, genfs_unlock }, /* unlock */ + { &vop_bmap_desc, v7fs_bmap }, /* bmap */ + { &vop_strategy_desc, v7fs_strategy }, /* strategy */ + { &vop_print_desc, v7fs_print }, /* print */ + { &vop_islocked_desc, genfs_islocked }, /* islocked */ + { &vop_pathconf_desc, v7fs_pathconf }, /* pathconf */ + { &vop_advlock_desc, v7fs_advlock }, /* advlock */ + { &vop_bwrite_desc, vn_bwrite }, /* bwrite */ + { &vop_getpages_desc, genfs_getpages }, /* getpages */ + { &vop_putpages_desc, genfs_putpages }, /* putpages */ + { NULL, NULL } +}; + + +int (**v7fs_specop_p)(void *); +const struct vnodeopv_entry_desc v7fs_specop_entries[] = { + { &vop_default_desc, vn_default_error }, + { &vop_lookup_desc, spec_lookup }, /* lookup */ + { &vop_create_desc, spec_create }, /* create xxx*/ + { &vop_mknod_desc, spec_mknod }, /* mknod xxx*/ + { &vop_open_desc, spec_open }, /* open */ + { &vop_close_desc, spec_close }, /* close */ + { &vop_access_desc, v7fs_access }, /* access */ + { &vop_getattr_desc, v7fs_getattr }, /* getattr */ + { &vop_setattr_desc, v7fs_setattr }, /* setattr */ + { &vop_read_desc, spec_read }, /* read */ + { &vop_write_desc, spec_write }, /* write */ + { &vop_ioctl_desc, spec_ioctl }, /* ioctl */ + { &vop_fcntl_desc, genfs_fcntl }, /* fcntl */ + { &vop_poll_desc, spec_poll }, /* poll */ + { &vop_kqfilter_desc, spec_kqfilter }, /* kqfilter */ + { &vop_revoke_desc, spec_revoke }, /* revoke */ + { &vop_mmap_desc, spec_mmap }, /* mmap */ + { &vop_fsync_desc, spec_fsync }, /* fsync */ + { &vop_seek_desc, spec_seek }, /* seek */ + { &vop_remove_desc, spec_remove }, /* remove */ + { &vop_link_desc, spec_link }, /* link */ + { &vop_rename_desc, spec_rename }, /* rename */ + { &vop_mkdir_desc, spec_mkdir }, /* mkdir */ + { &vop_rmdir_desc, spec_rmdir }, /* rmdir */ + { &vop_symlink_desc, spec_symlink }, /* symlink */ + { &vop_readdir_desc, spec_readdir }, /* readdir */ + { &vop_readlink_desc, spec_readlink }, /* readlink */ + { &vop_abortop_desc, spec_abortop }, /* abortop */ + { &vop_inactive_desc, v7fs_inactive }, /* inactive */ + { &vop_reclaim_desc, v7fs_reclaim }, /* reclaim */ + { &vop_lock_desc, genfs_lock }, /* lock */ + { &vop_unlock_desc, genfs_unlock }, /* unlock */ + { &vop_bmap_desc, spec_bmap }, /* bmap */ + { &vop_strategy_desc, spec_strategy }, /* strategy */ + { &vop_print_desc, spec_print }, /* print */ + { &vop_islocked_desc, genfs_islocked }, /* islocked */ + { &vop_pathconf_desc, spec_pathconf }, /* pathconf */ + { &vop_advlock_desc, spec_advlock }, /* advlock */ + { &vop_bwrite_desc, vn_bwrite }, /* bwrite */ + { &vop_getpages_desc, spec_getpages }, /* getpages */ + { &vop_putpages_desc, spec_putpages }, /* putpages */ + { NULL, NULL } +}; + +int (**v7fs_fifoop_p)(void *); +const struct vnodeopv_entry_desc v7fs_fifoop_entries[] = { + { &vop_default_desc, vn_default_error }, + { &vop_lookup_desc, vn_fifo_bypass }, /* lookup */ + { &vop_create_desc, vn_fifo_bypass }, /* create */ + { &vop_mknod_desc, vn_fifo_bypass }, /* mknod */ + { &vop_open_desc, vn_fifo_bypass }, /* open */ + { &vop_close_desc, vn_fifo_bypass }, /* close */ + { &vop_access_desc, v7fs_access }, /* access */ + { &vop_getattr_desc, v7fs_getattr }, /* getattr */ + { &vop_setattr_desc, v7fs_setattr }, /* setattr */ + { &vop_read_desc, vn_fifo_bypass }, /* read */ + { &vop_write_desc, vn_fifo_bypass }, /* write */ + { &vop_ioctl_desc, vn_fifo_bypass }, /* ioctl */ + { &vop_fcntl_desc, genfs_fcntl }, /* fcntl */ + { &vop_poll_desc, vn_fifo_bypass }, /* poll */ + { &vop_kqfilter_desc, vn_fifo_bypass }, /* kqfilter */ + { &vop_revoke_desc, vn_fifo_bypass }, /* revoke */ + { &vop_mmap_desc, vn_fifo_bypass }, /* mmap */ + { &vop_fsync_desc, vn_fifo_bypass }, /* fsync */ + { &vop_seek_desc, vn_fifo_bypass }, /* seek */ + { &vop_remove_desc, vn_fifo_bypass }, /* remove */ + { &vop_link_desc, vn_fifo_bypass }, /* link */ + { &vop_rename_desc, vn_fifo_bypass }, /* rename */ + { &vop_mkdir_desc, vn_fifo_bypass }, /* mkdir */ + { &vop_rmdir_desc, vn_fifo_bypass }, /* rmdir */ + { &vop_symlink_desc, vn_fifo_bypass }, /* symlink */ + { &vop_readdir_desc, vn_fifo_bypass }, /* readdir */ + { &vop_readlink_desc, vn_fifo_bypass }, /* readlink */ + { &vop_abortop_desc, vn_fifo_bypass }, /* abortop */ + { &vop_inactive_desc, v7fs_inactive }, /* inactive */ + { &vop_reclaim_desc, v7fs_reclaim }, /* reclaim */ + { &vop_lock_desc, vn_fifo_bypass }, /* lock */ + { &vop_unlock_desc, vn_fifo_bypass }, /* unlock */ + { &vop_bmap_desc, vn_fifo_bypass }, /* bmap */ + { &vop_strategy_desc, vn_fifo_bypass }, /* strategy */ + { &vop_print_desc, vn_fifo_bypass }, /* print */ + { &vop_islocked_desc, vn_fifo_bypass }, /* islocked */ + { &vop_pathconf_desc, vn_fifo_bypass }, /* pathconf */ + { &vop_advlock_desc, vn_fifo_bypass }, /* advlock */ + { &vop_bwrite_desc, vn_bwrite }, /* bwrite */ + { &vop_putpages_desc, vn_fifo_bypass }, /* putpages */ + { NULL, NULL } +}; + +const struct vnodeopv_desc v7fs_fifoop_opv_desc = { + &v7fs_fifoop_p, + v7fs_fifoop_entries +}; + +const struct vnodeopv_desc v7fs_vnodeop_opv_desc = { + &v7fs_vnodeop_p, + v7fs_vnodeop_entries +}; + +const struct vnodeopv_desc v7fs_specop_opv_desc = { + &v7fs_specop_p, + v7fs_specop_entries +}; + +const struct vnodeopv_desc *v7fs_vnodeopv_descs[] = { + &v7fs_vnodeop_opv_desc, + &v7fs_specop_opv_desc, + &v7fs_fifoop_opv_desc, + NULL, +}; + +const struct genfs_ops v7fs_genfsops = { + .gop_size = genfs_size, + .gop_alloc = v7fs_gop_alloc, + .gop_write = genfs_gop_write, +}; + +struct vfsops v7fs_vfsops = { + MOUNT_V7FS, + sizeof(struct v7fs_args), + v7fs_mount, + v7fs_start, + v7fs_unmount, + v7fs_root, + (void *)eopnotsupp, /* vfs_quotactl */ + v7fs_statvfs, + v7fs_sync, + v7fs_vget, + v7fs_fhtovp, + v7fs_vptofh, + v7fs_init, + v7fs_reinit, + v7fs_done, + v7fs_mountroot, + (int (*)(struct mount *, struct vnode *, struct timespec *)) + eopnotsupp, /* snapshot */ + vfs_stdextattrctl, + (void *)eopnotsupp, /* vfs_suspendctl */ + genfs_renamelock_enter, + genfs_renamelock_exit, + (void *)eopnotsupp, + v7fs_vnodeopv_descs, + 0, + { NULL, NULL } +}; + +static int +v7fs_modcmd(modcmd_t cmd, void *arg) +{ + + switch (cmd) { + case MODULE_CMD_INIT: + return vfs_attach(&v7fs_vfsops); + case MODULE_CMD_FINI: + return vfs_detach(&v7fs_vfsops); + default: + return ENOTTY; + } +} diff --git a/sys/fs/v7fs/v7fs_extern.h b/sys/fs/v7fs/v7fs_extern.h new file mode 100644 index 000000000..80c4f9cc2 --- /dev/null +++ b/sys/fs/v7fs/v7fs_extern.h @@ -0,0 +1,116 @@ +/* $NetBSD: v7fs_extern.h,v 1.1 2011/06/27 11:52:24 uch Exp $ */ + +/*- + * Copyright (c) 2004, 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FS_V7FS_EXTERN_H_ +#define _FS_V7FS_EXTERN_H_ + +#include + +#include +#include +#include + +#include "v7fs.h" +#include "v7fs_impl.h" +#include "v7fs_inode.h" + +struct v7fs_mount { + struct mount *mountp; + struct vnode *devvp; /* block device mounted vnode */ + struct v7fs_self *core; /* filesystem dependent implementation*/ + LIST_HEAD(, v7fs_node) v7fs_node_head; +}; + +struct v7fs_node { + struct genfs_node gnode; + struct v7fs_inode inode; /* filesystem dependent implementation */ + struct vnode *vnode; /* back-link */ + struct v7fs_mount *v7fsmount; /* our filesystem */ + struct lockf *lockf; /* advlock */ + + int update_ctime; + int update_atime; + int update_mtime; + + LIST_ENTRY(v7fs_node) link; +}; + +#define VFSTOV7FS(mp) ((struct v7fs_mount *)((mp)->mnt_data)) + +__BEGIN_DECLS +/* v-node ops. */ +int v7fs_lookup(void *); +int v7fs_create(void *); +int v7fs_open(void *); +int v7fs_close(void *); +int v7fs_access(void *); +int v7fs_getattr(void *); +int v7fs_setattr(void *); +int v7fs_read(void *); +int v7fs_write(void *); +int v7fs_fsync(void *); +int v7fs_remove(void *); +int v7fs_rename(void *); +int v7fs_readdir(void *); +int v7fs_inactive(void *); +int v7fs_reclaim(void *); +int v7fs_bmap(void *); +int v7fs_strategy(void *); +int v7fs_print(void *); +int v7fs_advlock(void *); +int v7fs_pathconf(void *); + +int v7fs_link(void *); +int v7fs_symlink(void *); +int v7fs_readlink(void *); + +int v7fs_mkdir(void *); +int v7fs_rmdir(void *); + +int v7fs_mknod(void *); + +/* vfs ops. */ +VFS_PROTOS(v7fs); + +int v7fs_mountroot(void); +extern int (**v7fs_vnodeop_p)(void *); +extern int (**v7fs_specop_p)(void *); +extern int (**v7fs_fifoop_p)(void *); + +/* genfs ops */ +int v7fs_gop_alloc(struct vnode *, off_t, off_t, int, kauth_cred_t); +extern const struct genfs_ops v7fs_genfsops; + +/* internal service */ +int v7fs_update(struct vnode *, const struct timespec *, + const struct timespec *, int); +__END_DECLS +#endif /* _FS_V7FS_EXTERN_H_ */ diff --git a/sys/fs/v7fs/v7fs_file.c b/sys/fs/v7fs/v7fs_file.c new file mode 100644 index 000000000..f0d32b07a --- /dev/null +++ b/sys/fs/v7fs/v7fs_file.c @@ -0,0 +1,422 @@ +/* $NetBSD: v7fs_file.c,v 1.5 2012/12/07 06:50:15 msaitoh Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: v7fs_file.c,v 1.5 2012/12/07 06:50:15 msaitoh Exp $"); +#if defined _KERNEL_OPT +#include "opt_v7fs.h" +#endif + +#include +#ifdef _KERNEL +#include +#else +#include +#include +#include +#endif + +#include "v7fs.h" +#include "v7fs_impl.h" +#include "v7fs_endian.h" +#include "v7fs_inode.h" +#include "v7fs_dirent.h" +#include "v7fs_file.h" +#include "v7fs_datablock.h" + +#ifdef V7FS_FILE_DEBUG +#define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) +#else +#define DPRINTF(fmt, args...) ((void)0) +#endif + +static int lookup_subr(struct v7fs_self *, void *, v7fs_daddr_t, size_t); +static int remove_subr(struct v7fs_self *, void *, v7fs_daddr_t, size_t); + +int +v7fs_file_lookup_by_name(struct v7fs_self *fs, struct v7fs_inode *parent_dir, + const char *name, v7fs_ino_t *ino) +{ + char filename[V7FS_NAME_MAX + 1]; + char *q; + int error; + size_t len; + + if ((q = strchr(name, '/'))) { + /* Zap following path. */ + len = MIN(V7FS_NAME_MAX, q - name); + memcpy(filename, name, len); + filename[len] = '\0'; /* '/' -> '\0' */ + } else { + v7fs_dirent_filename(filename, name); + } + DPRINTF("%s(%s) dir=%d\n", filename, name, parent_dir->inode_number); + + struct v7fs_lookup_arg lookup_arg = { .name = filename, + .inode_number = 0 }; + if ((error = v7fs_datablock_foreach(fs, parent_dir, lookup_subr, + &lookup_arg)) != V7FS_ITERATOR_BREAK) { + DPRINTF("not found.\n"); + return ENOENT; + } + + *ino = lookup_arg.inode_number; + DPRINTF("done. ino=%d\n", *ino); + + return 0; +} + +static int +lookup_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, size_t sz) +{ + struct v7fs_lookup_arg *p = (struct v7fs_lookup_arg *)ctx; + struct v7fs_dirent *dir; + const char *name = p->name; + void *buf; + size_t i, n; + int ret = 0; + + if (!(buf = scratch_read(fs, blk))) + return EIO; + + dir = (struct v7fs_dirent *)buf; + n = sz / sizeof(*dir); + v7fs_dirent_endian_convert(fs, dir, n); + + for (i = 0; i < n; i++, dir++) { + if (dir->inode_number < 1) { + DPRINTF("*** bad inode #%d ***\n", dir->inode_number); + continue; + } + + if (strncmp((const char *)dir->name, name, V7FS_NAME_MAX) == 0) + { + p->inode_number = dir->inode_number; + ret = V7FS_ITERATOR_BREAK; /* found */ + break; + } + } + scratch_free(fs, buf); + + return ret; +} + +int +v7fs_file_allocate(struct v7fs_self *fs, struct v7fs_inode *parent_dir, + const char *srcname, struct v7fs_fileattr *attr, v7fs_ino_t *ino) +{ + struct v7fs_inode inode; + char filename[V7FS_NAME_MAX + 1]; + struct v7fs_dirent *dir; + int error; + + /* Truncate filename. */ + v7fs_dirent_filename(filename, srcname); + DPRINTF("%s(%s)\n", filename, srcname); + + /* Check filename. */ + if (v7fs_file_lookup_by_name(fs, parent_dir, filename, ino) == 0) { + DPRINTF("%s exists\n", filename); + return EEXIST; + } + + /* Get new inode. */ + if ((error = v7fs_inode_allocate(fs, ino))) + return error; + + /* Set initial attribute. */ + memset(&inode, 0, sizeof(inode)); + inode.inode_number = *ino; + inode.mode = attr->mode; + inode.uid = attr->uid; + inode.gid = attr->gid; + if (attr->ctime) + inode.ctime = attr->ctime; + if (attr->mtime) + inode.mtime = attr->mtime; + if (attr->atime) + inode.atime = attr->atime; + + switch (inode.mode & V7FS_IFMT) { + default: + DPRINTF("Can't allocate %o type.\n", inode.mode); + v7fs_inode_deallocate(fs, *ino); + return EINVAL; + case V7FS_IFCHR: + /* FALLTHROUGH */ + case V7FS_IFBLK: + inode.nlink = 1; + inode.device = attr->device; + inode.addr[0] = inode.device; + break; + case V7FSBSD_IFFIFO: + /* FALLTHROUGH */ + case V7FSBSD_IFSOCK: + /* FALLTHROUGH */ + case V7FSBSD_IFLNK: + /* FALLTHROUGH */ + case V7FS_IFREG: + inode.nlink = 1; + break; + case V7FS_IFDIR: + inode.nlink = 2; /* . + .. */ + if ((error = v7fs_datablock_expand(fs, &inode, sizeof(*dir) * 2 + ))) { + v7fs_inode_deallocate(fs, *ino); + return error; + } + v7fs_daddr_t blk = inode.addr[0]; + void *buf; + if (!(buf = scratch_read(fs, blk))) { + v7fs_inode_deallocate(fs, *ino); + return EIO; + } + dir = (struct v7fs_dirent *)buf; + strcpy(dir[0].name, "."); + dir[0].inode_number = V7FS_VAL16(fs, *ino); + strcpy(dir[1].name, ".."); + dir[1].inode_number = V7FS_VAL16(fs, parent_dir->inode_number); + if (!fs->io.write(fs->io.cookie, buf, blk)) { + scratch_free(fs, buf); + return EIO; + } + scratch_free(fs, buf); + break; + } + + v7fs_inode_writeback(fs, &inode); + + /* Link this inode to parent directory. */ + if ((error = v7fs_directory_add_entry(fs, parent_dir, *ino, filename))) + { + DPRINTF("can't add dirent.\n"); + return error; + } + + return 0; +} + +int +v7fs_file_deallocate(struct v7fs_self *fs, struct v7fs_inode *parent_dir, + const char *name) +{ + v7fs_ino_t ino; + struct v7fs_inode inode; + int error; + + DPRINTF("%s\n", name); + if ((error = v7fs_file_lookup_by_name(fs, parent_dir, name, &ino))) { + DPRINTF("no such a file: %s\n", name); + return error; + } + DPRINTF("%s->#%d\n", name, ino); + if ((error = v7fs_inode_load(fs, &inode, ino))) + return error; + + if (v7fs_inode_isdir(&inode)) { + char filename[V7FS_NAME_MAX + 1]; + v7fs_dirent_filename(filename, name); + /* Check parent */ + if (strncmp(filename, "..", V7FS_NAME_MAX) == 0) { + DPRINTF("can not remove '..'\n"); + return EINVAL; /* t_vnops rename_dotdot */ + } + /* Check empty */ + if (v7fs_inode_filesize(&inode) != + sizeof(struct v7fs_dirent) * 2 /*"." + ".."*/) { + DPRINTF("directory not empty.\n"); + return ENOTEMPTY;/* t_vnops dir_noempty, rename_dir(6)*/ + } + inode.nlink = 0; /* remove this. */ + } else { + /* Decrement reference count. */ + --inode.nlink; /* regular file. */ + DPRINTF("%s nlink=%d\n", name, inode.nlink); + } + + + if ((error = v7fs_directory_remove_entry(fs, parent_dir, name))) + return error; + DPRINTF("remove dirent\n"); + + if (inode.nlink == 0) { + v7fs_datablock_contract(fs, &inode, inode.filesize); + DPRINTF("remove datablock\n"); + v7fs_inode_deallocate(fs, ino); + DPRINTF("remove inode\n"); + } else { + v7fs_inode_writeback(fs, &inode); + } + + return 0; +} + +int +v7fs_directory_add_entry(struct v7fs_self *fs, struct v7fs_inode *parent_dir, + v7fs_ino_t ino, const char *srcname) +{ + struct v7fs_inode inode; + struct v7fs_dirent *dir; + int error = 0; + v7fs_daddr_t blk; + void *buf; + char filename[V7FS_NAME_MAX + 1]; + + /* Truncate filename. */ + v7fs_dirent_filename(filename, srcname); + DPRINTF("%s(%s) %d\n", filename, srcname, ino); + + /* Target inode */ + if ((error = v7fs_inode_load(fs, &inode, ino))) + return error; + + /* Expand datablock. */ + if ((error = v7fs_datablock_expand(fs, parent_dir, sizeof(*dir)))) + return error; + + /* Read last entry. */ + if (!(blk = v7fs_datablock_last(fs, parent_dir, + v7fs_inode_filesize(parent_dir)))) + return EIO; + + /* Load dirent block. This vnode(parent dir) is locked by VFS layer. */ + if (!(buf = scratch_read(fs, blk))) + return EIO; + + size_t sz = v7fs_inode_filesize(parent_dir); + sz = V7FS_RESIDUE_BSIZE(sz); /* last block payload. */ + int n = sz / sizeof(*dir) - 1; + /* Add dirent. */ + dir = (struct v7fs_dirent *)buf; + dir[n].inode_number = V7FS_VAL16(fs, ino); + memcpy((char *)dir[n].name, filename, V7FS_NAME_MAX); + /* Write back datablock */ + if (!fs->io.write(fs->io.cookie, buf, blk)) + error = EIO; + scratch_free(fs, buf); + + if (v7fs_inode_isdir(&inode)) { + parent_dir->nlink++; + v7fs_inode_writeback(fs, parent_dir); + } + + DPRINTF("done. (dirent size=%dbyte)\n", parent_dir->filesize); + + return error; +} + +int +v7fs_directory_remove_entry(struct v7fs_self *fs, struct v7fs_inode *parent_dir, + const char *name) +{ + struct v7fs_inode inode; + int error; + struct v7fs_dirent lastdirent; + v7fs_daddr_t lastblk; + size_t sz, lastsz; + v7fs_off_t pos; + void *buf; + + /* Setup replaced entry. */ + sz = parent_dir->filesize; + lastblk = v7fs_datablock_last(fs, parent_dir, + v7fs_inode_filesize(parent_dir)); + lastsz = V7FS_RESIDUE_BSIZE(sz); + pos = lastsz - sizeof(lastdirent); + + if (!(buf = scratch_read(fs, lastblk))) + return EIO; + lastdirent = *((struct v7fs_dirent *)((uint8_t *)buf + pos)); + scratch_free(fs, buf); + DPRINTF("last dirent=%d %s pos=%d\n", + V7FS_VAL16(fs, lastdirent.inode_number), lastdirent.name, pos); + + struct v7fs_lookup_arg lookup_arg = + { .name = name, .replace = &lastdirent/*disk endian */ }; + /* Search entry that removed. replace it to last dirent. */ + if ((error = v7fs_datablock_foreach(fs, parent_dir, remove_subr, + &lookup_arg)) != V7FS_ITERATOR_BREAK) + return ENOENT; + + /* Contract dirent entries. */ + v7fs_datablock_contract(fs, parent_dir, sizeof(lastdirent)); + DPRINTF("done. (dirent size=%dbyte)\n", parent_dir->filesize); + + /* Target inode */ + if ((error = v7fs_inode_load(fs, &inode, lookup_arg.inode_number))) + return error; + + if (v7fs_inode_isdir(&inode)) { + parent_dir->nlink--; + v7fs_inode_writeback(fs, parent_dir); + } + + return 0; +} + +static int +remove_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, size_t sz) +{ + struct v7fs_lookup_arg *p = (struct v7fs_lookup_arg *)ctx; + struct v7fs_dirent *dir; + void *buf; + size_t i; + int ret = 0; + + DPRINTF("match start blk=%x\n", blk); + if (!(buf = scratch_read(fs, blk))) + return EIO; + + dir = (struct v7fs_dirent *)buf; + + for (i = 0; i < sz / sizeof(*dir); i++, dir++) { + DPRINTF("%d\n", V7FS_VAL16(fs, dir->inode_number)); + if (strncmp(p->name, + (const char *)dir->name, V7FS_NAME_MAX) == 0) { + p->inode_number = V7FS_VAL16(fs, dir->inode_number); + /* Replace to last dirent. */ + *dir = *(p->replace); /* disk endian */ + /* Write back. */ + if (!fs->io.write(fs->io.cookie, buf, blk)) + ret = EIO; + else + ret = V7FS_ITERATOR_BREAK; + break; + } + } + scratch_free(fs, buf); + + return ret; +} diff --git a/sys/fs/v7fs/v7fs_file.h b/sys/fs/v7fs/v7fs_file.h new file mode 100644 index 000000000..26c39fb5c --- /dev/null +++ b/sys/fs/v7fs/v7fs_file.h @@ -0,0 +1,65 @@ +/* $NetBSD: v7fs_file.h,v 1.2 2011/07/16 12:35:40 uch Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _V7FS_FILE_H_ +#define _V7FS_FILE_H_ + +struct v7fs_lookup_arg { + const char *name; + char *buf; + v7fs_ino_t inode_number; + struct v7fs_dirent *replace; +}; + +__BEGIN_DECLS +/* core */ +int v7fs_file_lookup_by_name(struct v7fs_self *, struct v7fs_inode *, + const char*, v7fs_ino_t *); +int v7fs_file_allocate(struct v7fs_self *, struct v7fs_inode *, const char *, + struct v7fs_fileattr *, v7fs_ino_t *); +int v7fs_file_deallocate(struct v7fs_self *, struct v7fs_inode *, const char *); +int v7fs_directory_add_entry(struct v7fs_self *,struct v7fs_inode *, v7fs_ino_t, + const char *); +int v7fs_directory_remove_entry(struct v7fs_self *,struct v7fs_inode *, + const char *); + +/* util */ +int v7fs_file_rename(struct v7fs_self *, struct v7fs_inode *, const char *, + struct v7fs_inode *, const char *); +int v7fs_directory_replace_entry(struct v7fs_self *, struct v7fs_inode *, + const char *, v7fs_ino_t); +int v7fs_file_link(struct v7fs_self *, struct v7fs_inode *, struct v7fs_inode *, + const char *); +bool v7fs_file_lookup_by_number(struct v7fs_self *, struct v7fs_inode *, + v7fs_ino_t, char *); +int v7fs_file_symlink(struct v7fs_self *, struct v7fs_inode *, const char *); +__END_DECLS +#endif /*!_V7FS_INODE_H_ */ diff --git a/sys/fs/v7fs/v7fs_file_util.c b/sys/fs/v7fs/v7fs_file_util.c new file mode 100644 index 000000000..a6cfb32e8 --- /dev/null +++ b/sys/fs/v7fs/v7fs_file_util.c @@ -0,0 +1,344 @@ +/* $NetBSD: v7fs_file_util.c,v 1.4 2011/07/30 03:52:04 uch Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: v7fs_file_util.c,v 1.4 2011/07/30 03:52:04 uch Exp $"); +#ifdef _KERNEL +#include +#include +#else +#include +#include +#include +#endif + +#include "v7fs.h" +#include "v7fs_impl.h" +#include "v7fs_endian.h" +#include "v7fs_inode.h" +#include "v7fs_dirent.h" +#include "v7fs_file.h" +#include "v7fs_datablock.h" + +#ifdef V7FS_FILE_DEBUG +#define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) +#else +#define DPRINTF(fmt, args...) ((void)0) +#endif + +static int replace_subr(struct v7fs_self *, void *, v7fs_daddr_t, size_t); +static int lookup_by_number_subr(struct v7fs_self *, void *, v7fs_daddr_t, + size_t); +static int can_dirmove(struct v7fs_self *, v7fs_ino_t, v7fs_ino_t); +static int lookup_parent_from_dir_subr(struct v7fs_self *, void *, + v7fs_daddr_t, size_t); + +int +v7fs_file_link(struct v7fs_self *fs, struct v7fs_inode *parent_dir, + struct v7fs_inode *p, const char *name) +{ + int error = 0; + + DPRINTF("%d %d %s\n", parent_dir->inode_number, p->inode_number, name); + if ((error = v7fs_directory_add_entry(fs, parent_dir, p->inode_number, + name))) { + DPRINTF("can't add entry"); + return error; + } + p->nlink++; + v7fs_inode_writeback(fs, p); + + return 0; +} + +int +v7fs_file_symlink(struct v7fs_self *fs, struct v7fs_inode *p, + const char *target) +{ + int error; + size_t len = strlen(target) + 1; + + if (len > V7FSBSD_MAXSYMLINKLEN) {/* limited target 512byte pathname */ + DPRINTF("too long pathname."); + return ENAMETOOLONG; + } + + if ((error = v7fs_datablock_expand(fs, p, len))) { + return error; + } + + v7fs_daddr_t blk = p->addr[0]; /* 1block only. */ + void *buf; + if (!(buf = scratch_read(fs, blk))) { + return EIO; + } + + strncpy(buf, target, V7FS_BSIZE); + if (!fs->io.write(fs->io.cookie, buf, blk)) { + scratch_free(fs, buf); + return EIO; + } + scratch_free(fs, buf); + v7fs_inode_writeback(fs, p); + + return 0; +} + +int +v7fs_file_rename(struct v7fs_self *fs, struct v7fs_inode *parent_from, + const char *from, struct v7fs_inode *parent_to, const char *to) +{ + v7fs_ino_t from_ino, to_ino; + struct v7fs_inode inode; + int error; + bool dir_move; + + /* Check source file */ + if ((error = v7fs_file_lookup_by_name(fs, parent_from, from, + &from_ino))) { + DPRINTF("%s don't exists\n", from); + return error; + } + v7fs_inode_load(fs, &inode, from_ino); + dir_move = v7fs_inode_isdir(&inode); + + /* Check target file */ + error = v7fs_file_lookup_by_name(fs, parent_to, to, &to_ino); + if (error == 0) { /* found */ + DPRINTF("%s already exists\n", to); + if ((error = v7fs_file_deallocate(fs, parent_to, to))) { + DPRINTF("%s can't remove %d\n", to, error); + return error; + } + } else if (error != ENOENT) { + DPRINTF("error=%d\n", error); + return error; + } + /* Check directory hierarchy. t_vnops rename_dir(5) */ + if (dir_move && (error = can_dirmove(fs, from_ino, + parent_to->inode_number))) { + DPRINTF("dst '%s' is child dir of '%s'. error=%d\n", to, from, + error); + return error; + } + + if ((error = v7fs_directory_add_entry(fs, parent_to, from_ino, to))) { + DPRINTF("can't add entry"); + return error; + } + + if ((error = v7fs_directory_remove_entry(fs, parent_from, from))) { + DPRINTF("can't remove entry"); + return error; + } + + if (dir_move && (parent_from != parent_to)) { + /* If directory move, update ".." */ + if ((error = v7fs_directory_replace_entry(fs, &inode, "..", + parent_to->inode_number))) { + DPRINTF("can't replace parent dir"); + return error; + } + v7fs_inode_writeback(fs, &inode); + } + + return 0; +} + + +int +v7fs_directory_replace_entry(struct v7fs_self *fs, struct v7fs_inode *self_dir, + const char *name, v7fs_ino_t ino) +{ + int error; + + /* Search entry that replaced. replace it to new inode number. */ + struct v7fs_lookup_arg lookup_arg = { .name = name, + .inode_number = ino }; + if ((error = v7fs_datablock_foreach(fs, self_dir, replace_subr, + &lookup_arg)) != V7FS_ITERATOR_BREAK) + return ENOENT; + + return 0; +} + +static int +replace_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, size_t sz) +{ + struct v7fs_lookup_arg *p = (struct v7fs_lookup_arg *)ctx; + struct v7fs_dirent *dir; + void *buf; + size_t i, n; + int ret = 0; + + DPRINTF("match start blk=%x\n", blk); + if (!(buf = scratch_read(fs, blk))) + return EIO; + + dir = (struct v7fs_dirent *)buf; + n = sz / sizeof(*dir); + + for (i = 0; i < n; i++, dir++) { /*disk endian */ + if (strncmp(p->name, (const char *)dir->name, V7FS_NAME_MAX) + == 0) { + /* Replace inode# */ + dir->inode_number = V7FS_VAL16(fs, p->inode_number); + /* Write back. */ + if (!fs->io.write(fs->io.cookie, buf, blk)) + ret = EIO; + else + ret = V7FS_ITERATOR_BREAK; + break; + } + } + scratch_free(fs, buf); + + return ret; +} + +bool +v7fs_file_lookup_by_number(struct v7fs_self *fs, struct v7fs_inode *parent_dir, + v7fs_ino_t ino, char *buf) +{ + int ret; + + ret = v7fs_datablock_foreach(fs, parent_dir, lookup_by_number_subr, + &(struct v7fs_lookup_arg){ .inode_number = ino, .buf = buf }); + + return ret == V7FS_ITERATOR_BREAK; +} + +static int +lookup_by_number_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, + size_t sz) +{ + struct v7fs_lookup_arg *p = (struct v7fs_lookup_arg *)ctx; + struct v7fs_dirent *dir; + void *buf; + size_t i, n; + int ret = 0; + + if (!(buf = scratch_read(fs, blk))) + return EIO; + + dir = (struct v7fs_dirent *)buf; + n = sz / sizeof(*dir); + v7fs_dirent_endian_convert(fs, dir, n); + + for (i = 0; i < n; i++, dir++) { + if (dir->inode_number == p->inode_number) { + if (p->buf) + v7fs_dirent_filename(p->buf, dir->name); + ret = V7FS_ITERATOR_BREAK; + break; + } + } + scratch_free(fs, buf); + + return ret; +} + +struct lookup_parent_arg { + v7fs_ino_t parent_ino; +}; + +static int +can_dirmove(struct v7fs_self *fs, v7fs_ino_t from_ino, v7fs_ino_t to_ino) +{ + struct v7fs_inode inode; + v7fs_ino_t parent; + int error; + + /* Start dir. */ + if ((error = v7fs_inode_load(fs, &inode, to_ino))) + return error; + + if (!v7fs_inode_isdir(&inode)) + return ENOTDIR; + + /* Lookup the parent. */ + do { + struct lookup_parent_arg arg; + /* Search parent dir */ + arg.parent_ino = 0; + v7fs_datablock_foreach(fs, &inode, lookup_parent_from_dir_subr, + &arg); + if ((parent = arg.parent_ino) == 0) { + DPRINTF("***parent missing\n"); + return ENOENT; + } + /* Load parent dir */ + if ((error = v7fs_inode_load(fs, &inode, parent))) + return error; + if (parent == from_ino) { + DPRINTF("#%d is child dir of #%d\n", to_ino, from_ino); + return EINVAL; + } + } while (parent != V7FS_ROOT_INODE); + + return 0; +} + +static int +lookup_parent_from_dir_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, + size_t sz) +{ + struct lookup_parent_arg *arg = (struct lookup_parent_arg *)ctx; + char name[V7FS_NAME_MAX + 1]; + void *buf; + int ret = 0; + + if (!(buf = scratch_read(fs, blk))) + return 0; + struct v7fs_dirent *dir = (struct v7fs_dirent *)buf; + size_t i, n = sz / sizeof(*dir); + if (!v7fs_dirent_endian_convert(fs, dir, n)) { + scratch_free(fs, buf); + return V7FS_ITERATOR_ERROR; + } + + for (i = 0; i < n; i++, dir++) { + v7fs_dirent_filename(name, dir->name); + if (strncmp(dir->name, "..", V7FS_NAME_MAX) != 0) + continue; + + arg->parent_ino = dir->inode_number; + ret = V7FS_ITERATOR_BREAK; + break; + } + + scratch_free(fs, buf); + return ret; +} diff --git a/sys/fs/v7fs/v7fs_impl.h b/sys/fs/v7fs/v7fs_impl.h new file mode 100644 index 000000000..a6cf6a39d --- /dev/null +++ b/sys/fs/v7fs/v7fs_impl.h @@ -0,0 +1,154 @@ +/* $NetBSD: v7fs_impl.h,v 1.1 2011/06/27 11:52:25 uch Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* V7FS implementation. */ +#ifndef _V7FS_IMPL_H_ +#define _V7FS_IMPL_H_ + +#ifndef _KERNEL +#include +#include +#define KDASSERT(x) assert(x) +#endif + +struct block_io_ops { + void *cookie; + bool (*drive)(void *, uint8_t); + bool (*read)(void *, uint8_t *, daddr_t); + bool (*read_n)(void *, uint8_t *, daddr_t, int); + bool (*write)(void *, uint8_t *, daddr_t); + bool (*write_n)(void *, uint8_t *, daddr_t, int); +}; + +#ifdef V7FS_EI +struct endian_conversion_ops { + uint32_t (*conv32)(uint32_t); + uint16_t (*conv16)(uint16_t); + /* For daddr packing */ + v7fs_daddr_t (*conv24read)(uint8_t *); + void (*conv24write)(v7fs_daddr_t, uint8_t *); +}; +#endif +#ifdef _KERNEL +#define V7FS_LOCK +#endif +#ifdef V7FS_LOCK +struct lock_ops { + void *cookie; + void (*lock)(void*); + void (*unlock)(void *); +}; +#define SUPERB_LOCK(x) ((x)->sb_lock.lock((x)->sb_lock.cookie)) +#define SUPERB_UNLOCK(x) ((x)->sb_lock.unlock((x)->sb_lock.cookie)) +#define ILIST_LOCK(x) ((x)->ilist_lock.lock((x)->ilist_lock.cookie)) +#define ILIST_UNLOCK(x) ((x)->ilist_lock.unlock((x)->ilist_lock.cookie)) +#define MEM_LOCK(x) ((x)->mem_lock.lock((x)->mem_lock.cookie)) +#define MEM_UNLOCK(x) ((x)->mem_lock.unlock((x)->mem_lock.cookie)) +#else /*V7FS_LOCK */ +#define SUPERB_LOCK(x) ((void)0) +#define SUPERB_UNLOCK(x) ((void)0) +#define ILIST_LOCK(x) ((void)0) +#define ILIST_UNLOCK(x) ((void)0) +#define MEM_LOCK(x) ((void)0) +#define MEM_UNLOCK(x) ((void)0) +#endif /*V7FS_LOCK */ + +struct v7fs_stat { + int32_t total_blocks; + int32_t free_blocks; + int32_t total_inode; + int32_t free_inode; + int32_t total_files; +}; + +struct v7fs_fileattr { + int16_t uid; + int16_t gid; + v7fs_mode_t mode; + v7fs_dev_t device; + v7fs_time_t ctime; + v7fs_time_t mtime; + v7fs_time_t atime; +}; + +struct v7fs_self { +#define V7FS_SELF_NSCRATCH 3 + uint8_t scratch[V7FS_SELF_NSCRATCH][V7FS_BSIZE]; + int scratch_free; /* free block bitmap. */ + int scratch_remain; /* for statistic */ + struct block_io_ops io; +#ifdef V7FS_EI + struct endian_conversion_ops val; +#endif +#ifdef V7FS_LOCK + /* in-core superblock access. (freeblock/freeinode) split? -uch */ + struct lock_ops sb_lock; + struct lock_ops ilist_lock; /* disk ilist access. */ + struct lock_ops mem_lock; /* work memory allocation lock. */ +#endif + struct v7fs_superblock superblock; + struct v7fs_stat stat; + int endian; +}; + +struct v7fs_mount_device { + union { + void *vnode; /* NetBSD kernel */ + int fd; /* NetBSD newfs,fsck */ + const char *filename;/* misc test */ + } device; + daddr_t sectors; /*total size in sector. */ + int endian; +}; + +#define V7FS_ITERATOR_BREAK (-1) +#define V7FS_ITERATOR_END (-2) +#define V7FS_ITERATOR_ERROR (-3) +__BEGIN_DECLS +int v7fs_io_init(struct v7fs_self **, const struct v7fs_mount_device *, size_t); +void v7fs_io_fini(struct v7fs_self *); +void *scratch_read(struct v7fs_self *, daddr_t); +void scratch_free(struct v7fs_self *, void *); +int scratch_remain(const struct v7fs_self *); +__END_DECLS + +#if 0 +#define V7FS_IO_DEBUG +#define V7FS_SUPERBLOCK_DEBUG +#define V7FS_DATABLOCK_DEBUG +#define V7FS_INODE_DEBUG +#define V7FS_DIRENT_DEBUG +#define V7FS_FILE_DEBUG +#define V7FS_VFSOPS_DEBUG +#define V7FS_VNOPS_DEBUG +#endif + +#endif /*!_V7FS_IMPL_H_ */ diff --git a/sys/fs/v7fs/v7fs_inode.c b/sys/fs/v7fs/v7fs_inode.c new file mode 100644 index 000000000..c3e683f8a --- /dev/null +++ b/sys/fs/v7fs/v7fs_inode.c @@ -0,0 +1,301 @@ +/* $NetBSD: v7fs_inode.c,v 1.2 2011/07/18 21:51:49 apb Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: v7fs_inode.c,v 1.2 2011/07/18 21:51:49 apb Exp $"); +#if defined _KERNEL_OPT +#include "opt_v7fs.h" +#endif + +#ifdef _KERNEL +#include +#include +#else +#include +#include +#include +#include +#endif + +#include "v7fs.h" +#include "v7fs_impl.h" +#include "v7fs_endian.h" +#include "v7fs_inode.h" +#include "v7fs_superblock.h" + +#ifdef V7FS_INODE_DEBUG +#define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) +#else +#define DPRINTF(fmt, args...) ((void)0) +#endif + +static void v7fs_inode_setup_disk_image(const struct v7fs_self *, + struct v7fs_inode *, struct v7fs_inode_diskimage *); +static int v7fs_inode_inquire_disk_location(const struct v7fs_self *, + v7fs_ino_t, v7fs_daddr_t *, v7fs_daddr_t *); +#ifdef V7FS_INODE_DEBUG +static int v7fs_inode_block_sanity(const struct v7fs_superblock *, + v7fs_daddr_t); + +static int +v7fs_inode_block_sanity(const struct v7fs_superblock *sb, v7fs_daddr_t blk) +{ + + if ((blk < V7FS_ILIST_SECTOR) || (blk >= sb->datablock_start_sector)) { + DPRINTF("invalid inode block#%d (%d-%d)\n", blk, + V7FS_ILIST_SECTOR, sb->datablock_start_sector); + return ENOSPC; + } + + return 0; +} +#endif /* V7FS_INODE_DEBUG */ + +int +v7fs_inode_number_sanity(const struct v7fs_superblock *sb, v7fs_ino_t ino) +{ + + if (ino < V7FS_ROOT_INODE || ((size_t)ino >= V7FS_MAX_INODE(sb))) { + DPRINTF("invalid inode#%d (%d-%zu)\n", ino, + V7FS_ROOT_INODE, V7FS_MAX_INODE(sb)); + return ENOSPC; + } + + return 0; +} + +int +v7fs_inode_allocate(struct v7fs_self *fs, v7fs_ino_t *ino) +{ + struct v7fs_superblock *sb = &fs->superblock; + v7fs_ino_t inode_number; + int error = ENOSPC; + *ino = 0; + + SUPERB_LOCK(fs); + if (sb->total_freeinode == 0) { + DPRINTF("inode exhausted!(1)\n"); + goto errexit; + } + + /* If there is no free inode cache, update it. */ + if (sb->nfreeinode <= 0 && (error = v7fs_freeinode_update(fs))) { + DPRINTF("inode exhausted!(2)\n"); + goto errexit; + } + /* Get inode from superblock cache. */ + KDASSERT(sb->nfreeinode <= V7FS_MAX_FREEINODE); + inode_number = sb->freeinode[--sb->nfreeinode]; + sb->total_freeinode--; + sb->modified = 1; + + if ((error = v7fs_inode_number_sanity(sb, inode_number))) { + DPRINTF("new inode#%d %d %d\n", inode_number, sb->nfreeinode, + sb->total_freeinode); + DPRINTF("free inode list corupt\n"); + goto errexit; + } + *ino = inode_number; + +errexit: + SUPERB_UNLOCK(fs); + + return error; +} + +void +v7fs_inode_deallocate(struct v7fs_self *fs, v7fs_ino_t ino) +{ + struct v7fs_superblock *sb = &fs->superblock; + struct v7fs_inode inode; + + memset(&inode, 0, sizeof(inode)); + inode.inode_number = ino; + v7fs_inode_writeback(fs, &inode); + + SUPERB_LOCK(fs); + if (sb->nfreeinode < V7FS_MAX_FREEINODE) { + /* link to freeinode list. */ + sb->freeinode[sb->nfreeinode++] = ino; + } + /* If superblock inode cache is full, this inode charged by + v7fs_freeinode_update() later. */ + sb->total_freeinode++; + sb->modified = true; + SUPERB_UNLOCK(fs); +} + +void +v7fs_inode_setup_memory_image(const struct v7fs_self *fs __unused, + struct v7fs_inode *mem, struct v7fs_inode_diskimage *disk) +{ +#define conv16(m) (mem->m = V7FS_VAL16(fs, (disk->m))) +#define conv32(m) (mem->m = V7FS_VAL32(fs, (disk->m))) + uint32_t addr; + int i; + + memset(mem, 0, sizeof(*mem)); + conv16(mode); + conv16(nlink); + conv16(uid); + conv16(gid); + conv32(filesize); + conv32(atime); + conv32(mtime); + conv32(ctime); + + for (i = 0; i < V7FS_NADDR; i++) { + int j = i * 3; /* 3 byte each. (v7fs_daddr is 24bit) */ + /* expand to 4byte with endian conversion. */ + addr = V7FS_VAL24_READ(fs, &disk->addr[j]); + mem->addr[i] = addr; + } + mem->device = 0; + if (v7fs_inode_iscdev(mem) || v7fs_inode_isbdev(mem)) { + mem->device = mem->addr[0]; + } + +#undef conv16 +#undef conv32 +} + +static void +v7fs_inode_setup_disk_image(const struct v7fs_self *fs __unused, + struct v7fs_inode *mem, struct v7fs_inode_diskimage *disk) +{ +#define conv16(m) (disk->m = V7FS_VAL16(fs, (mem->m))) +#define conv32(m) (disk->m = V7FS_VAL32(fs, (mem->m))) + + conv16(mode); + conv16(nlink); + conv16(uid); + conv16(gid); + conv32(filesize); + conv32(atime); + conv32(mtime); + conv32(ctime); + + int i; + for (i = 0; i < V7FS_NADDR; i++) { + int j = i * 3; /* 3 byte each. */ + V7FS_VAL24_WRITE(fs, mem->addr[i], disk->addr + j); + } +#undef conv16 +#undef conv32 +} + +/* Load inode from disk. */ +int +v7fs_inode_load(struct v7fs_self *fs, struct v7fs_inode *p, v7fs_ino_t n) +{ + v7fs_daddr_t blk, ofs; + struct v7fs_inode_diskimage *di; + void *buf; + + if (v7fs_inode_inquire_disk_location(fs, n, &blk, &ofs) != 0) + return ENOENT; + + ILIST_LOCK(fs); + if (!(buf = scratch_read(fs, blk))) { + ILIST_UNLOCK(fs); + return EIO; + } + ILIST_UNLOCK(fs); + di = (struct v7fs_inode_diskimage *)buf; + + /* Decode disk address, convert endian. */ + v7fs_inode_setup_memory_image(fs, p, di + ofs); + p->inode_number = n; + + scratch_free(fs, buf); + + return 0; +} + +/* Write back inode to disk. */ +int +v7fs_inode_writeback(struct v7fs_self *fs, struct v7fs_inode *mem) +{ + struct v7fs_inode_diskimage disk; + v7fs_ino_t ino = mem->inode_number; + v7fs_daddr_t blk; + v7fs_daddr_t ofs; + void *buf; + int error = 0; + + if (v7fs_inode_inquire_disk_location(fs, ino, &blk, &ofs) != 0) + return ENOENT; + + v7fs_inode_setup_disk_image(fs, mem, &disk); + + ILIST_LOCK(fs); + if (!(buf = scratch_read(fs, blk))) { + ILIST_UNLOCK(fs); + return EIO; + } + struct v7fs_inode_diskimage *di = (struct v7fs_inode_diskimage *)buf; + di[ofs] = disk; /* structure copy; */ + if (!fs->io.write(fs->io.cookie, buf, blk)) + error = EIO; + ILIST_UNLOCK(fs); + + scratch_free(fs, buf); + + return error; +} + +static int +v7fs_inode_inquire_disk_location(const struct v7fs_self *fs + __unused, v7fs_ino_t n, v7fs_daddr_t *block, + v7fs_daddr_t *offset) +{ + v7fs_daddr_t ofs, blk; +#ifdef V7FS_INODE_DEBUG + v7fs_inode_number_sanity(&fs->superblock, n); +#endif + ofs = (n - 1/*inode start from 1*/) * + sizeof(struct v7fs_inode_diskimage); + blk = ofs >> V7FS_BSHIFT; + + *block = blk + V7FS_ILIST_SECTOR; + *offset = (ofs - blk * V7FS_BSIZE) / + sizeof(struct v7fs_inode_diskimage); +#ifdef V7FS_INODE_DEBUG + return v7fs_inode_block_sanity(&fs->superblock, *block); +#else + return 0; +#endif +} + diff --git a/sys/fs/v7fs/v7fs_inode.h b/sys/fs/v7fs/v7fs_inode.h new file mode 100644 index 000000000..7dc42db5a --- /dev/null +++ b/sys/fs/v7fs/v7fs_inode.h @@ -0,0 +1,91 @@ +/* $NetBSD: v7fs_inode.h,v 1.1 2011/06/27 11:52:25 uch Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _V7FS_INODE_H_ +#define _V7FS_INODE_H_ + +/* Software implementation of inode. (memory image) */ +struct v7fs_inode { + v7fs_ino_t inode_number; /* inode location */ + /* attr */ + uint16_t mode; + int16_t nlink; + int16_t uid; + int16_t gid; + v7fs_time_t atime; + v7fs_time_t mtime; + v7fs_time_t ctime; + /* open mode */ + bool append_mode; + + v7fs_dev_t device; /* for special file.(cdev, bdev) */ + /* payload. */ + v7fs_off_t filesize; /* size of file (byte) */ + v7fs_daddr_t addr[V7FS_NADDR]; /* data block address list */ +}; + +#define v7fs_inode_filesize(i) ((i)->filesize) +#define v7fs_inode_allocated(i) ((i)->mode) +#define v7fs_inode_nlink(i) ((i)->nlink) +/* V7 original */ +#define v7fs_inode_isdir(i) (((i)->mode & V7FS_IFMT) == V7FS_IFDIR) +#define v7fs_inode_isfile(i) (((i)->mode & V7FS_IFMT) == V7FS_IFREG) +#define v7fs_inode_iscdev(i) (((i)->mode & V7FS_IFMT) == V7FS_IFCHR) +#define v7fs_inode_isbdev(i) (((i)->mode & V7FS_IFMT) == V7FS_IFBLK) +/* 2BSD extension (implementation is different) */ +#define v7fs_inode_islnk(i) (((i)->mode & V7FS_IFMT) == V7FSBSD_IFLNK) +#define v7fs_inode_issock(i) (((i)->mode & V7FS_IFMT) == V7FSBSD_IFSOCK) +/* NetBSD Extension */ +#define v7fs_inode_isfifo(i) (((i)->mode & V7FS_IFMT) == V7FSBSD_IFFIFO) + +__BEGIN_DECLS +/* Free inode access ops. */ +int v7fs_inode_allocate(struct v7fs_self *, v7fs_ino_t *); +void v7fs_inode_deallocate(struct v7fs_self *, v7fs_ino_t); + +/* Disk I/O ops. */ +int v7fs_inode_load(struct v7fs_self *, struct v7fs_inode *, v7fs_ino_t); +int v7fs_inode_writeback(struct v7fs_self *, struct v7fs_inode *); +void v7fs_inode_setup_memory_image(const struct v7fs_self *, + struct v7fs_inode *, struct v7fs_inode_diskimage *); + +/* Check. */ +int v7fs_inode_number_sanity(const struct v7fs_superblock *, v7fs_ino_t); + +/* Util. */ +void v7fs_inode_chmod(struct v7fs_inode *, v7fs_mode_t); +void v7fs_inode_dump(const struct v7fs_inode *); + +/* Loop over all inode in ilist. */ +int v7fs_ilist_foreach(struct v7fs_self *, int (*)(struct v7fs_self *, void *, + struct v7fs_inode *, v7fs_ino_t), void *); +__END_DECLS +#endif /*!_V7FS_INODE_H_ */ diff --git a/sys/fs/v7fs/v7fs_inode_util.c b/sys/fs/v7fs/v7fs_inode_util.c new file mode 100644 index 000000000..94dfd7a3b --- /dev/null +++ b/sys/fs/v7fs/v7fs_inode_util.c @@ -0,0 +1,128 @@ +/* $NetBSD: v7fs_inode_util.c,v 1.2 2011/07/18 21:51:49 apb Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: v7fs_inode_util.c,v 1.2 2011/07/18 21:51:49 apb Exp $"); +#if defined _KERNEL_OPT +#include "opt_v7fs.h" +#endif + +#ifdef _KERNEL +#include +#include +#else +#include +#include +#endif + +#include "v7fs.h" +#include "v7fs_impl.h" +#include "v7fs_inode.h" + +#ifdef V7FS_INODE_DEBUG +#define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) +#else +#define DPRINTF(fmt, args...) ((void)0) +#endif + +void +v7fs_inode_chmod(struct v7fs_inode *inode, v7fs_mode_t mode) +{ +#define V7FS_MODE_MASK 0xfff + DPRINTF("setattr %08o -> %08o\n", inode->mode, mode); + + inode->mode &= ~V7FS_MODE_MASK; + inode->mode |= (mode & V7FS_MODE_MASK); + DPRINTF("setattr %08o -> %08o\n", inode->mode, mode); +} + +void +v7fs_inode_dump(const struct v7fs_inode *p) +{ + printf("nlink %d mode %06o %d/%d %d bytes\n", + p->nlink, p->mode, + p->uid, p->gid, p->filesize); + + printf("atime %d mtime %d ctime %d\n", + p->atime, p->mtime, p->ctime); +#ifndef _KERNEL + time_t at = p->atime; + time_t mt = p->mtime; + time_t ct = p->ctime; + printf(" atime %s mtime %s ctime %s", ctime(&at), ctime(&mt), + ctime(&ct)); +#endif + if (v7fs_inode_iscdev(p) || v7fs_inode_isbdev(p)) { + printf("device:%d/%d\n", (p->device >> 8), p->device & 0xff); + } + printf("\n"); +} + + +int +v7fs_ilist_foreach +(struct v7fs_self *fs, + int (*func)(struct v7fs_self *, void *, struct v7fs_inode *, v7fs_ino_t), + void *ctx) +{ + struct v7fs_superblock *sb = &fs->superblock; + size_t i, j, k; + int ret; + + /* Loop over ilist. */ + for (k = 1, i = V7FS_ILIST_SECTOR; i < sb->datablock_start_sector; + i++) { + struct v7fs_inode_diskimage *di; + struct v7fs_inode inode; + void *buf; + + if (!(buf = scratch_read(fs, i))) { + DPRINTF("block %zu I/O error.\n", i); + k += V7FS_INODE_PER_BLOCK; + continue; + } + di = (struct v7fs_inode_diskimage *)buf; + for (j = 0; j < V7FS_INODE_PER_BLOCK; j++, k++) { + v7fs_inode_setup_memory_image(fs, &inode, di + j); + inode.inode_number = k; + if ((ret = func(fs, ctx, &inode, k))) { + scratch_free(fs, buf); + return ret; + } + } + scratch_free(fs, buf); + } + return 0; +} diff --git a/sys/fs/v7fs/v7fs_io.c b/sys/fs/v7fs/v7fs_io.c new file mode 100644 index 000000000..3b5cca41a --- /dev/null +++ b/sys/fs/v7fs/v7fs_io.c @@ -0,0 +1,141 @@ +/* $NetBSD: v7fs_io.c,v 1.3 2013/06/28 14:49:14 christos Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: v7fs_io.c,v 1.3 2013/06/28 14:49:14 christos Exp $"); +#if defined _KERNEL_OPT +#include "opt_v7fs.h" +#endif + +#ifdef _KERNEL +#include +#else +#include +#include +#include +#endif + +#include "v7fs.h" +#include "v7fs_impl.h" + +#if defined _KERNEL +#define STATIC_BUFFER +#endif + +#ifdef V7FS_IO_DEBUG +#define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) +#else +#define DPRINTF(fmt, args...) ((void)0) +#endif + +void * +scratch_read(struct v7fs_self *fs, daddr_t blk) +{ +#ifdef STATIC_BUFFER + int i; + MEM_LOCK(fs); + for (i = 0; i < V7FS_SELF_NSCRATCH; i++) { + if (fs->scratch_free & (1 << i)) { + fs->scratch_free &= ~(1 << i); + break; + } + } + if (i == V7FS_SELF_NSCRATCH) { + DPRINTF("No scratch area. increase V7FS_SELF_NSCRATCH\n"); + assert(0); + MEM_UNLOCK(fs); + return NULL; + } + + if (!fs->io.read(fs->io.cookie, fs->scratch[i], blk)) { + DPRINTF("*** I/O error block %ld\n", (long)blk); + fs->scratch_free |= (1 << i); + MEM_UNLOCK(fs); + return NULL; + } + MEM_UNLOCK(fs); + /* Statistic */ + int n; + if ((n = scratch_remain(fs)) < fs->scratch_remain) + fs->scratch_remain = n; + + return fs->scratch[i]; +#else + uint8_t *buf = malloc(V7FS_BSIZE); + if (!fs->io.read(fs->io.cookie, buf, blk)) { + DPRINTF("*** I/O error block %ld\n",(long)blk); + free(buf); + return NULL; + } + return buf; +#endif +} + +int +scratch_remain(const struct v7fs_self *fs) +{ +#ifdef STATIC_BUFFER + int nfree; + int i; + MEM_LOCK(fs); + for (i = 0, nfree = 0; i < V7FS_SELF_NSCRATCH; i++) { + if (fs->scratch_free & (1 << i)) { + nfree++; + } + } + MEM_UNLOCK(fs); + return nfree; +#else + return -1; +#endif +} + +void +scratch_free(struct v7fs_self *fs __unused, void *p) +{ +#ifdef STATIC_BUFFER + int i; + MEM_LOCK(fs); + for (i = 0; i < V7FS_SELF_NSCRATCH; i++) + if (fs->scratch[i] == p) { + fs->scratch_free |= (1 << i); + break; + } + MEM_UNLOCK(fs); + assert(i != V7FS_SELF_NSCRATCH); +#else + free(p); +#endif +} diff --git a/sys/fs/v7fs/v7fs_io_kern.c b/sys/fs/v7fs/v7fs_io_kern.c new file mode 100644 index 000000000..0151dc616 --- /dev/null +++ b/sys/fs/v7fs/v7fs_io_kern.c @@ -0,0 +1,241 @@ +/* $NetBSD: v7fs_io_kern.c,v 1.2 2013/11/20 23:44:23 rmind Exp $ */ + +/*- + * Copyright (c) 2004, 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__KERNEL_RCSID(0, "$NetBSD: v7fs_io_kern.c,v 1.2 2013/11/20 23:44:23 rmind Exp $"); +#if defined _KERNEL_OPT +#include "opt_v7fs.h" +#endif +#include + +__KERNEL_RCSID(0, "$NetBSD: v7fs_io_kern.c,v 1.2 2013/11/20 23:44:23 rmind Exp $"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "v7fs_endian.h" +#include "v7fs_impl.h" + +#ifdef V7FS_IO_DEBUG +#define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) +#else +#define DPRINTF(fmt, args...) ((void)0) +#endif + +struct local_io { + struct vnode *vp; + kauth_cred_t cred; +}; + +static bool v7fs_os_read_n(void *, uint8_t *, daddr_t, int); +static bool v7fs_os_read(void *, uint8_t *, daddr_t); +static bool v7fs_os_write_n(void *, uint8_t *, daddr_t, int); +static bool v7fs_os_write(void *, uint8_t *, daddr_t); +static void v7fs_os_lock(void *); +static void v7fs_os_unlock(void *); +static bool lock_init(struct lock_ops *); + +int +v7fs_io_init(struct v7fs_self **fs, + const struct v7fs_mount_device *mount_device, size_t block_size) +{ + struct vnode *vp = mount_device->device.vnode; + struct v7fs_self *p; + struct local_io *local; + int error = 0; + + if ((p = kmem_zalloc(sizeof(*p), KM_SLEEP)) == NULL) + return ENOMEM; + + p->scratch_free = -1; + p->scratch_remain = V7FS_SELF_NSCRATCH; + + /* Endian */ + p->endian = mount_device->endian; +#ifdef V7FS_EI + v7fs_endian_init(p); +#endif + /* IO */ + if ((local = kmem_zalloc(sizeof(*local), KM_SLEEP)) == NULL) { + error = ENOMEM; + goto errexit; + } + p->io.read = v7fs_os_read; + p->io.read_n = v7fs_os_read_n; + p->io.write = v7fs_os_write; + p->io.write_n = v7fs_os_write_n; + p->scratch_free = -1; /* free all scratch buffer */ + + p->io.cookie = local; + local->vp = vp; + local->cred = NOCRED; /* upper layer check cred. */ + + /*LOCK */ + error = ENOMEM; + if (!lock_init(&p->sb_lock)) + goto errexit; + if (!lock_init(&p->ilist_lock)) + goto errexit; + if (!lock_init(&p->mem_lock)) + goto errexit; + error = 0; + + *fs = p; + return 0; + +errexit: + v7fs_io_fini(p); + return error; +} + +static bool +lock_init(struct lock_ops *ops) +{ + if ((ops->cookie = kmem_zalloc(sizeof(kmutex_t), KM_SLEEP)) == NULL) { + return false; + } + mutex_init(ops->cookie, MUTEX_DEFAULT, IPL_NONE); + ops->lock = v7fs_os_lock; + ops->unlock = v7fs_os_unlock; + return true; +} + +void +v7fs_io_fini(struct v7fs_self *fs) +{ + if (fs->io.cookie) { + kmem_free(fs->io.cookie, sizeof(struct local_io)); + } + if (fs->sb_lock.cookie) { + mutex_destroy(fs->sb_lock.cookie); + kmem_free(fs->sb_lock.cookie, sizeof(kmutex_t)); + } + if (fs->ilist_lock.cookie) { + mutex_destroy(fs->ilist_lock.cookie); + kmem_free(fs->ilist_lock.cookie, sizeof(kmutex_t)); + } + if (fs->mem_lock.cookie) { + mutex_destroy(fs->mem_lock.cookie); + kmem_free(fs->mem_lock.cookie, sizeof(kmutex_t)); + } + kmem_free(fs, sizeof(*fs)); +} + +static bool +v7fs_os_read_n(void *self, uint8_t *buf, daddr_t block, int count) +{ + int i; + + for (i = 0; i < count; i++) { + if (!v7fs_os_read(self, buf, block)) + return false; + buf += DEV_BSIZE; + block++; + } + + return true; +} + +static bool +v7fs_os_read(void *self, uint8_t *buf, daddr_t block) +{ + struct local_io *bio = (struct local_io *)self; + struct buf *bp = NULL; + + if (bread(bio->vp, block, DEV_BSIZE, bio->cred, 0, &bp) != 0) + goto error_exit; + memcpy(buf, bp->b_data, DEV_BSIZE); + brelse(bp, 0); + + return true; +error_exit: + DPRINTF("block %ld read failed.\n", (long)block); + + if (bp != NULL) + brelse(bp, 0); + return false; +} + +static bool +v7fs_os_write_n(void *self, uint8_t *buf, daddr_t block, int count) +{ + int i; + + for (i = 0; i < count; i++) { + if (!v7fs_os_write(self, buf, block)) + return false; + buf += DEV_BSIZE; + block++; + } + + return true; +} + +static bool +v7fs_os_write(void *self, uint8_t *buf, daddr_t block) +{ + struct local_io *bio = (struct local_io *)self; + struct buf *bp; + + if ((bp = getblk(bio->vp, block, DEV_BSIZE, 0, 0)) == 0) { + DPRINTF("getblk failed. block=%ld\n", (long)block); + return false; + } + + memcpy(bp->b_data, buf, DEV_BSIZE); + + if (bwrite(bp) != 0) { + DPRINTF("bwrite failed. block=%ld\n", (long)block); + return false; + } + + return true; +} + +static void +v7fs_os_lock(void *self) +{ + + mutex_enter((kmutex_t *)self); +} + +static void +v7fs_os_unlock(void *self) +{ + + mutex_exit((kmutex_t *)self); +} diff --git a/sys/fs/v7fs/v7fs_io_user.c b/sys/fs/v7fs/v7fs_io_user.c new file mode 100644 index 000000000..907a78b50 --- /dev/null +++ b/sys/fs/v7fs/v7fs_io_user.c @@ -0,0 +1,172 @@ +/* $NetBSD: v7fs_io_user.c,v 1.4 2011/08/08 11:42:30 uch Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#ifndef lint +__RCSID("$NetBSD: v7fs_io_user.c,v 1.4 2011/08/08 11:42:30 uch Exp $"); +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include "v7fs.h" +#include "v7fs_endian.h" +#include "v7fs_impl.h" + +#ifdef V7FS_IO_DEBUG +#define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) +#else +#define DPRINTF(fmt, args...) ((void)0) +#endif + +struct local_io { + int fd; + size_t size; + size_t blksz; + uint8_t *addr; +} local; + +static bool read_sector(void *, uint8_t *, daddr_t); +static bool write_sector(void *, uint8_t *, daddr_t); +static bool read_mmap(void *, uint8_t *, daddr_t); +static bool write_mmap(void *, uint8_t *, daddr_t); + +int +v7fs_io_init(struct v7fs_self **fs, const struct v7fs_mount_device *mount, + size_t block_size) +{ + struct v7fs_self *p; + + if (!(p = (struct v7fs_self *)malloc(sizeof(*p)))) + return ENOMEM; + memset(p, 0, sizeof(*p)); + + /* Endian */ + p->endian = mount->endian; +#ifdef V7FS_EI + v7fs_endian_init(p); +#endif + local.blksz = block_size; + local.fd = mount->device.fd; + local.size = mount->sectors * block_size; + local.addr = mmap(NULL, local.size, PROT_READ | PROT_WRITE | PROT_NONE, + MAP_FILE | MAP_SHARED/*writeback*/, local.fd, 0); + if (local.addr == MAP_FAILED) { + local.addr = 0; + p->io.read = read_sector; + p->io.write = write_sector; + } else { + DPRINTF("mmaped addr=%p\n", local.addr); + p->io.read = read_mmap; + p->io.write = write_mmap; + } + + p->io.cookie = &local; + *fs = p; + + return 0; +} + +void +v7fs_io_fini(struct v7fs_self *fs) +{ + struct local_io *lio = (struct local_io *)fs->io.cookie; + + if (lio->addr) { + if (munmap(lio->addr, lio->size) != 0) + warn(0); + } + fsync(lio->fd); + + free(fs); +} + +static bool +read_sector(void *ctx, uint8_t *buf, daddr_t sector) +{ + struct local_io *lio = (struct local_io *)ctx; + size_t blksz = lio->blksz; + int fd = lio->fd; + + if ((lseek(fd, (off_t)sector * blksz, SEEK_SET) < 0) || + (read(fd, buf, blksz) < (ssize_t)blksz)) { + warn("sector=%ld\n", (long)sector); + return false; + } + + return true; +} + +static bool +write_sector(void *ctx, uint8_t *buf, daddr_t sector) +{ + struct local_io *lio = (struct local_io *)ctx; + size_t blksz = lio->blksz; + int fd = lio->fd; + + if ((lseek(fd, (off_t)sector * blksz, SEEK_SET) < 0) || + (write(fd, buf, blksz) < (ssize_t)blksz)) { + warn("sector=%ld\n", (long)sector); + return false; + } + + return true; +} + +static bool +read_mmap(void *ctx, uint8_t *buf, daddr_t sector) +{ + struct local_io *lio = (struct local_io *)ctx; + size_t blksz = lio->blksz; + + memcpy(buf, lio->addr + sector * blksz, blksz); + + return true; +} + +static bool +write_mmap(void *ctx, uint8_t *buf, daddr_t sector) +{ + struct local_io *lio = (struct local_io *)ctx; + size_t blksz = lio->blksz; + + memcpy(lio->addr + sector * blksz, buf, blksz); + + return true; +} diff --git a/sys/fs/v7fs/v7fs_superblock.c b/sys/fs/v7fs/v7fs_superblock.c new file mode 100644 index 000000000..413e0faf7 --- /dev/null +++ b/sys/fs/v7fs/v7fs_superblock.c @@ -0,0 +1,263 @@ +/* $NetBSD: v7fs_superblock.c,v 1.2 2011/07/18 21:51:49 apb Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: v7fs_superblock.c,v 1.2 2011/07/18 21:51:49 apb Exp $"); +#if defined _KERNEL_OPT +#include "opt_v7fs.h" +#endif + +#ifdef _KERNEL +#include +#include /* errno */ +#else +#include +#include +#include +#endif + +#include "v7fs.h" +#include "v7fs_impl.h" +#include "v7fs_endian.h" +#include "v7fs_superblock.h" +#include "v7fs_inode.h" +#include "v7fs_datablock.h" + +#ifdef V7FS_SUPERBLOCK_DEBUG +#define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) +#define DPRINTF_(fmt, args...) printf(fmt, ##args) +#else +#define DPRINTF(fmt, args...) ((void)0) +#define DPRINTF_(fmt, args...) ((void)0) +#endif + +static void v7fs_superblock_endian_convert(struct v7fs_self *, + struct v7fs_superblock *, struct v7fs_superblock *); +static int v7fs_superblock_sanity(struct v7fs_self *); + +/* Load superblock from disk. */ +int +v7fs_superblock_load(struct v7fs_self *fs) +{ + struct v7fs_superblock *disksb; + void *buf; + int error; + + if (!(buf = scratch_read(fs, V7FS_SUPERBLOCK_SECTOR))) + return EIO; + disksb = (struct v7fs_superblock *)buf; + v7fs_superblock_endian_convert(fs, &fs->superblock, disksb); + scratch_free(fs, buf); + + if ((error = v7fs_superblock_sanity(fs))) + return error; + + return 0; +} + +/* Writeback superblock to disk. */ +int +v7fs_superblock_writeback(struct v7fs_self *fs) +{ + struct v7fs_superblock *memsb = &fs->superblock; + struct v7fs_superblock *disksb; + void *buf; + int error = 0; + + if (!memsb->modified) + return 0; + + if (!(buf = scratch_read(fs, V7FS_SUPERBLOCK_SECTOR))) + return EIO; + disksb = (struct v7fs_superblock *)buf; + v7fs_superblock_endian_convert(fs, disksb, memsb); + if (!fs->io.write(fs->io.cookie, buf, V7FS_SUPERBLOCK_SECTOR)) + error = EIO; + scratch_free(fs, buf); + + memsb->modified = 0; + DPRINTF("done. %d\n", error); + + return error; +} + +/* Check endian mismatch. */ +static int +v7fs_superblock_sanity(struct v7fs_self *fs) +{ + const struct v7fs_superblock *sb = &fs->superblock; + void *buf = 0; + + if ((sb->volume_size < 128) || /* smaller than 64KB. */ + (sb->datablock_start_sector > sb->volume_size) || + (sb->nfreeinode > V7FS_MAX_FREEINODE) || + (sb->nfreeblock > V7FS_MAX_FREEBLOCK) || + (sb->update_time < 0) || + (sb->total_freeblock > sb->volume_size) || + ((sb->nfreeinode == 0) && (sb->nfreeblock == 0) && + (sb->total_freeblock == 0) && (sb->total_freeinode == 0)) || + (!(buf = scratch_read(fs, sb->volume_size - 1)))) { + DPRINTF("invalid super block.\n"); + return EINVAL; + } + if (buf) + scratch_free(fs, buf); + + return 0; +} + +/* Fill free block to superblock cache. */ +int +v7fs_freeblock_update(struct v7fs_self *fs, v7fs_daddr_t blk) +{ + /* Assume superblock is locked by caller. */ + struct v7fs_superblock *sb = &fs->superblock; + struct v7fs_freeblock *fb; + void *buf; + int error; + + /* Read next freeblock table from disk. */ + if (!datablock_number_sanity(fs, blk) || !(buf = scratch_read(fs, blk))) + return EIO; + + /* Update in-core superblock freelist. */ + fb = (struct v7fs_freeblock *)buf; + if ((error = v7fs_freeblock_endian_convert(fs, fb))) { + scratch_free(fs, buf); + return error; + } + DPRINTF("freeblock table#%d, nfree=%d\n", blk, fb->nfreeblock); + + memcpy(sb->freeblock, fb->freeblock, sizeof(blk) * fb->nfreeblock); + sb->nfreeblock = fb->nfreeblock; + sb->modified = true; + scratch_free(fs, buf); + + return 0; +} + +int +v7fs_freeblock_endian_convert(struct v7fs_self *fs __unused, + struct v7fs_freeblock *fb __unused) +{ +#ifdef V7FS_EI + int i; + int16_t nfree; + + nfree = V7FS_VAL16(fs, fb->nfreeblock); + if (nfree <= 0 || nfree > V7FS_MAX_FREEBLOCK) { + DPRINTF("invalid freeblock list. %d (max=%d)\n", nfree, + V7FS_MAX_FREEBLOCK); + return ENOSPC; + } + fb->nfreeblock = nfree; + + for (i = 0; i < nfree; i++) { + fb->freeblock[i] = V7FS_VAL32(fs, fb->freeblock[i]); + } +#endif /* V7FS_EI */ + + return 0; +} + +/* Fill free inode to superblock cache. */ +int +v7fs_freeinode_update(struct v7fs_self *fs) +{ + /* Assume superblock is locked by caller. */ + struct v7fs_superblock *sb = &fs->superblock; + v7fs_ino_t *freeinode = sb->freeinode; + size_t i, j, k; + v7fs_ino_t ino; + + /* Loop over all inode list. */ + for (i = V7FS_ILIST_SECTOR, ino = 1/* inode start from 1*/, k = 0; + i < sb->datablock_start_sector; i++) { + struct v7fs_inode_diskimage *di; + void *buf; + if (!(buf = scratch_read(fs, i))) { + DPRINTF("block %zu I/O error.\n", i); + ino += V7FS_INODE_PER_BLOCK; + continue; + } + di = (struct v7fs_inode_diskimage *)buf; + + for (j = 0; + (j < V7FS_INODE_PER_BLOCK) && (k < V7FS_MAX_FREEINODE); + j++, di++, ino++) { + if (v7fs_inode_allocated(di)) + continue; + DPRINTF("free inode%d\n", ino); + freeinode[k++] = ino; + } + scratch_free(fs, buf); + } + sb->nfreeinode = k; + + return 0; +} + +static void +v7fs_superblock_endian_convert(struct v7fs_self *fs __unused, + struct v7fs_superblock *to, struct v7fs_superblock *from) +{ +#ifdef V7FS_EI +#define conv16(m) (to->m = V7FS_VAL16(fs, from->m)) +#define conv32(m) (to->m = V7FS_VAL32(fs, from->m)) + int i; + + conv16(datablock_start_sector); + conv32(volume_size); + conv16(nfreeblock); + v7fs_daddr_t *dfrom = from->freeblock; + v7fs_daddr_t *dto = to->freeblock; + for (i = 0; i < V7FS_MAX_FREEBLOCK; i++, dfrom++, dto++) + *dto = V7FS_VAL32(fs, *dfrom); + + conv16(nfreeinode); + v7fs_ino_t *ifrom = from->freeinode; + v7fs_ino_t *ito = to->freeinode; + for (i = 0; i < V7FS_MAX_FREEINODE; i++, ifrom++, ito++) + *ito = V7FS_VAL16(fs, *ifrom); + + conv32(update_time); + conv32(total_freeblock); + conv16(total_freeinode); +#undef conv16 +#undef conv32 +#else /* V7FS_EI */ + memcpy(to, from , sizeof(*to)); +#endif /* V7FS_EI */ +} diff --git a/sys/fs/v7fs/v7fs_superblock.h b/sys/fs/v7fs/v7fs_superblock.h new file mode 100644 index 000000000..b58716cee --- /dev/null +++ b/sys/fs/v7fs/v7fs_superblock.h @@ -0,0 +1,48 @@ +/* $NetBSD: v7fs_superblock.h,v 1.1 2011/06/27 11:52:25 uch Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _V7FS_SUPERBLOCK_H_ +#define _V7FS_SUPERBLOCK_H_ +struct v7fs_self; + +__BEGIN_DECLS +/* core */ +int v7fs_superblock_load(struct v7fs_self *); +int v7fs_superblock_writeback(struct v7fs_self *); +int v7fs_freeblock_update(struct v7fs_self *, v7fs_daddr_t); +int v7fs_freeblock_endian_convert(struct v7fs_self *, struct v7fs_freeblock *); +int v7fs_freeinode_update(struct v7fs_self *); + +/* util. */ +void v7fs_superblock_status(struct v7fs_self *); +void v7fs_superblock_dump(const struct v7fs_self *); +__END_DECLS +#endif /*!_V7FS_SUPERBLOCK_H_ */ diff --git a/sys/fs/v7fs/v7fs_superblock_util.c b/sys/fs/v7fs/v7fs_superblock_util.c new file mode 100644 index 000000000..27417ee35 --- /dev/null +++ b/sys/fs/v7fs/v7fs_superblock_util.c @@ -0,0 +1,101 @@ +/* $NetBSD: v7fs_superblock_util.c,v 1.2 2011/07/18 21:51:49 apb Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: v7fs_superblock_util.c,v 1.2 2011/07/18 21:51:49 apb Exp $"); +#if defined _KERNEL_OPT +#include "opt_v7fs.h" +#endif + +#ifdef _KERNEL +#include +#include /* errno */ +#else +#include +#include +#endif + +#include "v7fs.h" +#include "v7fs_impl.h" +#include "v7fs_superblock.h" +#include "v7fs_inode.h" + +#ifdef V7FS_SUPERBLOCK_DEBUG +#define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) +#define DPRINTF_(fmt, args...) printf(fmt, ##args) +#else +#define DPRINTF(fmt, args...) ((void)0) +#define DPRINTF_(fmt, args...) ((void)0) +#endif + +void +v7fs_superblock_status(struct v7fs_self *fs) +{ + struct v7fs_superblock *sb = &fs->superblock; + struct v7fs_stat *stat = &fs->stat; + + stat->total_blocks = sb->volume_size - sb->datablock_start_sector; + stat->total_inode = V7FS_MAX_INODE(sb); + stat->free_inode = sb->total_freeinode; + stat->free_blocks = sb->total_freeblock; + stat->total_files = stat->total_inode - sb->total_freeinode - 1; + + DPRINTF("block %d/%d, inode %d/%d\n", stat->free_blocks, + stat->total_blocks, stat->free_inode, stat->total_inode); +} + +void +v7fs_superblock_dump(const struct v7fs_self *fs) +{ + const struct v7fs_superblock *sb = &fs->superblock; + +#define print(x) printf("%s: %d\n", #x, sb->x) + print(datablock_start_sector); + print(volume_size); + print(nfreeblock); + print(nfreeinode); + print(update_time); + print(lock_freeblock); + print(lock_freeinode); + print(modified); + print(readonly); +#if !defined _KERNEL + time_t t = sb->update_time; + printf("%s", ctime(&t)); +#endif + print(total_freeblock); + print(total_freeinode); +#undef print +} diff --git a/sys/fs/v7fs/v7fs_vfsops.c b/sys/fs/v7fs/v7fs_vfsops.c new file mode 100644 index 000000000..e8e95ee86 --- /dev/null +++ b/sys/fs/v7fs/v7fs_vfsops.c @@ -0,0 +1,617 @@ +/* $NetBSD: v7fs_vfsops.c,v 1.9 2013/11/23 13:35:36 christos Exp $ */ + +/*- + * Copyright (c) 2004, 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__KERNEL_RCSID(0, "$NetBSD: v7fs_vfsops.c,v 1.9 2013/11/23 13:35:36 christos Exp $"); +#if defined _KERNEL_OPT +#include "opt_v7fs.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* v-node */ +#include +#include +/* devsw */ +#include + +#include "v7fs_extern.h" +#include "v7fs.h" +#include "v7fs_impl.h" +#include "v7fs_inode.h" +#include "v7fs_superblock.h" + +#ifdef V7FS_VFSOPS_DEBUG +#define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) +#else +#define DPRINTF(arg...) ((void)0) +#endif + +struct pool v7fs_node_pool; + +static int v7fs_mountfs(struct vnode *, struct mount *, int); +static int v7fs_openfs(struct vnode *, struct mount *, struct lwp *); +static void v7fs_closefs(struct vnode *, struct mount *); +static int is_v7fs_partition(struct vnode *); +static enum vtype v7fs_mode_to_vtype(v7fs_mode_t mode); +int v7fs_vnode_reload(struct mount *, struct vnode *); + +int +v7fs_mount(struct mount *mp, const char *path, void *data, size_t *data_len) +{ + struct lwp *l = curlwp; + struct v7fs_args *args = data; + struct v7fs_mount *v7fsmount = (void *)mp->mnt_data; + struct vnode *devvp = NULL; + int error = 0; + bool update = mp->mnt_flag & MNT_UPDATE; + + DPRINTF("mnt_flag=%x %s\n", mp->mnt_flag, update ? "update" : ""); + + if (*data_len < sizeof(*args)) + return EINVAL; + + if (mp->mnt_flag & MNT_GETARGS) { + if (!v7fsmount) + return EIO; + args->fspec = NULL; + args->endian = v7fsmount->core->endian; + *data_len = sizeof(*args); + return 0; + } + + DPRINTF("args->fspec=%s endian=%d\n", args->fspec, args->endian); + if (args->fspec == NULL) { + /* nothing to do. */ + return EINVAL; + } + + if (args->fspec != NULL) { + /* Look up the name and verify that it's sane. */ + error = namei_simple_user(args->fspec, + NSM_FOLLOW_NOEMULROOT, &devvp); + if (error != 0) + return (error); + DPRINTF("mount device=%lx\n", (long)devvp->v_rdev); + + if (!update) { + /* + * Be sure this is a valid block device + */ + if (devvp->v_type != VBLK) + error = ENOTBLK; + else if (bdevsw_lookup(devvp->v_rdev) == NULL) + error = ENXIO; + } else { + KDASSERT(v7fsmount); + /* + * Be sure we're still naming the same device + * used for our initial mount + */ + if (devvp != v7fsmount->devvp) { + DPRINTF("devvp %p != %p rootvp=%p\n", devvp, + v7fsmount->devvp, rootvp); + if (rootvp == v7fsmount->devvp) { + vrele(devvp); + devvp = rootvp; + vref(devvp); + } else { + error = EINVAL; + } + } + } + } + + /* + * If mount by non-root, then verify that user has necessary + * permissions on the device. + * + * Permission to update a mount is checked higher, so here we presume + * updating the mount is okay (for example, as far as securelevel goes) + * which leaves us with the normal check. + */ + if (error == 0) { + int accessmode = VREAD; + if (update ? + (mp->mnt_iflag & IMNT_WANTRDWR) != 0 : + (mp->mnt_flag & MNT_RDONLY) == 0) + accessmode |= VWRITE; + error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_MOUNT, + KAUTH_REQ_SYSTEM_MOUNT_DEVICE, mp, devvp, + KAUTH_ARG(accessmode)); + } + + if (error) { + vrele(devvp); + return error; + } + + if (!update) { + if ((error = v7fs_openfs(devvp, mp, l))) { + vrele(devvp); + return error; + } + + if ((error = v7fs_mountfs(devvp, mp, args->endian))) { + v7fs_closefs(devvp, mp); + VOP_UNLOCK(devvp); + vrele(devvp); + return error; + } + VOP_UNLOCK(devvp); + } else if (mp->mnt_flag & MNT_RDONLY) { + /* XXX: r/w -> read only */ + } + + return set_statvfs_info(path, UIO_USERSPACE, args->fspec, UIO_USERSPACE, + mp->mnt_op->vfs_name, mp, l); +} + +static int +is_v7fs_partition(struct vnode *devvp) +{ + struct dkwedge_info dkw; + int error; + + if ((error = getdiskinfo(devvp, &dkw)) != 0) { + DPRINTF("getdiskinfo=%d\n", error); + return error; + } + DPRINTF("ptype=%s size=%" PRIu64 "\n", dkw.dkw_ptype, dkw->dkw_size); + + return strcmp(dkw.dkw_ptype, DKW_PTYPE_V7) == 0 ? 0 : EINVAL; +} + +static int +v7fs_openfs(struct vnode *devvp, struct mount *mp, struct lwp *l) +{ + kauth_cred_t cred = l->l_cred; + int oflags; + int error; + + /* Flush buffer */ + vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY); + if ((error = vinvalbuf(devvp, V_SAVE, cred, l, 0, 0))) + goto unlock_exit; + + /* Open block device */ + oflags = FREAD; + if ((mp->mnt_flag & MNT_RDONLY) == 0) + oflags |= FWRITE; + + if ((error = VOP_OPEN(devvp, oflags, NOCRED)) != 0) { + DPRINTF("VOP_OPEN=%d\n", error); + goto unlock_exit; + } + + return 0; /* lock held */ + +unlock_exit: + VOP_UNLOCK(devvp); + + return error; +} + +static void +v7fs_closefs(struct vnode *devvp, struct mount *mp) +{ + int oflags = FREAD; + + if ((mp->mnt_flag & MNT_RDONLY) == 0) + oflags |= FWRITE; + + VOP_CLOSE(devvp, oflags, NOCRED); +} + +static int +v7fs_mountfs(struct vnode *devvp, struct mount *mp, int endian) +{ + struct v7fs_mount *v7fsmount; + int error; + struct v7fs_mount_device mount; + + DPRINTF("%d\n",endian); + + v7fsmount = kmem_zalloc(sizeof(*v7fsmount), KM_SLEEP); + if (v7fsmount == NULL) { + return ENOMEM; + } + v7fsmount->devvp = devvp; + v7fsmount->mountp = mp; + + mount.device.vnode = devvp; + mount.endian = endian; + + if ((error = v7fs_io_init(&v7fsmount->core, &mount, V7FS_BSIZE))) { + goto err_exit; + } + struct v7fs_self *fs = v7fsmount->core; + + if ((error = v7fs_superblock_load(fs))) { + v7fs_io_fini(fs); + goto err_exit; + } + + LIST_INIT(&v7fsmount->v7fs_node_head); + + mp->mnt_data = v7fsmount; + mp->mnt_stat.f_fsidx.__fsid_val[0] = (long)devvp->v_rdev; + mp->mnt_stat.f_fsidx.__fsid_val[1] = makefstype(MOUNT_V7FS); + mp->mnt_stat.f_fsid = mp->mnt_stat.f_fsidx.__fsid_val[0]; + mp->mnt_stat.f_namemax = V7FS_NAME_MAX; + mp->mnt_flag |= MNT_LOCAL; + mp->mnt_dev_bshift = V7FS_BSHIFT; + mp->mnt_fs_bshift = V7FS_BSHIFT; + + return 0; + +err_exit: + kmem_free(v7fsmount, sizeof(*v7fsmount)); + return error; +} + +int +v7fs_start(struct mount *mp, int flags) +{ + + DPRINTF("\n"); + /* Nothing to do. */ + return 0; +} + +int +v7fs_unmount(struct mount *mp, int mntflags) +{ + struct v7fs_mount *v7fsmount = (void *)mp->mnt_data; + int error; + + DPRINTF("%p\n", v7fsmount); + + if ((error = vflush(mp, NULLVP, + mntflags & MNT_FORCE ? FORCECLOSE : 0)) != 0) + return error; + + vn_lock(v7fsmount->devvp, LK_EXCLUSIVE | LK_RETRY); + error = VOP_CLOSE(v7fsmount->devvp, FREAD, NOCRED); + vput(v7fsmount->devvp); + + v7fs_io_fini(v7fsmount->core); + + kmem_free(v7fsmount, sizeof(*v7fsmount)); + mp->mnt_data = NULL; + mp->mnt_flag &= ~MNT_LOCAL; + + return 0; +} + +int +v7fs_root(struct mount *mp, struct vnode **vpp) +{ + struct vnode *vp; + int error; + + DPRINTF("\n"); + if ((error = VFS_VGET(mp, V7FS_ROOT_INODE, &vp)) != 0) { + DPRINTF("error=%d\n", error); + return error; + } + *vpp = vp; + DPRINTF("done.\n"); + + return 0; +} + +int +v7fs_statvfs(struct mount *mp, struct statvfs *f) +{ + struct v7fs_mount *v7fsmount = mp->mnt_data; + struct v7fs_self *fs = v7fsmount->core; + + DPRINTF("scratch remain=%d\n", fs->scratch_remain); + + v7fs_superblock_status(fs); + + f->f_bsize = V7FS_BSIZE; + f->f_frsize = V7FS_BSIZE; + f->f_iosize = V7FS_BSIZE; + f->f_blocks = fs->stat.total_blocks; + f->f_bfree = fs->stat.free_blocks; + f->f_bavail = fs->stat.free_blocks; + f->f_bresvd = 0; + f->f_files = fs->stat.total_files; + f->f_ffree = fs->stat.free_inode; + f->f_favail = f->f_ffree; + f->f_fresvd = 0; + copy_statvfs_info(f, mp); + + return 0; +} + +int +v7fs_sync(struct mount *mp, int waitfor, kauth_cred_t cred) +{ + struct v7fs_mount *v7fsmount = mp->mnt_data; + struct v7fs_self *fs = v7fsmount->core; + struct v7fs_node *v7fs_node; + struct v7fs_inode *inode; + struct vnode *v; + int err, error; + int retry_cnt; + + DPRINTF("\n"); + + v7fs_superblock_writeback(fs); + for (retry_cnt = 0; retry_cnt < 2; retry_cnt++) { + error = 0; + + mutex_enter(&mntvnode_lock); + for (v7fs_node = LIST_FIRST(&v7fsmount->v7fs_node_head); + v7fs_node != NULL; v7fs_node = LIST_NEXT(v7fs_node, link)) { + inode = &v7fs_node->inode; + if (!v7fs_inode_allocated(inode)) { + continue; + } + v = v7fs_node->vnode; + mutex_enter(v->v_interlock); + mutex_exit(&mntvnode_lock); + err = vget(v, LK_EXCLUSIVE | LK_NOWAIT); + if (err == 0) { + err = VOP_FSYNC(v, cred, FSYNC_WAIT, 0, 0); + vput(v); + } + if (err != 0) + error = err; + mutex_enter(&mntvnode_lock); + } + mutex_exit(&mntvnode_lock); + + if (error == 0) + break; + } + + return error; +} + +static enum vtype +v7fs_mode_to_vtype (v7fs_mode_t mode) +{ + enum vtype table[] = { VCHR, VDIR, VBLK, VREG, VLNK, VSOCK }; + + if ((mode & V7FS_IFMT) == V7FSBSD_IFFIFO) + return VFIFO; + + return table[((mode >> 13) & 7) - 1]; +} + +int +v7fs_vget(struct mount *mp, ino_t ino, struct vnode **vpp) +{ + struct v7fs_mount *v7fsmount = mp->mnt_data; + struct v7fs_self *fs = v7fsmount->core; + struct vnode *vp; + struct v7fs_node *v7fs_node; + struct v7fs_inode inode; + int error; + + /* Lookup requested i-node */ + if ((error = v7fs_inode_load(fs, &inode, ino))) { + DPRINTF("v7fs_inode_load failed.\n"); + return error; + } + +retry: + mutex_enter(&mntvnode_lock); + for (v7fs_node = LIST_FIRST(&v7fsmount->v7fs_node_head); + v7fs_node != NULL; v7fs_node = LIST_NEXT(v7fs_node, link)) { + if (v7fs_node->inode.inode_number == ino) { + vp = v7fs_node->vnode; + mutex_enter(vp->v_interlock); + mutex_exit(&mntvnode_lock); + if (vget(vp, LK_EXCLUSIVE) == 0) { + *vpp = vp; + return 0; + } else { + DPRINTF("retry!\n"); + goto retry; + } + } + } + mutex_exit(&mntvnode_lock); + + /* Allocate v-node. */ + if ((error = getnewvnode(VT_V7FS, mp, v7fs_vnodeop_p, NULL, &vp))) { + DPRINTF("getnewvnode error.\n"); + return error; + } + /* Lock vnode here */ + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + + /* Allocate i-node */ + vp->v_data = pool_get(&v7fs_node_pool, PR_WAITOK); + memset(vp->v_data, 0, sizeof(*v7fs_node)); + v7fs_node = vp->v_data; + mutex_enter(&mntvnode_lock); + LIST_INSERT_HEAD(&v7fsmount->v7fs_node_head, v7fs_node, link); + mutex_exit(&mntvnode_lock); + v7fs_node->vnode = vp; + v7fs_node->v7fsmount = v7fsmount; + v7fs_node->inode = inode;/*structure copy */ + v7fs_node->lockf = NULL; /* advlock */ + + genfs_node_init(vp, &v7fs_genfsops); + uvm_vnp_setsize(vp, v7fs_inode_filesize(&inode)); + + if (ino == V7FS_ROOT_INODE) { + vp->v_type = VDIR; + vp->v_vflag |= VV_ROOT; + } else { + vp->v_type = v7fs_mode_to_vtype(inode.mode); + + if (vp->v_type == VBLK || vp->v_type == VCHR) { + dev_t rdev = inode.device; + vp->v_op = v7fs_specop_p; + spec_node_init(vp, rdev); + } else if (vp->v_type == VFIFO) { + vp->v_op = v7fs_fifoop_p; + } + } + + *vpp = vp; + + return 0; +} + + +int +v7fs_fhtovp(struct mount *mp, struct fid *fid, struct vnode **vpp) +{ + + DPRINTF("\n"); + /* notyet */ + return EOPNOTSUPP; +} + +int +v7fs_vptofh(struct vnode *vpp, struct fid *fid, size_t *fh_size) +{ + + DPRINTF("\n"); + /* notyet */ + return EOPNOTSUPP; +} + +void +v7fs_init(void) +{ + + DPRINTF("\n"); + pool_init(&v7fs_node_pool, sizeof(struct v7fs_node), 0, 0, 0, + "v7fs_node_pool", &pool_allocator_nointr, IPL_NONE); +} + +void +v7fs_reinit(void) +{ + + /* Nothing to do. */ + DPRINTF("\n"); +} + +void +v7fs_done(void) +{ + + DPRINTF("\n"); + pool_destroy(&v7fs_node_pool); +} + +int +v7fs_gop_alloc(struct vnode *vp, off_t off, off_t len, int flags, + kauth_cred_t cred) +{ + + DPRINTF("\n"); + return 0; +} + +int +v7fs_mountroot(void) +{ + struct mount *mp; + int error; + + DPRINTF(""); + /* On mountroot, devvp (rootdev) is opened by vfs_mountroot */ + if ((error = is_v7fs_partition (rootvp))) + return error; + + if ((error = vfs_rootmountalloc(MOUNT_V7FS, "root_device", &mp))) { + DPRINTF("mountalloc error=%d\n", error); + vrele(rootvp); + return error; + } + + if ((error = v7fs_mountfs(rootvp, mp, _BYTE_ORDER))) { + DPRINTF("mountfs error=%d\n", error); + vfs_unbusy(mp, false, NULL); + vfs_destroy(mp); + return error; + } + + mountlist_append(mp); + + vfs_unbusy(mp, false, NULL); + + return 0; +} + +/* Reload disk inode information */ +int +v7fs_vnode_reload(struct mount *mp, struct vnode *vp) +{ + struct v7fs_mount *v7fsmount = mp->mnt_data; + struct v7fs_self *fs = v7fsmount->core; + struct v7fs_node *v7fs_node; + struct v7fs_inode *inode = &((struct v7fs_node *)vp->v_data)->inode; + int target_ino = inode->inode_number; + int error = 0; + + DPRINTF("#%d\n", target_ino); + mutex_enter(&mntvnode_lock); + for (v7fs_node = LIST_FIRST(&v7fsmount->v7fs_node_head); + v7fs_node != NULL; v7fs_node = LIST_NEXT(v7fs_node, link)) { + inode = &v7fs_node->inode; + if (!v7fs_inode_allocated(inode)) { + continue; + } + if (inode->inode_number == target_ino) { + error = v7fs_inode_load(fs, &v7fs_node->inode, + target_ino); + DPRINTF("sync #%d error=%d\n", target_ino, error); + break; + } + } + mutex_exit(&mntvnode_lock); + + return error; +} diff --git a/sys/fs/v7fs/v7fs_vnops.c b/sys/fs/v7fs/v7fs_vnops.c new file mode 100644 index 000000000..3491f810e --- /dev/null +++ b/sys/fs/v7fs/v7fs_vnops.c @@ -0,0 +1,1328 @@ +/* $NetBSD: v7fs_vnops.c,v 1.13 2013/11/20 23:44:23 rmind Exp $ */ + +/*- + * Copyright (c) 2004, 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__KERNEL_RCSID(0, "$NetBSD: v7fs_vnops.c,v 1.13 2013/11/20 23:44:23 rmind Exp $"); +#if defined _KERNEL_OPT +#include "opt_v7fs.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /*APPEND */ +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef V7FS_VNOPS_DEBUG +#define DPRINTF(fmt, args...) printf("%s: " fmt, __func__, ##args) +#else +#define DPRINTF(arg...) ((void)0) +#endif + +int v7fs_vnode_reload(struct mount *, struct vnode *); + +static v7fs_mode_t vtype_to_v7fs_mode(enum vtype); +static uint8_t v7fs_mode_to_d_type(v7fs_mode_t); + +static v7fs_mode_t +vtype_to_v7fs_mode(enum vtype type) +{ + /* Convert Vnode types to V7FS types (sys/vnode.h)*/ + v7fs_mode_t table[] = { 0, V7FS_IFREG, V7FS_IFDIR, V7FS_IFBLK, + V7FS_IFCHR, V7FSBSD_IFLNK, V7FSBSD_IFSOCK, + V7FSBSD_IFFIFO }; + return table[type]; +} + +static uint8_t +v7fs_mode_to_d_type(v7fs_mode_t mode) +{ + /* Convert V7FS types to dirent d_type (sys/dirent.h)*/ + + return (mode & V7FS_IFMT) >> 12; +} + +int +v7fs_lookup(void *v) +{ + struct vop_lookup_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + } */ *a = v; + struct vnode *dvp = a->a_dvp; + struct v7fs_node *parent_node = dvp->v_data; + struct v7fs_inode *parent = &parent_node->inode; + struct v7fs_self *fs = parent_node->v7fsmount->core;/* my filesystem */ + struct vnode *vpp; + struct componentname *cnp = a->a_cnp; + int nameiop = cnp->cn_nameiop; + const char *name = cnp->cn_nameptr; + int namelen = cnp->cn_namelen; + int flags = cnp->cn_flags; + bool isdotdot = flags & ISDOTDOT; + bool islastcn = flags & ISLASTCN; + v7fs_ino_t ino; + int error; +#ifdef V7FS_VNOPS_DEBUG + const char *opname[] = { "LOOKUP", "CREATE", "DELETE", "RENAME" }; +#endif + DPRINTF("'%s' op=%s flags=%d parent=%d %o %dbyte\n", name, + opname[nameiop], cnp->cn_flags, parent->inode_number, parent->mode, + parent->filesize); + + *a->a_vpp = 0; + + /* Check directory permission for search */ + if ((error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred))) { + DPRINTF("***perm.\n"); + return error; + } + + /* Deny last component write operation on a read-only mount */ + if (islastcn && (dvp->v_mount->mnt_flag & MNT_RDONLY) && + (nameiop == DELETE || nameiop == RENAME)) { + DPRINTF("***ROFS.\n"); + return EROFS; + } + + /* "." */ + if (namelen == 1 && name[0] == '.') { + if ((nameiop == RENAME) && islastcn) { + return EISDIR; /* t_vnops rename_dir(3) */ + } + vref(dvp); /* v_usecount++ */ + *a->a_vpp = dvp; + DPRINTF("done.(.)\n"); + return 0; + } + + /* ".." and reguler file. */ + if ((error = v7fs_file_lookup_by_name(fs, parent, name, &ino))) { + /* Not found. Tell this entry be able to allocate. */ + if (((nameiop == CREATE) || (nameiop == RENAME)) && islastcn) { + /* Check directory permission to allocate. */ + if ((error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred))) { + DPRINTF("access denied. (%s)\n", name); + return error; + } + DPRINTF("EJUSTRETURN op=%d (%s)\n", nameiop, name); + return EJUSTRETURN; + } + DPRINTF("lastcn=%d\n", flags & ISLASTCN); + return error; + } + + if ((nameiop == DELETE) && islastcn) { + if ((error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred))) { + DPRINTF("access denied. (%s)\n", name); + return error; + } + } + + /* Entry found. Allocate v-node */ + // Check permissions? + vpp = 0; + if (isdotdot) { + VOP_UNLOCK(dvp); /* preserve reference count. (not vput) */ + } + DPRINTF("enter vget\n"); + if ((error = v7fs_vget(dvp->v_mount, ino, &vpp))) { + DPRINTF("***can't get vnode.\n"); + return error; + } + DPRINTF("exit vget\n"); + if (isdotdot) { + vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY); + } + *a->a_vpp = vpp; + DPRINTF("done.(%s)\n", name); + + return 0; +} + +int +v7fs_create(void *v) +{ + struct vop_create_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *a = v; + struct v7fs_node *parent_node = a->a_dvp->v_data; + struct v7fs_mount *v7fsmount = parent_node->v7fsmount; + struct v7fs_self *fs = v7fsmount->core; + struct mount *mp = v7fsmount->mountp; + struct v7fs_fileattr attr; + struct vattr *va = a->a_vap; + kauth_cred_t cr = a->a_cnp->cn_cred; + v7fs_ino_t ino; + int error = 0; + + DPRINTF("%s parent#%d\n", a->a_cnp->cn_nameptr, + parent_node->inode.inode_number); + KDASSERT((va->va_type == VREG) || (va->va_type == VSOCK)); + + memset(&attr, 0, sizeof(attr)); + attr.uid = kauth_cred_geteuid(cr); + attr.gid = kauth_cred_getegid(cr); + attr.mode = va->va_mode | vtype_to_v7fs_mode (va->va_type); + attr.device = 0; + + /* Allocate disk entry. and register its entry to parent directory. */ + if ((error = v7fs_file_allocate(fs, &parent_node->inode, + a->a_cnp->cn_nameptr, &attr, &ino))) { + DPRINTF("v7fs_file_allocate failed.\n"); + goto unlock_exit; + } + /* Sync dirent size change. */ + uvm_vnp_setsize(a->a_dvp, v7fs_inode_filesize(&parent_node->inode)); + + /* Get myself vnode. */ + *a->a_vpp = 0; + if ((error = v7fs_vget(mp, ino, a->a_vpp))) { + DPRINTF("v7fs_vget failed.\n"); + goto unlock_exit; + } + + /* Scheduling update time. real update by v7fs_update */ + struct v7fs_node *newnode = (*a->a_vpp)->v_data; + newnode->update_ctime = true; + newnode->update_mtime = true; + newnode->update_atime = true; + DPRINTF("allocated %s->#%d\n", a->a_cnp->cn_nameptr, ino); + +unlock_exit: + /* unlock parent directory */ + vput(a->a_dvp); /* locked at v7fs_lookup(); */ + + return error; +} + +int +v7fs_mknod(void *v) +{ + struct vop_mknod_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *a = v; + struct componentname *cnp = a->a_cnp; + kauth_cred_t cr = cnp->cn_cred; + struct vnode *dvp = a->a_dvp; + struct vattr *va = a->a_vap; + struct v7fs_node *parent_node = dvp->v_data; + struct v7fs_mount *v7fsmount = parent_node->v7fsmount; + struct v7fs_self *fs = v7fsmount->core; + struct mount *mp = v7fsmount->mountp; + struct v7fs_fileattr attr; + + v7fs_ino_t ino; + int error = 0; + + DPRINTF("%s %06o %lx %d\n", cnp->cn_nameptr, va->va_mode, + (long)va->va_rdev, va->va_type); + memset(&attr, 0, sizeof(attr)); + attr.uid = kauth_cred_geteuid(cr); + attr.gid = kauth_cred_getegid(cr); + attr.mode = va->va_mode | vtype_to_v7fs_mode(va->va_type); + attr.device = va->va_rdev; + + if ((error = v7fs_file_allocate(fs, &parent_node->inode, + cnp->cn_nameptr, &attr, &ino))) + goto unlock_exit; + /* Sync dirent size change. */ + uvm_vnp_setsize(dvp, v7fs_inode_filesize(&parent_node->inode)); + + if ((error = v7fs_vget(mp, ino, a->a_vpp))) { + DPRINTF("can't get vnode.\n"); + goto unlock_exit; + } + struct v7fs_node *newnode = (*a->a_vpp)->v_data; + newnode->update_ctime = true; + newnode->update_mtime = true; + newnode->update_atime = true; + +unlock_exit: + vput(dvp); + + return error; +} + +int +v7fs_open(void *v) +{ + struct vop_open_args /* { + struct vnode *a_vp; + int a_mode; + kauth_cred_t a_cred; + } */ *a = v; + + struct vnode *vp = a->a_vp; + struct v7fs_node *v7node = vp->v_data; + struct v7fs_inode *inode = &v7node->inode; + + DPRINTF("inode %d\n", inode->inode_number); + /* Append mode file pointer is managed by kernel. */ + if (inode->append_mode && + ((a->a_mode & (FWRITE | O_APPEND)) == FWRITE)) { + DPRINTF("file is already opened by append mode.\n"); + return EPERM; + } + + return 0; +} + +int +v7fs_close(void *v) +{ + struct vop_close_args /* { + struct vnodeop_desc *a_desc; + struct vnode *a_vp; + int a_fflag; + kauth_cred_t a_cred; + } */ *a = v; + struct vnode *vp = a->a_vp; +#ifdef V7FS_VNOPS_DEBUG + struct v7fs_node *v7node = vp->v_data; + struct v7fs_inode *inode = &v7node->inode; +#endif + DPRINTF("#%d (i)%dbyte (v)%zubyte\n", inode->inode_number, + v7fs_inode_filesize(inode), vp->v_size); + + /* Update timestamp */ + v7fs_update(vp, 0, 0, UPDATE_WAIT); + + return 0; +} + +static int +v7fs_check_possible(struct vnode *vp, struct v7fs_node *v7node, + mode_t mode) +{ + + if (!(mode & VWRITE)) + return 0; + + switch (vp->v_type) { + default: + /* special file is always writable. */ + return 0; + case VDIR: + case VLNK: + case VREG: + break; + } + + return vp->v_mount->mnt_flag & MNT_RDONLY ? EROFS : 0; +} + +static int +v7fs_check_permitted(struct vnode *vp, struct v7fs_node *v7node, + mode_t mode, kauth_cred_t cred) +{ + + struct v7fs_inode *inode = &v7node->inode; + + return kauth_authorize_vnode(cred, KAUTH_ACCESS_ACTION(mode, + vp->v_type, inode->mode), vp, NULL, genfs_can_access(vp->v_type, + inode->mode, inode->uid, inode->gid, mode, cred)); +} + +int +v7fs_access(void *v) +{ + struct vop_access_args /* { + struct vnode *a_vp; + int a_mode; + kauth_cred_t a_cred; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct v7fs_node *v7node = vp->v_data; + int error; + + error = v7fs_check_possible(vp, v7node, ap->a_mode); + if (error) + return error; + + error = v7fs_check_permitted(vp, v7node, ap->a_mode, ap->a_cred); + + return error; +} + +int +v7fs_getattr(void *v) +{ + struct vop_getattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + kauth_cred_t a_cred; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct v7fs_node *v7node = vp->v_data; + struct v7fs_inode *inode = &v7node->inode; + struct v7fs_mount *v7fsmount = v7node->v7fsmount; + struct vattr *vap = ap->a_vap; + + DPRINTF("\n"); + vap->va_type = vp->v_type; + vap->va_mode = inode->mode; + vap->va_nlink = inode->nlink; + vap->va_uid = inode->uid; + vap->va_gid = inode->gid; + vap->va_fsid = v7fsmount->devvp->v_rdev; + vap->va_fileid = inode->inode_number; + vap->va_size = vp->v_size; + vap->va_atime.tv_sec = inode->atime; + vap->va_mtime.tv_sec = inode->mtime; + vap->va_ctime.tv_sec = inode->ctime; + vap->va_birthtime.tv_sec = 0; + vap->va_gen = 1; + vap->va_flags = inode->append_mode ? SF_APPEND : 0; + vap->va_rdev = inode->device; + vap->va_bytes = vap->va_size; /* No sparse support. */ + vap->va_filerev = 0; + vap->va_vaflags = 0; + /* PAGE_SIZE is larger than sizeof(struct dirent). OK. + getcwd_scandir()@vfs_getcwd.c */ + vap->va_blocksize = PAGE_SIZE; + + return 0; +} + +int +v7fs_setattr(void *v) +{ + struct vop_setattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + kauth_cred_t a_cred; + struct proc *p; + } */ *ap = v; + struct vnode *vp = ap->a_vp; + struct vattr *vap = ap->a_vap; + struct v7fs_node *v7node = vp->v_data; + struct v7fs_self *fs = v7node->v7fsmount->core; + struct v7fs_inode *inode = &v7node->inode; + kauth_cred_t cred = ap->a_cred; + struct timespec *acc, *mod; + int error = 0; + acc = mod = NULL; + + DPRINTF("\n"); + + if (vp->v_mount->mnt_flag & MNT_RDONLY) { + switch (vp->v_type) { + default: + /* special file is always writable. */ + break; + case VDIR: + case VLNK: + case VREG: + DPRINTF("read-only mount\n"); + return EROFS; + } + } + + if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) || + (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) || + (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) || + ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) { + DPRINTF("invalid request\n"); + return EINVAL; + } + /* File pointer mode. */ + if (vap->va_flags != VNOVAL) { + error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_FLAGS, + vp, NULL, genfs_can_chflags(cred, vp->v_type, inode->uid, + false)); + if (error) + return error; + inode->append_mode = vap->va_flags & SF_APPEND; + } + + /* File size change. */ + if ((vap->va_size != VNOVAL) && (vp->v_type == VREG)) { + error = v7fs_datablock_size_change(fs, vap->va_size, inode); + if (error == 0) + uvm_vnp_setsize(vp, vap->va_size); + } + uid_t uid = inode->uid; + gid_t gid = inode->gid; + + if (vap->va_uid != (uid_t)VNOVAL) { + uid = vap->va_uid; + error = kauth_authorize_vnode(cred, + KAUTH_VNODE_CHANGE_OWNERSHIP, vp, NULL, + genfs_can_chown(cred, inode->uid, inode->gid, uid, + gid)); + if (error) + return error; + inode->uid = uid; + } + if (vap->va_gid != (uid_t)VNOVAL) { + gid = vap->va_gid; + error = kauth_authorize_vnode(cred, + KAUTH_VNODE_CHANGE_OWNERSHIP, vp, NULL, + genfs_can_chown(cred, inode->uid, inode->gid, uid, + gid)); + if (error) + return error; + inode->gid = gid; + } + if (vap->va_mode != (mode_t)VNOVAL) { + mode_t mode = vap->va_mode; + error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_SECURITY, + vp, NULL, genfs_can_chmod(vp->v_type, cred, inode->uid, inode->gid, + mode)); + if (error) { + return error; + } + v7fs_inode_chmod(inode, mode); + } + if ((vap->va_atime.tv_sec != VNOVAL) || + (vap->va_mtime.tv_sec != VNOVAL) || + (vap->va_ctime.tv_sec != VNOVAL)) { + error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_TIMES, vp, + NULL, genfs_can_chtimes(vp, vap->va_vaflags, inode->uid, + cred)); + if (error) + return error; + + if (vap->va_atime.tv_sec != VNOVAL) { + acc = &vap->va_atime; + } + if (vap->va_mtime.tv_sec != VNOVAL) { + mod = &vap->va_mtime; + v7node->update_mtime = true; + } + if (vap->va_ctime.tv_sec != VNOVAL) { + v7node->update_ctime = true; + } + } + + v7node->update_atime = true; + v7fs_update(vp, acc, mod, 0); + + return error; +} + +int +v7fs_read(void *v) +{ + struct vop_read_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + kauth_cred_t a_cred; + } */ *a = v; + struct vnode *vp = a->a_vp; + struct uio *uio = a->a_uio; + struct v7fs_node *v7node = vp->v_data; + struct v7fs_inode *inode = &v7node->inode; + vsize_t sz, filesz = v7fs_inode_filesize(inode); + const int advice = IO_ADV_DECODE(a->a_ioflag); + int error = 0; + + DPRINTF("type=%d inode=%d\n", vp->v_type, v7node->inode.inode_number); + + while (uio->uio_resid > 0) { + if ((sz = MIN(filesz - uio->uio_offset, uio->uio_resid)) == 0) + break; + + error = ubc_uiomove(&vp->v_uobj, uio, sz, advice, UBC_READ | + UBC_PARTIALOK | UBC_UNMAP_FLAG(v)); + if (error) { + break; + } + DPRINTF("read %zubyte\n", sz); + } + v7node->update_atime = true; + + return error; +} + +int +v7fs_write(void *v) +{ + struct vop_write_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + kauth_cred_t a_cred; + } */ *a = v; + struct vnode *vp = a->a_vp; + struct uio *uio = a->a_uio; + int advice = IO_ADV_DECODE(a->a_ioflag); + struct v7fs_node *v7node = vp->v_data; + struct v7fs_inode *inode = &v7node->inode; + struct v7fs_self *fs = v7node->v7fsmount->core; + vsize_t sz; + int error = 0; + + if (uio->uio_resid == 0) + return 0; + + sz = v7fs_inode_filesize(inode); + DPRINTF("(i)%ld (v)%zu ofs=%zu + res=%zu = %zu\n", sz, vp->v_size, + uio->uio_offset, uio->uio_resid, uio->uio_offset + uio->uio_resid); + + /* Append mode file offset is managed by kernel. */ + if (a->a_ioflag & IO_APPEND) + uio->uio_offset = sz; + + /* If write region is over filesize, expand. */ + size_t newsize= uio->uio_offset + uio->uio_resid; + ssize_t expand = newsize - sz; + if (expand > 0) { + if ((error = v7fs_datablock_expand(fs, inode, expand))) + return error; + uvm_vnp_setsize(vp, newsize); + } + + while (uio->uio_resid > 0) { + sz = uio->uio_resid; + if ((error = ubc_uiomove(&vp->v_uobj, uio, sz, advice, + UBC_WRITE | UBC_UNMAP_FLAG(v)))) + break; + DPRINTF("write %zubyte\n", sz); + } + v7node->update_mtime = true; + + return error; +} + +int +v7fs_fsync(void *v) +{ + struct vop_fsync_args /* { + struct vnode *a_vp; + kauth_cred_t a_cred; + int a_flags; + off_t offlo; + off_t offhi; + } */ *a = v; + struct vnode *vp = a->a_vp; + int error, wait; + + DPRINTF("%p\n", a->a_vp); + if (a->a_flags & FSYNC_CACHE) { + return EOPNOTSUPP; + } + + wait = (a->a_flags & FSYNC_WAIT); + error = vflushbuf(vp, a->a_flags); + + if (error == 0 && (a->a_flags & FSYNC_DATAONLY) == 0) + error = v7fs_update(vp, NULL, NULL, wait ? UPDATE_WAIT : 0); + + return error; +} + +int +v7fs_remove(void *v) +{ + struct vop_remove_args /* { + struct vnodeop_desc *a_desc; + struct vnode * a_dvp; + struct vnode * a_vp; + struct componentname * a_cnp; + } */ *a = v; + struct v7fs_node *parent_node = a->a_dvp->v_data; + struct v7fs_mount *v7fsmount = parent_node->v7fsmount; + struct vnode *vp = a->a_vp; + struct v7fs_inode *inode = &((struct v7fs_node *)vp->v_data)->inode; + struct vnode *dvp = a->a_dvp; + struct v7fs_self *fs = v7fsmount->core; + bool remove; + int error = 0; + + DPRINTF("delete %s\n", a->a_cnp->cn_nameptr); + + if (vp->v_type == VDIR) { + error = EPERM; + goto out; + } + + remove = v7fs_inode_nlink(inode) == 1; + if (remove) + uvm_vnp_setsize(vp, 0); + + if ((error = v7fs_file_deallocate(fs, &parent_node->inode, + a->a_cnp->cn_nameptr))) { + DPRINTF("v7fs_file_delete failed.\n"); + goto out; + } + /* Sync dirent size change. */ + uvm_vnp_setsize(dvp, v7fs_inode_filesize(&parent_node->inode)); + /* This inode is no longer used. -> v7fs_inactive */ + if (remove) + memset(inode, 0, sizeof(*inode)); + +out: + if (dvp == vp) + vrele(vp); /* v_usecount-- of unlocked vp */ + else + vput(vp); /* unlock vp and then v_usecount-- */ + vput(dvp); + + return error; +} + +int +v7fs_link(void *v) +{ + struct vop_link_args /* { + struct vnode *a_dvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *a = v; + struct vnode *dvp = a->a_dvp; + struct vnode *vp = a->a_vp; + struct v7fs_node *parent_node = dvp->v_data; + struct v7fs_node *node = vp->v_data; + struct v7fs_inode *parent = &parent_node->inode; + struct v7fs_inode *p = &node->inode; + struct v7fs_self *fs = node->v7fsmount->core; + struct componentname *cnp = a->a_cnp; + int error = 0; + + DPRINTF("%p\n", vp); + /* Lock soruce file */ + if ((error = vn_lock(vp, LK_EXCLUSIVE))) { + DPRINTF("lock failed. %p\n", vp); + VOP_ABORTOP(dvp, cnp); + goto unlock; + } + error = v7fs_file_link(fs, parent, p, cnp->cn_nameptr); + /* Sync dirent size change. */ + uvm_vnp_setsize(dvp, v7fs_inode_filesize(&parent_node->inode)); + + VOP_UNLOCK(vp); +unlock: + vput(dvp); + + return error; +} + +int +v7fs_rename(void *v) +{ + struct vop_rename_args /* { + struct vnode *a_fdvp; from parent-directory + struct vnode *a_fvp; from file + struct componentname *a_fcnp; + struct vnode *a_tdvp; to parent-directory + struct vnode *a_tvp; to file + struct componentname *a_tcnp; + } */ *a = v; + struct vnode *fvp = a->a_fvp; + struct vnode *tvp = a->a_tvp; + struct vnode *fdvp = a->a_fdvp; + struct vnode *tdvp = a->a_tdvp; + struct v7fs_node *parent_from = fdvp->v_data; + struct v7fs_node *parent_to = tdvp->v_data; + struct v7fs_node *v7node = fvp->v_data; + struct v7fs_self *fs = v7node->v7fsmount->core; + const char *from_name = a->a_fcnp->cn_nameptr; + const char *to_name = a->a_tcnp->cn_nameptr; + int error; + + DPRINTF("%s->%s %p %p\n", from_name, to_name, fvp, tvp); + + if ((fvp->v_mount != tdvp->v_mount) || + (tvp && (fvp->v_mount != tvp->v_mount))) { + error = EXDEV; + DPRINTF("cross-device link\n"); + goto out; + } + // XXXsource file lock? + error = v7fs_file_rename(fs, &parent_from->inode, from_name, + &parent_to->inode, to_name); + /* 'to file' inode may be changed. (hard-linked and it is cached.) + t_vnops rename_reg_nodir */ + if (tvp) { + v7fs_vnode_reload(parent_from->v7fsmount->mountp, tvp); + } + /* Sync dirent size change. */ + uvm_vnp_setsize(tdvp, v7fs_inode_filesize(&parent_to->inode)); + uvm_vnp_setsize(fdvp, v7fs_inode_filesize(&parent_from->inode)); +out: + if (tvp) + vput(tvp); /* locked on entry */ + if (tdvp == tvp) + vrele(tdvp); + else + vput(tdvp); + vrele(fdvp); + vrele(fvp); + + return error; +} + +int +v7fs_mkdir(void *v) +{ + struct vop_mkdir_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *a = v; + struct componentname *cnp = a->a_cnp; + kauth_cred_t cr = cnp->cn_cred; + struct vnode *dvp = a->a_dvp; + struct vattr *va = a->a_vap; + struct v7fs_node *parent_node = dvp->v_data; + struct v7fs_mount *v7fsmount = parent_node->v7fsmount; + struct v7fs_self *fs = v7fsmount->core; + struct v7fs_fileattr attr; + struct mount *mp = v7fsmount->mountp; + v7fs_ino_t ino; + int error = 0; + + DPRINTF("\n"); + memset(&attr, 0, sizeof(attr)); + attr.uid = kauth_cred_geteuid(cr); + attr.gid = kauth_cred_getegid(cr); + attr.mode = va->va_mode | vtype_to_v7fs_mode(va->va_type); + + if ((error = v7fs_file_allocate(fs, &parent_node->inode, + cnp->cn_nameptr, &attr, &ino))) + goto unlock_exit; + /* Sync dirent size change. */ + uvm_vnp_setsize(dvp, v7fs_inode_filesize(&parent_node->inode)); + + if ((error = v7fs_vget(mp, ino, a->a_vpp))) { + DPRINTF("can't get vnode.\n"); + } + struct v7fs_node *newnode = (*a->a_vpp)->v_data; + newnode->update_ctime = true; + newnode->update_mtime = true; + newnode->update_atime = true; + +unlock_exit: + vput(dvp); + + return error; +} + +int +v7fs_rmdir(void *v) +{ + struct vop_rmdir_args /* { + struct vnode *a_dvp; + struct vnode *a_vp; + struct componentname *a_cnp; + } */ *a = v; + struct vnode *vp = a->a_vp; + struct vnode *dvp = a->a_dvp; + struct v7fs_node *parent_node = dvp->v_data; + struct v7fs_mount *v7fsmount = parent_node->v7fsmount; + struct v7fs_inode *inode = &((struct v7fs_node *)vp->v_data)->inode; + struct v7fs_self *fs = v7fsmount->core; + int error = 0; + + DPRINTF("delete %s\n", a->a_cnp->cn_nameptr); + + KDASSERT(vp->v_type == VDIR); + + if ((error = v7fs_file_deallocate(fs, &parent_node->inode, + a->a_cnp->cn_nameptr))) { + DPRINTF("v7fs_directory_deallocate failed.\n"); + goto out; + } + uvm_vnp_setsize(vp, 0); + /* Sync dirent size change. */ + uvm_vnp_setsize(dvp, v7fs_inode_filesize(&parent_node->inode)); + /* This inode is no longer used. -> v7fs_inactive */ + memset(inode, 0, sizeof(*inode)); +out: + vput(vp); + vput(dvp); + + return error; +} + +struct v7fs_readdir_arg { + struct dirent *dp; + struct uio *uio; + int start; + int end; + int cnt; +}; +static int readdir_subr(struct v7fs_self *, void *, v7fs_daddr_t, size_t); + +int +readdir_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, size_t sz) +{ + struct v7fs_readdir_arg *p = (struct v7fs_readdir_arg *)ctx; + struct v7fs_dirent *dir; + struct dirent *dp = p->dp; + struct v7fs_inode inode; + char filename[V7FS_NAME_MAX + 1]; + int i, n; + int error = 0; + void *buf; + + if (!(buf = scratch_read(fs, blk))) + return EIO; + dir = (struct v7fs_dirent *)buf; + + n = sz / sizeof(*dir); + + for (i = 0; (i < n) && (p->cnt < p->end); i++, dir++, p->cnt++) { + if (p->cnt < p->start) + continue; + + if ((error = v7fs_inode_load(fs, &inode, dir->inode_number))) + break; + + v7fs_dirent_filename(filename, dir->name); + + DPRINTF("inode=%d name=%s %s\n", dir->inode_number, filename, + v7fs_inode_isdir(&inode) ? "DIR" : "FILE"); + memset(dp, 0, sizeof(*dp)); + dp->d_fileno = dir->inode_number; + dp->d_type = v7fs_mode_to_d_type(inode.mode); + dp->d_namlen = strlen(filename); + strcpy(dp->d_name, filename); + dp->d_reclen = sizeof(*dp); + if ((error = uiomove(dp, dp->d_reclen, p->uio))) { + DPRINTF("uiomove failed.\n"); + break; + } + } + scratch_free(fs, buf); + + if (p->cnt == p->end) + return V7FS_ITERATOR_BREAK; + + return error; +} + +int +v7fs_readdir(void *v) +{ + struct vop_readdir_args /* { + struct vnode *a_vp; + struct uio *a_uio; + kauth_cred_t a_cred; + int *a_eofflag; + off_t **a_cookies; + int *a_ncookies; + } */ *a = v; + struct uio *uio = a->a_uio; + struct vnode *vp = a->a_vp; + struct v7fs_node *v7node = vp->v_data; + struct v7fs_inode *inode = &v7node->inode; + struct v7fs_self *fs = v7node->v7fsmount->core; + struct dirent *dp; + int error; + + DPRINTF("offset=%zu residue=%zu\n", uio->uio_offset, uio->uio_resid); + + KDASSERT(vp->v_type == VDIR); + KDASSERT(uio->uio_offset >= 0); + KDASSERT(v7fs_inode_isdir(inode)); + + struct v7fs_readdir_arg arg; + arg.start = uio->uio_offset / sizeof(*dp); + arg.end = arg.start + uio->uio_resid / sizeof(*dp); + if (arg.start == arg.end) {/* user buffer has not enuf space. */ + DPRINTF("uio buffer too small\n"); + return ENOMEM; + } + dp = kmem_zalloc(sizeof(*dp), KM_SLEEP); + arg.cnt = 0; + arg.dp = dp; + arg.uio = uio; + + *a->a_eofflag = false; + error = v7fs_datablock_foreach(fs, inode, readdir_subr, &arg); + if (error == V7FS_ITERATOR_END) { + *a->a_eofflag = true; + } + if (error < 0) + error = 0; + + kmem_free(dp, sizeof(*dp)); + + return error; +} + +int +v7fs_inactive(void *v) +{ + struct vop_inactive_args /* { + struct vnode *a_vp; + bool *a_recycle; + } */ *a = v; + struct vnode *vp = a->a_vp; + struct v7fs_node *v7node = vp->v_data; + struct v7fs_inode *inode = &v7node->inode; + + DPRINTF("%p #%d\n", vp, inode->inode_number); + if (v7fs_inode_allocated(inode)) { + v7fs_update(vp, 0, 0, UPDATE_WAIT); + *a->a_recycle = false; + } else { + *a->a_recycle = true; + } + + VOP_UNLOCK(vp); + + return 0; +} + +int +v7fs_reclaim(void *v) +{ + /*This vnode is no longer referenced by kernel. */ + extern struct pool v7fs_node_pool; + struct vop_reclaim_args /* { + struct vnode *a_vp; + } */ *a = v; + struct vnode *vp = a->a_vp; + struct v7fs_node *v7node = vp->v_data; + + DPRINTF("%p #%d\n", vp, v7node->inode.inode_number); + mutex_enter(&mntvnode_lock); + LIST_REMOVE(v7node, link); + mutex_exit(&mntvnode_lock); + genfs_node_destroy(vp); + pool_put(&v7fs_node_pool, v7node); + vp->v_data = NULL; + + return 0; +} + +int +v7fs_bmap(void *v) +{ + struct vop_bmap_args /* { + struct vnode *a_vp; + daddr_t a_bn; + struct vnode **a_vpp; + daddr_t *a_bnp; + int *a_runp; + } */ *a = v; + struct vnode *vp = a->a_vp; + struct v7fs_node *v7node = vp->v_data; + struct v7fs_mount *v7fsmount = v7node->v7fsmount; + struct v7fs_self *fs = v7node->v7fsmount->core; + struct v7fs_inode *inode = &v7node->inode; + int error = 0; + + DPRINTF("inode=%d offset=%zu %p\n", inode->inode_number, a->a_bn, vp); + DPRINTF("filesize: %d\n", inode->filesize); + if (!a->a_bnp) + return 0; + + v7fs_daddr_t blk; + if (!(blk = v7fs_datablock_last(fs, inode, + (a->a_bn + 1) << V7FS_BSHIFT))) { + /* +1 converts block # to file offset. */ + return ENOSPC; + } + + *a->a_bnp = blk; + + if (a->a_vpp) + *a->a_vpp = v7fsmount->devvp; + if (a->a_runp) + *a->a_runp = 0; /*XXX TODO */ + + DPRINTF("%d %zu->%zu status=%d\n", inode->inode_number, a->a_bn, + *a->a_bnp, error); + + return error; +} + +int +v7fs_strategy(void *v) +{ + struct vop_strategy_args /* { + struct vnode *a_vp; + struct buf *a_bp; + } */ *a = v; + struct buf *b = a->a_bp; + struct vnode *vp = a->a_vp; + struct v7fs_node *v7node = vp->v_data; + struct v7fs_mount *v7fsmount = v7node->v7fsmount; + int error; + + DPRINTF("%p\n", vp); + KDASSERT(vp->v_type == VREG); + if (b->b_blkno == b->b_lblkno) { + error = VOP_BMAP(vp, b->b_lblkno, NULL, &b->b_blkno, NULL); + if (error) { + b->b_error = error; + biodone(b); + return error; + } + if ((long)b->b_blkno == -1) + clrbuf(b); + } + if ((long)b->b_blkno == -1) { + biodone(b); + return 0; + } + + return VOP_STRATEGY(v7fsmount->devvp, b); +} + +int +v7fs_print(void *v) +{ + struct vop_print_args /* { + struct vnode *a_vp; + } */ *a = v; + struct v7fs_node *v7node = a->a_vp->v_data; + + v7fs_inode_dump(&v7node->inode); + + return 0; +} + +int +v7fs_advlock(void *v) +{ + struct vop_advlock_args /* { + struct vnode *a_vp; + void *a_id; + int a_op; + struct flock *a_fl; + int a_flags; + } */ *a = v; + struct v7fs_node *v7node = a->a_vp->v_data; + + DPRINTF("op=%d\n", a->a_op); + + return lf_advlock(a, &v7node->lockf, + v7fs_inode_filesize(&v7node->inode)); +} + +int +v7fs_pathconf(void *v) +{ + struct vop_pathconf_args /* { + struct vnode *a_vp; + int a_name; + register_t *a_retval; + } */ *a = v; + int err = 0; + + DPRINTF("%p\n", a->a_vp); + + switch (a->a_name) { + case _PC_LINK_MAX: + *a->a_retval = V7FS_LINK_MAX; + break; + case _PC_NAME_MAX: + *a->a_retval = V7FS_NAME_MAX; + break; + case _PC_PATH_MAX: + *a->a_retval = V7FS_PATH_MAX; + break; + case _PC_CHOWN_RESTRICTED: + *a->a_retval = 1; + break; + case _PC_NO_TRUNC: + *a->a_retval = 0; + break; + case _PC_SYNC_IO: + *a->a_retval = 1; + break; + case _PC_FILESIZEBITS: + *a->a_retval = 30; /* ~1G */ + break; + case _PC_SYMLINK_MAX: + *a->a_retval = V7FSBSD_MAXSYMLINKLEN; + break; + case _PC_2_SYMLINKS: + *a->a_retval = 1; + break; + default: + err = EINVAL; + break; + } + + return err; +} + +int +v7fs_update(struct vnode *vp, const struct timespec *acc, + const struct timespec *mod, int flags) +{ + struct v7fs_node *v7node = vp->v_data; + struct v7fs_inode *inode = &v7node->inode; + struct v7fs_self *fs = v7node->v7fsmount->core; + bool update = false; + + DPRINTF("%p %zu %d\n", vp, vp->v_size, v7fs_inode_filesize(inode)); + KDASSERT(vp->v_size == v7fs_inode_filesize(inode)); + + if (v7node->update_atime) { + inode->atime = acc ? acc->tv_sec : time_second; + v7node->update_atime = false; + update = true; + } + if (v7node->update_ctime) { + inode->ctime = time_second; + v7node->update_ctime = false; + update = true; + } + if (v7node->update_mtime) { + inode->mtime = mod ? mod->tv_sec : time_second; + v7node->update_mtime = false; + update = true; + } + + if (update) + v7fs_inode_writeback(fs, inode); + + return 0; +} + +int +v7fs_symlink(void *v) +{ + struct vop_symlink_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + char *a_target; + } */ *a = v; + struct v7fs_node *parent_node = a->a_dvp->v_data; + struct v7fs_mount *v7fsmount = parent_node->v7fsmount; + struct v7fs_self *fs = v7fsmount->core; + struct vattr *va = a->a_vap; + kauth_cred_t cr = a->a_cnp->cn_cred; + struct componentname *cnp = a->a_cnp; + struct v7fs_fileattr attr; + v7fs_ino_t ino; + const char *from = a->a_target; + const char *to = cnp->cn_nameptr; + size_t len = strlen(from) + 1; + int error = 0; + + if (len > V7FS_BSIZE) { /* limited to 512byte pathname */ + DPRINTF("too long pathname."); + return ENAMETOOLONG; + } + + memset(&attr, 0, sizeof(attr)); + attr.uid = kauth_cred_geteuid(cr); + attr.gid = kauth_cred_getegid(cr); + attr.mode = va->va_mode | vtype_to_v7fs_mode(va->va_type); + + if ((error = v7fs_file_allocate + (fs, &parent_node->inode, to, &attr, &ino))) { + goto unlock_exit; + } + /* Sync dirent size change. */ + uvm_vnp_setsize(a->a_dvp, v7fs_inode_filesize(&parent_node->inode)); + + /* Get myself vnode. */ + if ((error = v7fs_vget(v7fsmount->mountp, ino, a->a_vpp))) { + DPRINTF("can't get vnode.\n"); + } + + struct v7fs_node *newnode = (*a->a_vpp)->v_data; + struct v7fs_inode *p = &newnode->inode; + v7fs_file_symlink(fs, p, from); + uvm_vnp_setsize(*a->a_vpp, v7fs_inode_filesize(p)); + + newnode->update_ctime = true; + newnode->update_mtime = true; + newnode->update_atime = true; +unlock_exit: + /* unlock parent directory */ + vput(a->a_dvp); + + return error; +} + +int +v7fs_readlink(void *v) +{ + struct vop_readlink_args /* { + struct vnode *a_vp; + struct uio *a_uio; + kauth_cred_t a_cred; + } */ *a = v; + struct uio *uio = a->a_uio; + struct vnode *vp = a->a_vp; + struct v7fs_node *v7node = vp->v_data; + struct v7fs_inode *inode = &v7node->inode; + struct v7fs_self *fs = v7node->v7fsmount->core; + int error = 0; + + KDASSERT(vp->v_type == VLNK); + KDASSERT(uio->uio_offset >= 0); + KDASSERT(v7fs_inode_islnk(inode)); + + v7fs_daddr_t blk = inode->addr[0]; + void *buf; + if (!(buf = scratch_read(fs, blk))) { + error = EIO; + goto error_exit; + } + + if ((error = uiomove(buf, strlen(buf), uio))) { + DPRINTF("uiomove failed.\n"); + } + scratch_free(fs, buf); + +error_exit: + return error; +} diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index bd000bb5c..a0a8d69a4 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -15,7 +15,7 @@ SUBDIR= \ \ \ link \ - \ + makefs \ mtree \ \ \ diff --git a/usr.sbin/makefs/Makefile b/usr.sbin/makefs/Makefile new file mode 100644 index 000000000..7522fc979 --- /dev/null +++ b/usr.sbin/makefs/Makefile @@ -0,0 +1,37 @@ +# $NetBSD: Makefile,v 1.36 2013/08/05 14:41:57 reinoud Exp $ +# + +WARNS?= 5 + +.include + +PROG= makefs +SRCS= cd9660.c chfs.c ffs.c v7fs.c msdos.c udf.c\ + getid.c \ + makefs.c misc.c \ + pack_dev.c \ + spec.c \ + walk.c +MAN= makefs.8 + +MKNODSRC= ${NETBSDSRCDIR}/sbin/mknod +MTREESRC= ${NETBSDSRCDIR}/usr.sbin/mtree + +CPPFLAGS+= -I${.CURDIR} -I${MKNODSRC} -I${MTREESRC} -DMAKEFS +#CPPFLAGS+= -DMSDOSFS_DEBUG +.PATH: ${MKNODSRC} ${MTREESRC} + +.include "${.CURDIR}/cd9660/Makefile.inc" +.include "${.CURDIR}/chfs/Makefile.inc" +.include "${.CURDIR}/ffs/Makefile.inc" +.include "${.CURDIR}/v7fs/Makefile.inc" +.include "${.CURDIR}/msdos/Makefile.inc" +.include "${.CURDIR}/udf/Makefile.inc" + +.if !defined(HOSTPROG) +DPADD+= ${LIBUTIL} +LDADD+= -lutil +.endif + +.include +# DO NOT DELETE diff --git a/usr.sbin/makefs/README b/usr.sbin/makefs/README new file mode 100644 index 000000000..7779a06e3 --- /dev/null +++ b/usr.sbin/makefs/README @@ -0,0 +1,129 @@ +$NetBSD: README,v 1.5 2011/07/18 08:58:38 uch Exp $ + +makefs - build a file system image from a directory tree + +NOTES: + + * This tool uses modified local copies of source found in other + parts of the tree. This is intentional. + + * makefs is a work in progress, and subject to change. + + +user overview: +-------------- + +makefs creates a file system image from a given directory tree. +the following file system types can be built: + ffs BSD fast file system + cd9660 ISO 9660 file system + v7fs 7th edition(V7) file system + +Support for the following file systems maybe be added in the future + ext2fs Linux EXT2 file system + fat MS-DOS `FAT' file system (FAT12, FAT16, FAT32) + +Various file system independent parameters and contraints can be +specified, such as: + - minimum file system size (in KB) + - maximum file system size (in KB) + - free inodes + - free blocks (in KB) + - mtree(8) specification file containing permissions and ownership + to use in image, overridding the settings in the directory tree + - file containing list of files to specifically exclude or include + - fnmatch(3) pattern of filenames to exclude or include + - endianness of target file system + +File system specific parameters can be given as well, with a command +line option such as "-o fsspeccific-options,comma-separated". +For example, ffs would allow tuning of: + - block & fragment size + - cylinder groups + - number of blocks per inode + - minimum free space + +Other file systems might have controls on how to "munge" file names to +fit within the constraints of the target file system. + +Exit codes: + 0 all ok + 1 fatal error + 2 some files couldn't be added during image creation + (bad perms, missing file, etc). image will continue + to be made + + +Implementation overview: +------------------------ + +The implementation must allow for easy addition of extra file systems +with minimal changes to the file system independent sections. + +The main program will: + - parse the options, including calling fs-specific routines to + validate fs-specific options + - walk the tree, building up a data structure which represents + the tree to stuff into the image. The structure will + probably be a similar tree to what mtree(8) uses internally; + a linked list of entries per directory with a child pointer + to children of directories. ".." won't be stored in the list; + the fs-specific tree walker should add this if required by the fs. + this builder have the smarts to handle hard links correctly. + - (optionally) Change the permissions in the tree according to + the mtree(8) specfile + - Call an fs-specific routine to build the image based on the + data structures. + +Each fs-specific module should have the following external interfaces: + + prepare_options optional file system specific defaults that need to be + setup before parsing fs-specific options. + + parse_options parse the string for fs-specific options, feeding + errors back to the user as appropriate + + cleanup_options optional file system specific data that need to be + cleaned up when done with this filesystem. + + make_fs take the data structures representing the + directory tree and fs parameters, + validate that the parameters are valid + (e.g, the requested image will be large enough), + create the image, and + populate the image + +prepare_options and cleanup_options are optional and can be NULL. + +NOTE: All file system specific options are referenced via the fs_specific +pointer from the fsinfo_t strucutre. It is up to the filesystem to allocate +and free any data needed for this via the prepare and cleanup callbacks. + +Each fs-specific module will need to add it's routines to the dispatch array +in makefs.c and add prototypes for these to makefs.h + +All other implementation details should not need to change any of the +generic code. + +ffs implementation +------------------ + +In the ffs case, we can leverage off sbin/newfs/mkfs.c to actually build +the image. When building and populating the image, the implementation +can be greatly simplified if some assumptions are made: + - the total required size (in blocks and inodes) is determined + as part of the validation phase + - a "file" (including a directory) has a known size, so + support for growing a file is not necessary + +Two underlying primitives are provided: + make_inode create an inode, returning the inode number + + write_file write file (from memory if DIR, file descriptor + if FILE or SYMLINK), referencing given inode. + it is smart enough to know if a short symlink + can be stuffed into the inode, etc. + +When creating a directory, the directory entries in the previously +built tree data structure is scanned and built in memory so it can +be written entirely as a single write_file() operation. diff --git a/usr.sbin/makefs/TODO b/usr.sbin/makefs/TODO new file mode 100644 index 000000000..dfd306a03 --- /dev/null +++ b/usr.sbin/makefs/TODO @@ -0,0 +1,41 @@ +$NetBSD: TODO,v 1.7 2007/12/10 23:54:35 dyoung Exp $ + +todo +---- + +- read files from multiple directories with or without root + specification, e.g., makefs -t cd9660 output.iso dir1 root2=dir2 + dir3 root4=dir4 + +- display block numbers for a given file (e.g, /boot) + +- finish makefs.8 + +- testing + +- even more testing + +- add support for converting a tar file (instead of a directory tree); + suggested by kpneal@pobox.com + + +outstanding bugs +---------------- + +- size estimation is still out (need to take into account indirect blocks!) + +- parameter checking when density is rather high or low. + +- filling up a file system (running out of inodes or whatever) + doesn't do the right thing. + + +discuss +------- + +- consider replacing ffs_balloc() et al with own code that doesn't + need hacked-up buf.c code + +- whacking on newfs/mkfs.c to allow .PATH-ing directly into makefs(8). + this would involve passing all of mkfs()'s parameters in a single + struct rather than a lot of global vars, etc. diff --git a/usr.sbin/makefs/cd9660.c b/usr.sbin/makefs/cd9660.c new file mode 100644 index 000000000..c449e0ca4 --- /dev/null +++ b/usr.sbin/makefs/cd9660.c @@ -0,0 +1,2148 @@ +/* $NetBSD: cd9660.c,v 1.47 2013/10/24 14:01:01 apb Exp $ */ + +/* + * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan + * Perez-Rathke and Ram Vedam. All rights reserved. + * + * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys, + * Alan Perez-Rathke and Ram Vedam. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ +/* + * Copyright (c) 2001 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Luke Mewburn for Wasabi Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Copyright (c) 1982, 1986, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#else +#include +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: cd9660.c,v 1.47 2013/10/24 14:01:01 apb Exp $"); +#endif /* !__lint */ + +#include +#include +#include +#include +#include + +#include "makefs.h" +#include "cd9660.h" +#include "cd9660/iso9660_rrip.h" +#include "cd9660/cd9660_archimedes.h" + +/* + * Global variables + */ + +static void cd9660_finalize_PVD(iso9660_disk *); +static cd9660node *cd9660_allocate_cd9660node(void); +static void cd9660_set_defaults(iso9660_disk *); +static int cd9660_arguments_set_string(const char *, const char *, int, + char, char *); +static void cd9660_populate_iso_dir_record( + struct _iso_directory_record_cd9660 *, u_char, u_char, u_char, + const char *); +static void cd9660_setup_root_node(iso9660_disk *); +static int cd9660_setup_volume_descriptors(iso9660_disk *); +#if 0 +static int cd9660_fill_extended_attribute_record(cd9660node *); +#endif +static void cd9660_sort_nodes(cd9660node *); +static int cd9660_translate_node_common(iso9660_disk *, cd9660node *); +static int cd9660_translate_node(iso9660_disk *, fsnode *, cd9660node *); +static int cd9660_compare_filename(const char *, const char *); +static void cd9660_sorted_child_insert(cd9660node *, cd9660node *); +static int cd9660_handle_collisions(iso9660_disk *, cd9660node *, int); +static cd9660node *cd9660_rename_filename(iso9660_disk *, cd9660node *, int, + int); +static void cd9660_copy_filenames(iso9660_disk *, cd9660node *); +static void cd9660_sorting_nodes(cd9660node *); +static int cd9660_count_collisions(cd9660node *); +static cd9660node *cd9660_rrip_move_directory(iso9660_disk *, cd9660node *); +static int cd9660_add_dot_records(iso9660_disk *, cd9660node *); + +static void cd9660_convert_structure(iso9660_disk *, fsnode *, cd9660node *, int, + int *, int *); +static void cd9660_free_structure(cd9660node *); +static int cd9660_generate_path_table(iso9660_disk *); +static int cd9660_level1_convert_filename(iso9660_disk *, const char *, char *, + int); +static int cd9660_level2_convert_filename(iso9660_disk *, const char *, char *, + int); +#if 0 +static int cd9660_joliet_convert_filename(iso9660_disk *, const char *, char *, + int); +#endif +static int cd9660_convert_filename(iso9660_disk *, const char *, char *, int); +static void cd9660_populate_dot_records(iso9660_disk *, cd9660node *); +static int64_t cd9660_compute_offsets(iso9660_disk *, cd9660node *, int64_t); +#if 0 +static int cd9660_copy_stat_info(cd9660node *, cd9660node *, int); +#endif +static cd9660node *cd9660_create_virtual_entry(iso9660_disk *, const char *, + cd9660node *, int, int); +static cd9660node *cd9660_create_file(iso9660_disk *, const char *, + cd9660node *, cd9660node *); +static cd9660node *cd9660_create_directory(iso9660_disk *, const char *, + cd9660node *, cd9660node *); +static cd9660node *cd9660_create_special_directory(iso9660_disk *, u_char, + cd9660node *); +static int cd9660_add_generic_bootimage(iso9660_disk *, const char *); + + +/* + * Allocate and initalize a cd9660node + * @returns struct cd9660node * Pointer to new node, or NULL on error + */ +static cd9660node * +cd9660_allocate_cd9660node(void) +{ + cd9660node *temp = ecalloc(1, sizeof(*temp)); + TAILQ_INIT(&temp->cn_children); + temp->parent = temp->dot_record = temp->dot_dot_record = NULL; + temp->ptnext = temp->ptprev = temp->ptlast = NULL; + temp->node = NULL; + temp->isoDirRecord = NULL; + temp->isoExtAttributes = NULL; + temp->rr_real_parent = temp->rr_relocated = NULL; + temp->su_tail_data = NULL; + return temp; +} + +int cd9660_defaults_set = 0; + +/** +* Set default values for cd9660 extension to makefs +*/ +static void +cd9660_set_defaults(iso9660_disk *diskStructure) +{ + /*Fix the sector size for now, though the spec allows for other sizes*/ + diskStructure->sectorSize = 2048; + + /* Set up defaults in our own structure */ + diskStructure->verbose_level = 0; + diskStructure->keep_bad_images = 0; + diskStructure->follow_sym_links = 0; + diskStructure->isoLevel = 2; + + diskStructure->rock_ridge_enabled = 0; + diskStructure->rock_ridge_renamed_dir_name = 0; + diskStructure->rock_ridge_move_count = 0; + diskStructure->rr_moved_dir = 0; + + diskStructure->archimedes_enabled = 0; + diskStructure->chrp_boot = 0; + + diskStructure->include_padding_areas = 1; + + /* Spec breaking functionality */ + diskStructure->allow_deep_trees = + diskStructure->allow_start_dot = + diskStructure->allow_max_name = + diskStructure->allow_illegal_chars = + diskStructure->allow_lowercase = + diskStructure->allow_multidot = + diskStructure->omit_trailing_period = 0; + + /* Make sure the PVD is clear */ + memset(&diskStructure->primaryDescriptor, 0, 2048); + + memset(diskStructure->primaryDescriptor.publisher_id, 0x20,128); + memset(diskStructure->primaryDescriptor.preparer_id, 0x20,128); + memset(diskStructure->primaryDescriptor.application_id, 0x20,128); + memset(diskStructure->primaryDescriptor.copyright_file_id, 0x20,37); + memset(diskStructure->primaryDescriptor.abstract_file_id, 0x20,37); + memset(diskStructure->primaryDescriptor.bibliographic_file_id, 0x20,37); + + strcpy(diskStructure->primaryDescriptor.system_id,"NetBSD"); + + cd9660_defaults_set = 1; + + /* Boot support: Initially disabled */ + diskStructure->has_generic_bootimage = 0; + diskStructure->generic_bootimage = NULL; + + diskStructure->boot_image_directory = 0; + /*memset(diskStructure->boot_descriptor, 0, 2048);*/ + + diskStructure->is_bootable = 0; + TAILQ_INIT(&diskStructure->boot_images); + LIST_INIT(&diskStructure->boot_entries); +} + +void +cd9660_prep_opts(fsinfo_t *fsopts) +{ + iso9660_disk *diskStructure = ecalloc(1, sizeof(*diskStructure)); + +#define OPT_STR(letter, name, desc) \ + { letter, name, NULL, OPT_STRBUF, 0, 0, desc } + +#define OPT_NUM(letter, name, field, min, max, desc) \ + { letter, name, &diskStructure->field, \ + sizeof(diskStructure->field) == 8 ? OPT_INT64 : \ + (sizeof(diskStructure->field) == 4 ? OPT_INT32 : \ + (sizeof(diskStructure->field) == 2 ? OPT_INT16 : OPT_INT8)), \ + min, max, desc } + +#define OPT_BOOL(letter, name, field, desc) \ + OPT_NUM(letter, name, field, 0, 1, desc) + + const option_t cd9660_options[] = { + OPT_NUM('l', "isolevel", isoLevel, + 1, 3, "ISO Level"), + OPT_NUM('v', "verbose", verbose_level, + 0, 2, "Turns on verbose output"), + + OPT_BOOL('h', "help", displayHelp, + "Show help message"), + OPT_BOOL('S', "follow-symlinks", follow_sym_links, + "Resolve symlinks in pathnames"), + OPT_BOOL('R', "rockridge", rock_ridge_enabled, + "Enable Rock-Ridge extensions"), + OPT_BOOL('C', "chrp-boot", chrp_boot, + "Enable CHRP boot"), + OPT_BOOL('K', "keep-bad-images", keep_bad_images, + "Keep bad images"), + OPT_BOOL('D', "allow-deep-trees", allow_deep_trees, + "Allow trees more than 8 levels"), + OPT_BOOL('a', "allow-max-name", allow_max_name, + "Allow 37 char filenames (unimplemented)"), + OPT_BOOL('i', "allow-illegal-chars", allow_illegal_chars, + "Allow illegal characters in filenames"), + OPT_BOOL('D', "allow-multidot", allow_multidot, + "Allow multiple periods in filenames"), + OPT_BOOL('o', "omit-trailing-period", omit_trailing_period, + "Omit trailing periods in filenames"), + OPT_BOOL('\0', "allow-lowercase", allow_lowercase, + "Allow lowercase characters in filenames"), + OPT_BOOL('\0', "archimedes", archimedes_enabled, + "Enable Archimedes structure"), + OPT_BOOL('\0', "no-trailing-padding", include_padding_areas, + "Include padding areas"), + + OPT_STR('A', "applicationid", "Application Identifier"), + OPT_STR('P', "publisher", "Publisher Identifier"), + OPT_STR('p', "preparer", "Preparer Identifier"), + OPT_STR('L', "label", "Disk Label"), + OPT_STR('V', "volumeid", "Volume Set Identifier"), + OPT_STR('B', "bootimage", "Boot image parameter"), + OPT_STR('G', "generic-bootimage", "Generic boot image param"), + OPT_STR('\0', "bootimagedir", "Boot image directory"), + OPT_STR('\0', "no-emul-boot", "No boot emulation"), + OPT_STR('\0', "no-boot", "No boot support"), + OPT_STR('\0', "hard-disk-boot", "Boot from hard disk"), + OPT_STR('\0', "boot-load-segment", "Boot load segment"), + + { .name = NULL } + }; + + fsopts->fs_specific = diskStructure; + fsopts->fs_options = copy_opts(cd9660_options); + + cd9660_set_defaults(diskStructure); +} + +void +cd9660_cleanup_opts(fsinfo_t *fsopts) +{ + free(fsopts->fs_specific); + free(fsopts->fs_options); +} + +static int +cd9660_arguments_set_string(const char *val, const char *fieldtitle, int length, + char testmode, char * dest) +{ + int len, test; + + if (val == NULL) + warnx("error: The %s requires a string argument", fieldtitle); + else if ((len = strlen(val)) <= length) { + if (testmode == 'd') + test = cd9660_valid_d_chars(val); + else + test = cd9660_valid_a_chars(val); + if (test) { + memcpy(dest, val, len); + if (test == 2) + cd9660_uppercase_characters(dest, len); + return 1; + } else + warnx("error: The %s must be composed of " + "%c-characters", fieldtitle, testmode); + } else + warnx("error: The %s must be at most 32 characters long", + fieldtitle); + return 0; +} + +/* + * Command-line parsing function + */ + +int +cd9660_parse_opts(const char *option, fsinfo_t *fsopts) +{ + int rv, i; + iso9660_disk *diskStructure = fsopts->fs_specific; + option_t *cd9660_options = fsopts->fs_options; + char buf[1024]; + const char *name, *desc; + + assert(option != NULL); + + if (debug & DEBUG_FS_PARSE_OPTS) + printf("cd9660_parse_opts: got `%s'\n", option); + + i = set_option(cd9660_options, option, buf, sizeof(buf)); + if (i == -1) + return 0; + + if (cd9660_options[i].name == NULL) + abort(); + + + name = cd9660_options[i].name; + desc = cd9660_options[i].desc; + switch (cd9660_options[i].letter) { + case 'h': + case 'S': + rv = 0; /* this is not handled yet */ + break; + case 'L': + rv = cd9660_arguments_set_string(buf, desc, 32, 'd', + diskStructure->primaryDescriptor.volume_id); + break; + case 'A': + rv = cd9660_arguments_set_string(buf, desc, 128, 'a', + diskStructure->primaryDescriptor.application_id); + break; + case 'P': + rv = cd9660_arguments_set_string(buf, desc, 128, 'a', + diskStructure->primaryDescriptor.publisher_id); + break; + case 'p': + rv = cd9660_arguments_set_string(buf, desc, 128, 'a', + diskStructure->primaryDescriptor.preparer_id); + break; + case 'V': + rv = cd9660_arguments_set_string(buf, desc, 128, 'a', + diskStructure->primaryDescriptor.volume_set_id); + break; + /* Boot options */ + case 'B': + if (buf[0] == '\0') { + warnx("The Boot Image parameter requires a valid boot" + " information string"); + rv = 0; + } else + rv = cd9660_add_boot_disk(diskStructure, buf); + break; + case 'G': + if (buf[0] == '\0') { + warnx("The Generic Boot Image parameter requires a" + " valid boot information string"); + rv = 0; + } else + rv = cd9660_add_generic_bootimage(diskStructure, buf); + break; + default: + if (strcmp(name, "bootimagedir") == 0) { + /* + * XXXfvdl this is unused. + */ + if (buf[0] == '\0') { + warnx("The Boot Image Directory parameter" + " requires a directory name\n"); + rv = 0; + } else { + diskStructure->boot_image_directory = + emalloc(strlen(buf) + 1); + /* BIG TODO: Add the max length function here */ + rv = cd9660_arguments_set_string(buf, desc, 12, + 'd', diskStructure->boot_image_directory); + } + } else if (strcmp(name, "no-emul-boot") == 0 || + strcmp(name, "no-boot") == 0 || + strcmp(name, "hard-disk-boot") == 0) { + /* RRIP */ + cd9660_eltorito_add_boot_option(diskStructure, name, 0); + rv = 1; + } else if (strcmp(name, "boot-load-segment") == 0) { + if (buf[0] == '\0') { + warnx("Option `%s' doesn't contain a value", + name); + rv = 0; + } else { + cd9660_eltorito_add_boot_option(diskStructure, + name, buf); + rv = 1; + } + } else + rv = 1; + } + return rv; +} + +/* + * Main function for cd9660_makefs + * Builds the ISO image file + * @param const char *image The image filename to create + * @param const char *dir The directory that is being read + * @param struct fsnode *root The root node of the filesystem tree + * @param struct fsinfo_t *fsopts Any options + */ +void +cd9660_makefs(const char *image, const char *dir, fsnode *root, + fsinfo_t *fsopts) +{ + int64_t startoffset; + int numDirectories; + uint64_t pathTableSectors; + int64_t firstAvailableSector; + int64_t totalSpace; + int error; + cd9660node *real_root; + iso9660_disk *diskStructure = fsopts->fs_specific; + + if (diskStructure->verbose_level > 0) + printf("cd9660_makefs: ISO level is %i\n", + diskStructure->isoLevel); + if (diskStructure->isoLevel < 2 && + diskStructure->allow_multidot) + errx(1, "allow-multidot requires iso level of 2\n"); + + assert(image != NULL); + assert(dir != NULL); + assert(root != NULL); + + if (diskStructure->displayHelp) { + /* + * Display help here - probably want to put it in + * a separate function + */ + return; + } + + if (diskStructure->verbose_level > 0) + printf("cd9660_makefs: image %s directory %s root %p\n", + image, dir, root); + + /* Set up some constants. Later, these will be defined with options */ + + /* Counter needed for path tables */ + numDirectories = 0; + + /* Convert tree to our own format */ + /* Actually, we now need to add the REAL root node, at level 0 */ + + real_root = cd9660_allocate_cd9660node(); + real_root->isoDirRecord = emalloc(sizeof(*real_root->isoDirRecord)); + /* Leave filename blank for root */ + memset(real_root->isoDirRecord->name, 0, + ISO_FILENAME_MAXLENGTH_WITH_PADDING); + + real_root->level = 0; + diskStructure->rootNode = real_root; + real_root->type = CD9660_TYPE_DIR; + error = 0; + real_root->node = root; + cd9660_convert_structure(diskStructure, root, real_root, 1, + &numDirectories, &error); + + if (TAILQ_EMPTY(&real_root->cn_children)) { + errx(1, "cd9660_makefs: converted directory is empty. " + "Tree conversion failed\n"); + } else if (error != 0) { + errx(1, "cd9660_makefs: tree conversion failed\n"); + } else { + if (diskStructure->verbose_level > 0) + printf("cd9660_makefs: tree converted\n"); + } + + /* Add the dot and dot dot records */ + cd9660_add_dot_records(diskStructure, real_root); + + cd9660_setup_root_node(diskStructure); + + if (diskStructure->verbose_level > 0) + printf("cd9660_makefs: done converting tree\n"); + + /* non-SUSP extensions */ + if (diskStructure->archimedes_enabled) + archimedes_convert_tree(diskStructure->rootNode); + + /* Rock ridge / SUSP init pass */ + if (diskStructure->rock_ridge_enabled) { + cd9660_susp_initialize(diskStructure, diskStructure->rootNode, + diskStructure->rootNode, NULL); + } + + /* Build path table structure */ + diskStructure->pathTableLength = cd9660_generate_path_table( + diskStructure); + + pathTableSectors = CD9660_BLOCKS(diskStructure->sectorSize, + diskStructure->pathTableLength); + + firstAvailableSector = cd9660_setup_volume_descriptors(diskStructure); + if (diskStructure->is_bootable) { + firstAvailableSector = cd9660_setup_boot(diskStructure, + firstAvailableSector); + if (firstAvailableSector < 0) + errx(1, "setup_boot failed"); + } + /* LE first, then BE */ + diskStructure->primaryLittleEndianTableSector = firstAvailableSector; + diskStructure->primaryBigEndianTableSector = + diskStructure->primaryLittleEndianTableSector + pathTableSectors; + + /* Set the secondary ones to -1, not going to use them for now */ + diskStructure->secondaryBigEndianTableSector = -1; + diskStructure->secondaryLittleEndianTableSector = -1; + + diskStructure->dataFirstSector = + diskStructure->primaryBigEndianTableSector + pathTableSectors; + if (diskStructure->verbose_level > 0) + printf("cd9660_makefs: Path table conversion complete. " + "Each table is %i bytes, or %" PRIu64 " sectors.\n", + diskStructure->pathTableLength, pathTableSectors); + + startoffset = diskStructure->sectorSize*diskStructure->dataFirstSector; + + totalSpace = cd9660_compute_offsets(diskStructure, real_root, startoffset); + + diskStructure->totalSectors = diskStructure->dataFirstSector + + CD9660_BLOCKS(diskStructure->sectorSize, totalSpace); + + /* Disabled until pass 1 is done */ + if (diskStructure->rock_ridge_enabled) { + diskStructure->susp_continuation_area_start_sector = + diskStructure->totalSectors; + diskStructure->totalSectors += + CD9660_BLOCKS(diskStructure->sectorSize, + diskStructure->susp_continuation_area_size); + cd9660_susp_finalize(diskStructure, diskStructure->rootNode); + } + + + cd9660_finalize_PVD(diskStructure); + + /* Add padding sectors, just for testing purposes right now */ + /* diskStructure->totalSectors+=150; */ + + /* Debugging output */ + if (diskStructure->verbose_level > 0) { + printf("cd9660_makefs: Sectors 0-15 reserved\n"); + printf("cd9660_makefs: Primary path tables starts in sector %" + PRId64 "\n", diskStructure->primaryLittleEndianTableSector); + printf("cd9660_makefs: File data starts in sector %" + PRId64 "\n", diskStructure->dataFirstSector); + printf("cd9660_makefs: Total sectors: %" + PRId64 "\n", diskStructure->totalSectors); + } + + /* + * Add padding sectors at the end + * TODO: Clean this up and separate padding + */ + if (diskStructure->include_padding_areas) + diskStructure->totalSectors += 150; + + cd9660_write_image(diskStructure, image); + + if (diskStructure->verbose_level > 1) { + debug_print_volume_descriptor_information(diskStructure); + debug_print_tree(diskStructure, real_root, 0); + debug_print_path_tree(real_root); + } + + /* Clean up data structures */ + cd9660_free_structure(real_root); + + if (diskStructure->verbose_level > 0) + printf("cd9660_makefs: done\n"); +} + +/* Generic function pointer - implement later */ +typedef int (*cd9660node_func)(cd9660node *); + +static void +cd9660_finalize_PVD(iso9660_disk *diskStructure) +{ + time_t tim; + + /* root should be a fixed size of 34 bytes since it has no name */ + memcpy(diskStructure->primaryDescriptor.root_directory_record, + diskStructure->rootNode->dot_record->isoDirRecord, 34); + + /* In RRIP, this might be longer than 34 */ + diskStructure->primaryDescriptor.root_directory_record[0] = 34; + + /* Set up all the important numbers in the PVD */ + cd9660_bothendian_dword(diskStructure->totalSectors, + (unsigned char *)diskStructure->primaryDescriptor.volume_space_size); + cd9660_bothendian_word(1, + (unsigned char *)diskStructure->primaryDescriptor.volume_set_size); + cd9660_bothendian_word(1, + (unsigned char *) + diskStructure->primaryDescriptor.volume_sequence_number); + cd9660_bothendian_word(diskStructure->sectorSize, + (unsigned char *) + diskStructure->primaryDescriptor.logical_block_size); + cd9660_bothendian_dword(diskStructure->pathTableLength, + (unsigned char *)diskStructure->primaryDescriptor.path_table_size); + + cd9660_731(diskStructure->primaryLittleEndianTableSector, + (u_char *)diskStructure->primaryDescriptor.type_l_path_table); + cd9660_732(diskStructure->primaryBigEndianTableSector, + (u_char *)diskStructure->primaryDescriptor.type_m_path_table); + + diskStructure->primaryDescriptor.file_structure_version[0] = 1; + + /* Pad all strings with spaces instead of nulls */ + cd9660_pad_string_spaces(diskStructure->primaryDescriptor.volume_id, 32); + cd9660_pad_string_spaces(diskStructure->primaryDescriptor.system_id, 32); + cd9660_pad_string_spaces(diskStructure->primaryDescriptor.volume_set_id, + 128); + cd9660_pad_string_spaces(diskStructure->primaryDescriptor.publisher_id, + 128); + cd9660_pad_string_spaces(diskStructure->primaryDescriptor.preparer_id, + 128); + cd9660_pad_string_spaces(diskStructure->primaryDescriptor.application_id, + 128); + cd9660_pad_string_spaces( + diskStructure->primaryDescriptor.copyright_file_id, 37); + cd9660_pad_string_spaces( + diskStructure->primaryDescriptor.abstract_file_id, 37); + cd9660_pad_string_spaces( + diskStructure->primaryDescriptor.bibliographic_file_id, 37); + + /* Setup dates */ + time(&tim); + cd9660_time_8426( + (unsigned char *)diskStructure->primaryDescriptor.creation_date, + tim); + cd9660_time_8426( + (unsigned char *)diskStructure->primaryDescriptor.modification_date, + tim); + + /* + cd9660_set_date(diskStructure->primaryDescriptor.expiration_date, now); + */ + memset(diskStructure->primaryDescriptor.expiration_date, '0' ,16); + diskStructure->primaryDescriptor.expiration_date[16] = 0; + + cd9660_time_8426( + (unsigned char *)diskStructure->primaryDescriptor.effective_date, + tim); +} + +static void +cd9660_populate_iso_dir_record(struct _iso_directory_record_cd9660 *record, + u_char ext_attr_length, u_char flags, + u_char name_len, const char * name) +{ + record->ext_attr_length[0] = ext_attr_length; + record->flags[0] = ISO_FLAG_CLEAR | flags; + record->file_unit_size[0] = 0; + record->interleave[0] = 0; + cd9660_bothendian_word(1, record->volume_sequence_number); + record->name_len[0] = name_len; + memset(record->name, '\0', sizeof (record->name)); + memcpy(record->name, name, name_len); + record->length[0] = 33 + name_len; + + /* Todo : better rounding */ + record->length[0] += (record->length[0] & 1) ? 1 : 0; +} + +static void +cd9660_setup_root_node(iso9660_disk *diskStructure) +{ + cd9660_populate_iso_dir_record(diskStructure->rootNode->isoDirRecord, + 0, ISO_FLAG_DIRECTORY, 1, "\0"); + +} + +/*********** SUPPORT FUNCTIONS ***********/ +static int +cd9660_setup_volume_descriptors(iso9660_disk *diskStructure) +{ + /* Boot volume descriptor should come second */ + int sector = 16; + /* For now, a fixed 2 : PVD and terminator */ + volume_descriptor *temp, *t; + + /* Set up the PVD */ + temp = emalloc(sizeof(*temp)); + temp->volumeDescriptorData = + (unsigned char *)&diskStructure->primaryDescriptor; + temp->volumeDescriptorData[0] = ISO_VOLUME_DESCRIPTOR_PVD; + temp->volumeDescriptorData[6] = 1; + temp->sector = sector; + memcpy(temp->volumeDescriptorData + 1, + ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5); + diskStructure->firstVolumeDescriptor = temp; + + sector++; + /* Set up boot support if enabled. BVD must reside in sector 17 */ + if (diskStructure->is_bootable) { + t = emalloc(sizeof(*t)); + t->volumeDescriptorData = ecalloc(1, 2048); + temp->next = t; + temp = t; + t->sector = 17; + if (diskStructure->verbose_level > 0) + printf("Setting up boot volume descriptor\n"); + cd9660_setup_boot_volume_descriptor(diskStructure, t); + sector++; + } + + /* Set up the terminator */ + t = emalloc(sizeof(*t)); + t->volumeDescriptorData = ecalloc(1, 2048); + temp->next = t; + t->volumeDescriptorData[0] = ISO_VOLUME_DESCRIPTOR_TERMINATOR; + t->next = 0; + t->volumeDescriptorData[6] = 1; + t->sector = sector; + memcpy(t->volumeDescriptorData + 1, + ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5); + + sector++; + return sector; +} + +#if 0 +/* + * Populate EAR at some point. Not required, but is used by NetBSD's + * cd9660 support + */ +static int +cd9660_fill_extended_attribute_record(cd9660node *node) +{ + node->isoExtAttributes = emalloc(sizeof(*node->isoExtAttributes)); + return 1; +} +#endif + +static int +cd9660_translate_node_common(iso9660_disk *diskStructure, cd9660node *newnode) +{ + time_t tim; + u_char flag; + char temp[ISO_FILENAME_MAXLENGTH_WITH_PADDING]; + + /* Now populate the isoDirRecord structure */ + memset(temp, 0, ISO_FILENAME_MAXLENGTH_WITH_PADDING); + + (void)cd9660_convert_filename(diskStructure, newnode->node->name, + temp, !(S_ISDIR(newnode->node->type))); + + flag = ISO_FLAG_CLEAR; + if (S_ISDIR(newnode->node->type)) + flag |= ISO_FLAG_DIRECTORY; + + cd9660_populate_iso_dir_record(newnode->isoDirRecord, 0, + flag, strlen(temp), temp); + + /* Set the various dates */ + + /* If we want to use the current date and time */ + time(&tim); + + cd9660_time_915(newnode->isoDirRecord->date, tim); + + cd9660_bothendian_dword(newnode->fileDataLength, + newnode->isoDirRecord->size); + /* If the file is a link, we want to set the size to 0 */ + if (S_ISLNK(newnode->node->type)) + newnode->fileDataLength = 0; + + return 1; +} + +/* + * Translate fsnode to cd9660node + * Translate filenames and other metadata, including dates, sizes, + * permissions, etc + * @param struct fsnode * The node generated by makefs + * @param struct cd9660node * The intermediate node to be written to + * @returns int 0 on failure, 1 on success + */ +static int +cd9660_translate_node(iso9660_disk *diskStructure, fsnode *node, + cd9660node *newnode) +{ + if (node == NULL) { + if (diskStructure->verbose_level > 0) + printf("cd9660_translate_node: NULL node passed, " + "returning\n"); + return 0; + } + newnode->isoDirRecord = emalloc(sizeof(*newnode->isoDirRecord)); + /* Set the node pointer */ + newnode->node = node; + + /* Set the size */ + if (!(S_ISDIR(node->type))) + newnode->fileDataLength = node->inode->st.st_size; + + if (cd9660_translate_node_common(diskStructure, newnode) == 0) + return 0; + + /* Finally, overwrite some of the values that are set by default */ + cd9660_time_915(newnode->isoDirRecord->date, node->inode->st.st_mtime); + + return 1; +} + +/* + * Compares two ISO filenames + * @param const char * The first file name + * @param const char * The second file name + * @returns : -1 if first is less than second, 0 if they are the same, 1 if + * the second is greater than the first + */ +static int +cd9660_compare_filename(const char *first, const char *second) +{ + /* + * This can be made more optimal once it has been tested + * (the extra character, for example, is for testing) + */ + + int p1 = 0; + int p2 = 0; + char c1, c2; + /* First, on the filename */ + + while (p1 < ISO_FILENAME_MAXLENGTH_BEFORE_VERSION-1 + && p2 < ISO_FILENAME_MAXLENGTH_BEFORE_VERSION-1) { + c1 = first[p1]; + c2 = second[p2]; + if (c1 == '.' && c2 =='.') + break; + else if (c1 == '.') { + p2++; + c1 = ' '; + } else if (c2 == '.') { + p1++; + c2 = ' '; + } else { + p1++; + p2++; + } + + if (c1 < c2) + return -1; + else if (c1 > c2) { + return 1; + } + } + + if (first[p1] == '.' && second[p2] == '.') { + p1++; + p2++; + while (p1 < ISO_FILENAME_MAXLENGTH_BEFORE_VERSION - 1 + && p2 < ISO_FILENAME_MAXLENGTH_BEFORE_VERSION - 1) { + c1 = first[p1]; + c2 = second[p2]; + if (c1 == ';' && c2 == ';') + break; + else if (c1 == ';') { + p2++; + c1 = ' '; + } else if (c2 == ';') { + p1++; + c2 = ' '; + } else { + p1++; + p2++; + } + + if (c1 < c2) + return -1; + else if (c1 > c2) + return 1; + } + } + return 0; +} + +/* + * Insert a node into list with ISO sorting rules + * @param cd9660node * The head node of the list + * @param cd9660node * The node to be inserted + */ +static void +cd9660_sorted_child_insert(cd9660node *parent, cd9660node *cn_new) +{ + int compare; + cd9660node *cn; + struct cd9660_children_head *head = &parent->cn_children; + + /* TODO: Optimize? */ + cn_new->parent = parent; + + /* + * first will either be 0, the . or the .. + * if . or .., this means no other entry may be written before first + * if 0, the new node may be inserted at the head + */ + + TAILQ_FOREACH(cn, head, cn_next_child) { + /* + * Dont insert a node twice - + * that would cause an infinite loop + */ + if (cn_new == cn) + return; + + compare = cd9660_compare_filename(cn_new->isoDirRecord->name, + cn->isoDirRecord->name); + + if (compare == 0) + compare = cd9660_compare_filename(cn_new->node->name, + cn->node->name); + + if (compare < 0) + break; + } + if (cn == NULL) + TAILQ_INSERT_TAIL(head, cn_new, cn_next_child); + else + TAILQ_INSERT_BEFORE(cn, cn_new, cn_next_child); +} + +/* + * Called After cd9660_sorted_child_insert + * handles file collisions by suffixing each filname with ~n + * where n represents the files respective place in the ordering + */ +static int +cd9660_handle_collisions(iso9660_disk *diskStructure, cd9660node *colliding, + int past) +{ + cd9660node *iter, *next, *prev; + int skip; + int delete_chars = 0; + int temp_past = past; + int temp_skip; + int flag = 0; + cd9660node *end_of_range; + + for (iter = TAILQ_FIRST(&colliding->cn_children); + iter != NULL && (next = TAILQ_NEXT(iter, cn_next_child)) != NULL;) { + if (strcmp(iter->isoDirRecord->name, + next->isoDirRecord->name) != 0) { + iter = TAILQ_NEXT(iter, cn_next_child); + continue; + } + flag = 1; + temp_skip = skip = cd9660_count_collisions(iter); + end_of_range = iter; + while (temp_skip > 0) { + temp_skip--; + end_of_range = TAILQ_NEXT(end_of_range, cn_next_child); + } + temp_past = past; + while (temp_past > 0) { + if ((next = TAILQ_NEXT(end_of_range, cn_next_child)) != NULL) + end_of_range = next; + else if ((prev = TAILQ_PREV(iter, cd9660_children_head, cn_next_child)) != NULL) + iter = prev; + else + delete_chars++; + temp_past--; + } + skip += past; + iter = cd9660_rename_filename(diskStructure, iter, skip, + delete_chars); + } + return flag; +} + + +static cd9660node * +cd9660_rename_filename(iso9660_disk *diskStructure, cd9660node *iter, int num, + int delete_chars) +{ + int i = 0; + int numbts, dot, semi, digit, digits, temp, powers, multiplier, count; + char *naming; + int maxlength; + char *tmp; + + if (diskStructure->verbose_level > 0) + printf("Rename_filename called\n"); + + /* TODO : A LOT of chanes regarding 8.3 filenames */ + if (diskStructure->isoLevel == 1) + maxlength = 8; + else if (diskStructure->isoLevel == 2) + maxlength = 31; + else + maxlength = ISO_FILENAME_MAXLENGTH_BEFORE_VERSION; + + tmp = emalloc(ISO_FILENAME_MAXLENGTH_WITH_PADDING); + + while (i < num) { + powers = 1; + count = 0; + digits = 1; + multiplier = 1; + while (((int)(i / powers) ) >= 10) { + digits++; + powers = powers * 10; + } + + naming = iter->o_name; + + /* + while ((*naming != '.') && (*naming != ';')) { + naming++; + count++; + } + */ + + dot = -1; + semi = -1; + while (count < maxlength) { + if (*naming == '.') + dot = count; + else if (*naming == ';') { + semi = count; + break; + } + naming++; + count++; + } + + if ((count + digits) < maxlength) + numbts = count; + else + numbts = maxlength - (digits); + numbts -= delete_chars; + + /* 8.3 rules - keep the extension, add before the dot */ + + /* + * This code makes a bunch of assumptions. + * See if you can spot them all :) + */ + +#if 0 + if (diskStructure->isoLevel == 1) { + numbts = 8 - digits - delete_chars; + if (dot < 0) { + + } else { + if (dot < 8) { + memmove(&tmp[numbts],&tmp[dot],4); + } + } + } +#else + __USE(dot); + __USE(semi); + __USE(multiplier); +#endif + + /* (copying just the filename before the '.' */ + memcpy(tmp, (iter->o_name), numbts); + + /* adding the appropriate number following the name */ + temp = i; + while (digits > 0) { + digit = (int)(temp / powers); + temp = temp - digit * powers; + sprintf(&tmp[numbts] , "%d", digit); + digits--; + numbts++; + powers = powers / 10; + } + + while ((*naming != ';') && (numbts < maxlength)) { + tmp[numbts] = (*naming); + naming++; + numbts++; + } + + tmp[numbts] = ';'; + tmp[numbts+1] = '1'; + tmp[numbts+2] = '\0'; + + /* + * now tmp has exactly the identifier + * we want so we'll copy it back to record + */ + memcpy((iter->isoDirRecord->name), tmp, numbts + 3); + + iter = TAILQ_NEXT(iter, cn_next_child); + i++; + } + + free(tmp); + return iter; +} + +/* Todo: Figure out why these functions are nec. */ +static void +cd9660_copy_filenames(iso9660_disk *diskStructure, cd9660node *node) +{ + cd9660node *cn; + + if (TAILQ_EMPTY(&node->cn_children)) + return; + + if (TAILQ_FIRST(&node->cn_children)->isoDirRecord == NULL) { + debug_print_tree(diskStructure, diskStructure->rootNode, 0); + exit(1); + } + + TAILQ_FOREACH(cn, &node->cn_children, cn_next_child) { + cd9660_copy_filenames(diskStructure, cn); + memcpy(cn->o_name, cn->isoDirRecord->name, + ISO_FILENAME_MAXLENGTH_WITH_PADDING); + } +} + +static void +cd9660_sorting_nodes(cd9660node *node) +{ + cd9660node *cn; + + TAILQ_FOREACH(cn, &node->cn_children, cn_next_child) + cd9660_sorting_nodes(cn); + cd9660_sort_nodes(node); +} + +/* XXX Bubble sort. */ +static void +cd9660_sort_nodes(cd9660node *node) +{ + cd9660node *cn, *next; + + do { + TAILQ_FOREACH(cn, &node->cn_children, cn_next_child) { + if ((next = TAILQ_NEXT(cn, cn_next_child)) == NULL) + return; + else if (strcmp(next->isoDirRecord->name, + cn->isoDirRecord->name) >= 0) + continue; + TAILQ_REMOVE(&node->cn_children, next, cn_next_child); + TAILQ_INSERT_BEFORE(cn, next, cn_next_child); + break; + } + } while (cn != NULL); +} + +static int +cd9660_count_collisions(cd9660node *copy) +{ + int count = 0; + cd9660node *iter, *next; + + for (iter = copy; + (next = TAILQ_NEXT(iter, cn_next_child)) != NULL; + iter = next) { + if (cd9660_compare_filename(iter->isoDirRecord->name, + next->isoDirRecord->name) == 0) + count++; + else + return count; + } +#if 0 + if ((next = TAILQ_NEXT(iter, cn_next_child)) != NULL) { + printf("cd9660_recurse_on_collision: count is %i \n", count); + compare = cd9660_compare_filename(iter->isoDirRecord->name, + next->isoDirRecord->name); + if (compare == 0) { + count++; + return cd9660_recurse_on_collision(next, count); + } else + return count; + } +#endif + return count; +} + +static cd9660node * +cd9660_rrip_move_directory(iso9660_disk *diskStructure, cd9660node *dir) +{ + char newname[9]; + cd9660node *tfile; + + /* + * This function needs to: + * 1) Create an empty virtual file in place of the old directory + * 2) Point the virtual file to the new directory + * 3) Point the relocated directory to its old parent + * 4) Move the directory specified by dir into rr_moved_dir, + * and rename it to "diskStructure->rock_ridge_move_count" (as a string) + */ + + /* First see if the moved directory even exists */ + if (diskStructure->rr_moved_dir == NULL) { + diskStructure->rr_moved_dir = cd9660_create_directory( + diskStructure, ISO_RRIP_DEFAULT_MOVE_DIR_NAME, + diskStructure->rootNode, dir); + if (diskStructure->rr_moved_dir == NULL) + return 0; + } + + /* Create a file with the same ORIGINAL name */ + tfile = cd9660_create_file(diskStructure, dir->node->name, dir->parent, + dir); + if (tfile == NULL) + return NULL; + + diskStructure->rock_ridge_move_count++; + snprintf(newname, sizeof(newname), "%08i", + diskStructure->rock_ridge_move_count); + + /* Point to old parent */ + dir->rr_real_parent = dir->parent; + + /* Place the placeholder file */ + if (TAILQ_EMPTY(&dir->rr_real_parent->cn_children)) { + TAILQ_INSERT_HEAD(&dir->rr_real_parent->cn_children, tfile, + cn_next_child); + } else { + cd9660_sorted_child_insert(dir->rr_real_parent, tfile); + } + + /* Point to new parent */ + dir->parent = diskStructure->rr_moved_dir; + + /* Point the file to the moved directory */ + tfile->rr_relocated = dir; + + /* Actually move the directory */ + cd9660_sorted_child_insert(diskStructure->rr_moved_dir, dir); + + /* TODO: Inherit permissions / ownership (basically the entire inode) */ + + /* Set the new name */ + memset(dir->isoDirRecord->name, 0, ISO_FILENAME_MAXLENGTH_WITH_PADDING); + strncpy(dir->isoDirRecord->name, newname, 8); + dir->isoDirRecord->length[0] = 34 + 8; + dir->isoDirRecord->name_len[0] = 8; + + return dir; +} + +static int +cd9660_add_dot_records(iso9660_disk *diskStructure, cd9660node *root) +{ + struct cd9660_children_head *head = &root->cn_children; + cd9660node *cn; + + TAILQ_FOREACH(cn, head, cn_next_child) { + if ((cn->type & CD9660_TYPE_DIR) == 0) + continue; + /* Recursion first */ + cd9660_add_dot_records(diskStructure, cn); + } + cd9660_create_special_directory(diskStructure, CD9660_TYPE_DOT, root); + cd9660_create_special_directory(diskStructure, CD9660_TYPE_DOTDOT, + root); + return 1; +} + +/* + * Convert node to cd9660 structure + * This function is designed to be called recursively on the root node of + * the filesystem + * Lots of recursion going on here, want to make sure it is efficient + * @param struct fsnode * The root node to be converted + * @param struct cd9660* The parent node (should not be NULL) + * @param int Current directory depth + * @param int* Running count of the number of directories that are being created + */ +static void +cd9660_convert_structure(iso9660_disk *diskStructure, fsnode *root, + cd9660node *parent_node, int level, int *numDirectories, int *error) +{ + fsnode *iterator = root; + cd9660node *this_node; + int working_level; + int add; + int flag = 0; + int counter = 0; + + /* + * Newer, more efficient method, reduces recursion depth + */ + if (root == NULL) { + warnx("%s: root is null\n", __func__); + return; + } + + /* Test for an empty directory - makefs still gives us the . record */ + if ((S_ISDIR(root->type)) && (root->name[0] == '.') + && (root->name[1] == '\0')) { + root = root->next; + if (root == NULL) + return; + } + if ((this_node = cd9660_allocate_cd9660node()) == NULL) { + CD9660_MEM_ALLOC_ERROR(__func__); + } + + /* + * To reduce the number of recursive calls, we will iterate over + * the next pointers to the right. + */ + while (iterator != NULL) { + add = 1; + /* + * Increment the directory count if this is a directory + * Ignore "." entries. We will generate them later + */ + if (!S_ISDIR(iterator->type) || + strcmp(iterator->name, ".") != 0) { + + /* Translate the node, including its filename */ + this_node->parent = parent_node; + cd9660_translate_node(diskStructure, iterator, + this_node); + this_node->level = level; + + if (S_ISDIR(iterator->type)) { + (*numDirectories)++; + this_node->type = CD9660_TYPE_DIR; + working_level = level + 1; + + /* + * If at level 8, directory would be at 8 + * and have children at 9 which is not + * allowed as per ISO spec + */ + if (level == 8) { + if ((!diskStructure->allow_deep_trees) && + (!diskStructure->rock_ridge_enabled)) { + warnx("error: found entry " + "with depth greater " + "than 8."); + (*error) = 1; + return; + } else if (diskStructure-> + rock_ridge_enabled) { + working_level = 3; + /* + * Moved directory is actually + * at level 2. + */ + this_node->level = + working_level - 1; + if (cd9660_rrip_move_directory( + diskStructure, + this_node) == 0) { + warnx("Failure in " + "cd9660_rrip_" + "move_directory" + ); + (*error) = 1; + return; + } + add = 0; + } + } + + /* Do the recursive call on the children */ + if (iterator->child != 0) { + cd9660_convert_structure(diskStructure, + iterator->child, this_node, + working_level, + numDirectories, error); + + if ((*error) == 1) { + warnx("%s: Error on recursive " + "call", __func__); + return; + } + } + + } else { + /* Only directories should have children */ + assert(iterator->child == NULL); + + this_node->type = CD9660_TYPE_FILE; + } + + /* + * Finally, do a sorted insert + */ + if (add) { + cd9660_sorted_child_insert( + parent_node, this_node); + } + + /*Allocate new temp_node */ + if (iterator->next != 0) { + this_node = cd9660_allocate_cd9660node(); + if (this_node == NULL) + CD9660_MEM_ALLOC_ERROR(__func__); + } + } + iterator = iterator->next; + } + + /* cd9660_handle_collisions(first_node); */ + + /* TODO: need cleanup */ + cd9660_copy_filenames(diskStructure, parent_node); + + do { + flag = cd9660_handle_collisions(diskStructure, parent_node, + counter); + counter++; + cd9660_sorting_nodes(parent_node); + } while ((flag == 1) && (counter < 100)); +} + +/* + * Clean up the cd9660node tree + * This is designed to be called recursively on the root node + * @param struct cd9660node *root The node to free + * @returns void + */ +static void +cd9660_free_structure(cd9660node *root) +{ + cd9660node *cn; + + while ((cn = TAILQ_FIRST(&root->cn_children)) != NULL) { + TAILQ_REMOVE(&root->cn_children, cn, cn_next_child); + cd9660_free_structure(cn); + } + free(root); +} + +/* + * Be a little more memory conservative: + * instead of having the TAILQ_ENTRY as part of the cd9660node, + * just create a temporary structure + */ +struct ptq_entry +{ + TAILQ_ENTRY(ptq_entry) ptq; + cd9660node *node; +} *n; + +#define PTQUEUE_NEW(n,s,r,t){\ + n = emalloc(sizeof(struct s)); \ + if (n == NULL) \ + return r; \ + n->node = t;\ +} + +/* + * Generate the path tables + * The specific implementation of this function is left as an exercise to the + * programmer. It could be done recursively. Make sure you read how the path + * table has to be laid out, it has levels. + * @param struct iso9660_disk *disk The disk image + * @returns int The number of built path tables (between 1 and 4), 0 on failure + */ +static int +cd9660_generate_path_table(iso9660_disk *diskStructure) +{ + cd9660node *cn, *dirNode = diskStructure->rootNode; + cd9660node *last = dirNode; + int pathTableSize = 0; /* computed as we go */ + int counter = 1; /* root gets a count of 0 */ + + TAILQ_HEAD(cd9660_pt_head, ptq_entry) pt_head; + TAILQ_INIT(&pt_head); + + PTQUEUE_NEW(n, ptq_entry, -1, diskStructure->rootNode); + + /* Push the root node */ + TAILQ_INSERT_HEAD(&pt_head, n, ptq); + + /* Breadth-first traversal of file structure */ + while (pt_head.tqh_first != 0) { + n = pt_head.tqh_first; + dirNode = n->node; + TAILQ_REMOVE(&pt_head, pt_head.tqh_first, ptq); + free(n); + + /* Update the size */ + pathTableSize += ISO_PATHTABLE_ENTRY_BASESIZE + + dirNode->isoDirRecord->name_len[0]+ + (dirNode->isoDirRecord->name_len[0] % 2 == 0 ? 0 : 1); + /* includes the padding bit */ + + dirNode->ptnumber=counter; + if (dirNode != last) { + last->ptnext = dirNode; + dirNode->ptprev = last; + } + last = dirNode; + + /* Push children onto queue */ + TAILQ_FOREACH(cn, &dirNode->cn_children, cn_next_child) { + /* + * Dont add the DOT and DOTDOT types to the path + * table. + */ + if ((cn->type != CD9660_TYPE_DOT) + && (cn->type != CD9660_TYPE_DOTDOT)) { + + if (S_ISDIR(cn->node->type)) { + PTQUEUE_NEW(n, ptq_entry, -1, cn); + TAILQ_INSERT_TAIL(&pt_head, n, ptq); + } + } + } + counter++; + } + return pathTableSize; +} + +void +cd9660_compute_full_filename(cd9660node *node, char *buf) +{ + int len; + + len = CD9660MAXPATH + 1; + len = snprintf(buf, len, "%s/%s/%s", node->node->root, + node->node->path, node->node->name); + if (len > CD9660MAXPATH) + errx(1, "Pathname too long."); +} + +/* NEW filename conversion method */ +typedef int(*cd9660_filename_conversion_functor)(iso9660_disk *, const char *, + char *, int); + + +/* + * TODO: These two functions are almost identical. + * Some code cleanup is possible here + * + * XXX bounds checking! + */ +static int +cd9660_level1_convert_filename(iso9660_disk *diskStructure, const char *oldname, + char *newname, int is_file) +{ + /* + * ISO 9660 : 10.1 + * File Name shall not contain more than 8 d or d1 characters + * File Name Extension shall not contain more than 3 d or d1 characters + * Directory Identifier shall not contain more than 8 d or d1 characters + */ + int namelen = 0; + int extlen = 0; + int found_ext = 0; + + while (*oldname != '\0' && extlen < 3) { + /* Handle period first, as it is special */ + if (*oldname == '.') { + if (found_ext) { + *newname++ = '_'; + extlen ++; + } + else { + *newname++ = '.'; + found_ext = 1; + } + } else { + /* cut RISC OS file type off ISO name */ + if (diskStructure->archimedes_enabled && + *oldname == ',' && strlen(oldname) == 4) + break; + + /* Enforce 12.3 / 8 */ + if (namelen == 8 && !found_ext) + break; + + if (islower((unsigned char)*oldname)) + *newname++ = toupper((unsigned char)*oldname); + else if (isupper((unsigned char)*oldname) + || isdigit((unsigned char)*oldname)) + *newname++ = *oldname; + else + *newname++ = '_'; + + if (found_ext) + extlen++; + else + namelen++; + } + oldname++; + } + if (is_file) { + if (!found_ext && !diskStructure->omit_trailing_period) + *newname++ = '.'; + /* Add version */ + sprintf(newname, ";%i", 1); + } + return namelen + extlen + found_ext; +} + +/* XXX bounds checking! */ +static int +cd9660_level2_convert_filename(iso9660_disk *diskStructure, const char *oldname, + char *newname, int is_file) +{ + /* + * ISO 9660 : 7.5.1 + * File name : 0+ d or d1 characters + * separator 1 (.) + * File name extension : 0+ d or d1 characters + * separator 2 (;) + * File version number (5 characters, 1-32767) + * 1 <= Sum of File name and File name extension <= 30 + */ + int namelen = 0; + int extlen = 0; + int found_ext = 0; + + while (*oldname != '\0' && namelen + extlen < 30) { + /* Handle period first, as it is special */ + if (*oldname == '.') { + if (found_ext) { + if (diskStructure->allow_multidot) { + *newname++ = '.'; + } else { + *newname++ = '_'; + } + extlen ++; + } + else { + *newname++ = '.'; + found_ext = 1; + } + } else { + /* cut RISC OS file type off ISO name */ + if (diskStructure->archimedes_enabled && + *oldname == ',' && strlen(oldname) == 4) + break; + + if (islower((unsigned char)*oldname)) + *newname++ = toupper((unsigned char)*oldname); + else if (isupper((unsigned char)*oldname) || + isdigit((unsigned char)*oldname)) + *newname++ = *oldname; + else if (diskStructure->allow_multidot && + *oldname == '.') { + *newname++ = '.'; + } else { + *newname++ = '_'; + } + + if (found_ext) + extlen++; + else + namelen++; + } + oldname ++; + } + if (is_file) { + if (!found_ext && !diskStructure->omit_trailing_period) + *newname++ = '.'; + /* Add version */ + sprintf(newname, ";%i", 1); + } + return namelen + extlen + found_ext; +} + +#if 0 +static int +cd9660_joliet_convert_filename(iso9660_disk *diskStructure, const char *oldname, + char *newname, int is_file) +{ + /* TODO: implement later, move to cd9660_joliet.c ?? */ +} +#endif + + +/* + * Convert a file name to ISO compliant file name + * @param char * oldname The original filename + * @param char ** newname The new file name, in the appropriate character + * set and of appropriate length + * @param int 1 if file, 0 if directory + * @returns int The length of the new string + */ +static int +cd9660_convert_filename(iso9660_disk *diskStructure, const char *oldname, + char *newname, int is_file) +{ + /* NEW */ + cd9660_filename_conversion_functor conversion_function = 0; + if (diskStructure->isoLevel == 1) + conversion_function = &cd9660_level1_convert_filename; + else if (diskStructure->isoLevel == 2) + conversion_function = &cd9660_level2_convert_filename; + return (*conversion_function)(diskStructure, oldname, newname, is_file); +} + +int +cd9660_compute_record_size(iso9660_disk *diskStructure, cd9660node *node) +{ + int size = node->isoDirRecord->length[0]; + + if (diskStructure->rock_ridge_enabled) + size += node->susp_entry_size; + size += node->su_tail_size; + size += size & 1; /* Ensure length of record is even. */ + assert(size <= 254); + return size; +} + +static void +cd9660_populate_dot_records(iso9660_disk *diskStructure, cd9660node *node) +{ + node->dot_record->fileDataSector = node->fileDataSector; + memcpy(node->dot_record->isoDirRecord,node->isoDirRecord, 34); + node->dot_record->isoDirRecord->name_len[0] = 1; + node->dot_record->isoDirRecord->name[0] = 0; + node->dot_record->isoDirRecord->name[1] = 0; + node->dot_record->isoDirRecord->length[0] = 34; + node->dot_record->fileRecordSize = + cd9660_compute_record_size(diskStructure, node->dot_record); + + if (node == diskStructure->rootNode) { + node->dot_dot_record->fileDataSector = node->fileDataSector; + memcpy(node->dot_dot_record->isoDirRecord,node->isoDirRecord, + 34); + } else { + node->dot_dot_record->fileDataSector = + node->parent->fileDataSector; + memcpy(node->dot_dot_record->isoDirRecord, + node->parent->isoDirRecord,34); + } + node->dot_dot_record->isoDirRecord->name_len[0] = 1; + node->dot_dot_record->isoDirRecord->name[0] = 1; + node->dot_dot_record->isoDirRecord->name[1] = 0; + node->dot_dot_record->isoDirRecord->length[0] = 34; + node->dot_dot_record->fileRecordSize = + cd9660_compute_record_size(diskStructure, node->dot_dot_record); +} + +/* + * @param struct cd9660node *node The node + * @param int The offset (in bytes) - SHOULD align to the beginning of a sector + * @returns int The total size of files and directory entries (should be + * a multiple of sector size) +*/ +static int64_t +cd9660_compute_offsets(iso9660_disk *diskStructure, cd9660node *node, + int64_t startOffset) +{ + /* + * This function needs to compute the size of directory records and + * runs, file lengths, and set the appropriate variables both in + * cd9660node and isoDirEntry + */ + int64_t used_bytes = 0; + int64_t current_sector_usage = 0; + cd9660node *child; + fsinode *inode; + int64_t r; + + assert(node != NULL); + + + /* + * NOTE : There needs to be some special case detection for + * the "real root" node, since for it, node->node is undefined + */ + + node->fileDataSector = -1; + + if (node->type & CD9660_TYPE_DIR) { + node->fileRecordSize = cd9660_compute_record_size( + diskStructure, node); + /*Set what sector this directory starts in*/ + node->fileDataSector = + CD9660_BLOCKS(diskStructure->sectorSize,startOffset); + + cd9660_bothendian_dword(node->fileDataSector, + node->isoDirRecord->extent); + + /* + * First loop over children, need to know the size of + * their directory records + */ + node->fileSectorsUsed = 1; + TAILQ_FOREACH(child, &node->cn_children, cn_next_child) { + node->fileDataLength += + cd9660_compute_record_size(diskStructure, child); + if ((cd9660_compute_record_size(diskStructure, child) + + current_sector_usage) >= + diskStructure->sectorSize) { + current_sector_usage = 0; + node->fileSectorsUsed++; + } + + current_sector_usage += + cd9660_compute_record_size(diskStructure, child); + } + + cd9660_bothendian_dword(node->fileSectorsUsed * + diskStructure->sectorSize,node->isoDirRecord->size); + + /* + * This should point to the sector after the directory + * record (or, the first byte in that sector) + */ + used_bytes += node->fileSectorsUsed * diskStructure->sectorSize; + + for (child = TAILQ_NEXT(node->dot_dot_record, cn_next_child); + child != NULL; child = TAILQ_NEXT(child, cn_next_child)) { + /* Directories need recursive call */ + if (S_ISDIR(child->node->type)) { + r = cd9660_compute_offsets(diskStructure, child, + used_bytes + startOffset); + + if (r != -1) + used_bytes += r; + else + return -1; + } + } + + /* Explicitly set the . and .. records */ + cd9660_populate_dot_records(diskStructure, node); + + /* Finally, do another iteration to write the file data*/ + for (child = TAILQ_NEXT(node->dot_dot_record, cn_next_child); + child != NULL; + child = TAILQ_NEXT(child, cn_next_child)) { + /* Files need extent set */ + if (S_ISDIR(child->node->type)) + continue; + child->fileRecordSize = + cd9660_compute_record_size(diskStructure, child); + + child->fileSectorsUsed = + CD9660_BLOCKS(diskStructure->sectorSize, + child->fileDataLength); + + inode = child->node->inode; + if ((inode->flags & FI_ALLOCATED) == 0) { + inode->ino = + CD9660_BLOCKS(diskStructure->sectorSize, + used_bytes + startOffset); + inode->flags |= FI_ALLOCATED; + used_bytes += child->fileSectorsUsed * + diskStructure->sectorSize; + } else { + INODE_WARNX(("%s: already allocated inode %d " + "data sectors at %" PRIu32, __func__, + (int)inode->st.st_ino, inode->ino)); + } + child->fileDataSector = inode->ino; + cd9660_bothendian_dword(child->fileDataSector, + child->isoDirRecord->extent); + } + } + + return used_bytes; +} + +#if 0 +/* Might get rid of this func */ +static int +cd9660_copy_stat_info(cd9660node *from, cd9660node *to, int file) +{ + to->node->inode->st.st_dev = 0; + to->node->inode->st.st_ino = 0; + to->node->inode->st.st_size = 0; + to->node->inode->st.st_blksize = from->node->inode->st.st_blksize; + to->node->inode->st.st_atime = from->node->inode->st.st_atime; + to->node->inode->st.st_mtime = from->node->inode->st.st_mtime; + to->node->inode->st.st_ctime = from->node->inode->st.st_ctime; + to->node->inode->st.st_uid = from->node->inode->st.st_uid; + to->node->inode->st.st_gid = from->node->inode->st.st_gid; + to->node->inode->st.st_mode = from->node->inode->st.st_mode; + /* Clear out type */ + to->node->inode->st.st_mode = to->node->inode->st.st_mode & ~(S_IFMT); + if (file) + to->node->inode->st.st_mode |= S_IFREG; + else + to->node->inode->st.st_mode |= S_IFDIR; + return 1; +} +#endif + +static cd9660node * +cd9660_create_virtual_entry(iso9660_disk *diskStructure, const char *name, + cd9660node *parent, int file, int insert) +{ + cd9660node *temp; + fsnode * tfsnode; + + assert(parent != NULL); + + temp = cd9660_allocate_cd9660node(); + if (temp == NULL) + return NULL; + + tfsnode = emalloc(sizeof(*tfsnode)); + tfsnode->name = estrdup(name); + temp->isoDirRecord = emalloc(sizeof(*temp->isoDirRecord)); + + cd9660_convert_filename(diskStructure, tfsnode->name, + temp->isoDirRecord->name, file); + + temp->node = tfsnode; + temp->parent = parent; + + if (insert) { + if (temp->parent != NULL) { + temp->level = temp->parent->level + 1; + if (!TAILQ_EMPTY(&temp->parent->cn_children)) + cd9660_sorted_child_insert(temp->parent, temp); + else + TAILQ_INSERT_HEAD(&temp->parent->cn_children, + temp, cn_next_child); + } + } + + if (parent->node != NULL) { + tfsnode->type = parent->node->type; + } + + /* Clear out file type bits */ + tfsnode->type &= ~(S_IFMT); + if (file) + tfsnode->type |= S_IFREG; + else + tfsnode->type |= S_IFDIR; + + /* Indicate that there is no spec entry (inode) */ + tfsnode->flags &= ~(FSNODE_F_HASSPEC); +#if 0 + cd9660_copy_stat_info(parent, temp, file); +#endif + return temp; +} + +static cd9660node * +cd9660_create_file(iso9660_disk *diskStructure, const char *name, + cd9660node *parent, cd9660node *me) +{ + cd9660node *temp; + + temp = cd9660_create_virtual_entry(diskStructure, name, parent, 1, 1); + if (temp == NULL) + return NULL; + + temp->fileDataLength = 0; + + temp->type = CD9660_TYPE_FILE | CD9660_TYPE_VIRTUAL; + + temp->node->inode = ecalloc(1, sizeof(*temp->node->inode)); + *temp->node->inode = *me->node->inode; + + if (cd9660_translate_node_common(diskStructure, temp) == 0) + return NULL; + return temp; +} + +/* + * Create a new directory which does not exist on disk + * @param const char * name The name to assign to the directory + * @param const char * parent Pointer to the parent directory + * @returns cd9660node * Pointer to the new directory + */ +static cd9660node * +cd9660_create_directory(iso9660_disk *diskStructure, const char *name, + cd9660node *parent, cd9660node *me) +{ + cd9660node *temp; + + temp = cd9660_create_virtual_entry(diskStructure, name, parent, 0, 1); + if (temp == NULL) + return NULL; + temp->node->type |= S_IFDIR; + + temp->type = CD9660_TYPE_DIR | CD9660_TYPE_VIRTUAL; + + temp->node->inode = ecalloc(1, sizeof(*temp->node->inode)); + *temp->node->inode = *me->node->inode; + + if (cd9660_translate_node_common(diskStructure, temp) == 0) + return NULL; + return temp; +} + +static cd9660node * +cd9660_create_special_directory(iso9660_disk *diskStructure, u_char type, + cd9660node *parent) +{ + cd9660node *temp, *first; + char na[2]; + + assert(parent != NULL); + + if (type == CD9660_TYPE_DOT) + na[0] = 0; + else if (type == CD9660_TYPE_DOTDOT) + na[0] = 1; + else + return 0; + + na[1] = 0; + if ((temp = cd9660_create_virtual_entry(diskStructure, na, parent, + 0, 0)) == NULL) + return NULL; + + temp->parent = parent; + temp->type = type; + temp->isoDirRecord->length[0] = 34; + /* Dot record is always first */ + if (type == CD9660_TYPE_DOT) { + parent->dot_record = temp; + TAILQ_INSERT_HEAD(&parent->cn_children, temp, cn_next_child); + /* DotDot should be second */ + } else if (type == CD9660_TYPE_DOTDOT) { + parent->dot_dot_record = temp; + /* + * If the first child is the dot record, insert + * this second. Otherwise, insert it at the head. + */ + if ((first = TAILQ_FIRST(&parent->cn_children)) == NULL || + (first->type & CD9660_TYPE_DOT) == 0) { + TAILQ_INSERT_HEAD(&parent->cn_children, temp, + cn_next_child); + } else { + TAILQ_INSERT_AFTER(&parent->cn_children, first, temp, + cn_next_child); + } + } + + return temp; +} + +static int +cd9660_add_generic_bootimage(iso9660_disk *diskStructure, const char *bootimage) +{ + struct stat stbuf; + + assert(bootimage != NULL); + + if (*bootimage == '\0') { + warnx("Error: Boot image must be a filename"); + return 0; + } + + diskStructure->generic_bootimage = estrdup(bootimage); + + /* Get information about the file */ + if (lstat(diskStructure->generic_bootimage, &stbuf) == -1) + err(EXIT_FAILURE, "%s: lstat(\"%s\")", __func__, + diskStructure->generic_bootimage); + + if (stbuf.st_size > 32768) { + warnx("Error: Boot image must be no greater than 32768 bytes"); + return 0; + } + + if (diskStructure->verbose_level > 0) { + printf("Generic boot image image has size %lld\n", + (long long)stbuf.st_size); + } + + diskStructure->has_generic_bootimage = 1; + + return 1; +} diff --git a/usr.sbin/makefs/cd9660.h b/usr.sbin/makefs/cd9660.h new file mode 100644 index 000000000..ef9f44f34 --- /dev/null +++ b/usr.sbin/makefs/cd9660.h @@ -0,0 +1,357 @@ +/* $NetBSD: cd9660.h,v 1.20 2013/01/29 15:52:25 christos Exp $ */ + +/* + * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan + * Perez-Rathke and Ram Vedam. All rights reserved. + * + * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys, + * Alan Perez-Rathke and Ram Vedam. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#ifndef _MAKEFS_CD9660_H +#define _MAKEFS_CD9660_H + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "makefs.h" +#include "iso.h" +#include "iso_rrip.h" +#include "cd9660/cd9660_eltorito.h" + +#ifdef DEBUG +#define INODE_WARNX(__x) warnx __x +#else /* DEBUG */ +#define INODE_WARNX(__x) +#endif /* DEBUG */ + +#define CD9660MAXPATH 4096 + +#define ISO_STRING_FILTER_NONE = 0x00 +#define ISO_STRING_FILTER_DCHARS = 0x01 +#define ISO_STRING_FILTER_ACHARS = 0x02 + +/* +Extended preferences type, in the spirit of what makefs gives us (only ints) +*/ +typedef struct { + const char *shortName; /* Short option */ + const char *name; /* option name */ + char *value; /* where to stuff the value */ + int minLength; /* minimum for value */ + int maxLength; /* maximum for value */ + const char *desc; /* option description */ + int filterFlags; +} string_option_t; + +/******** STRUCTURES **********/ + +/*Defaults*/ +#define ISO_DEFAULT_VOLUMEID "MAKEFS_CD9660_IMAGE" +#define ISO_DEFAULT_APPID "MAKEFS" +#define ISO_DEFAULT_PUBLISHER "MAKEFS" +#define ISO_DEFAULT_PREPARER "MAKEFS" + +#define ISO_VOLUME_DESCRIPTOR_STANDARD_ID "CD001" +#define ISO_VOLUME_DESCRIPTOR_BOOT 0 +#define ISO_VOLUME_DESCRIPTOR_PVD 1 +#define ISO_VOLUME_DESCRIPTOR_TERMINATOR 255 + +/*30 for name and extension, as well as version number and padding bit*/ +#define ISO_FILENAME_MAXLENGTH_BEFORE_VERSION 30 +#define ISO_FILENAME_MAXLENGTH 36 +#define ISO_FILENAME_MAXLENGTH_WITH_PADDING 37 + +#define ISO_FLAG_CLEAR 0x00 +#define ISO_FLAG_HIDDEN 0x01 +#define ISO_FLAG_DIRECTORY 0x02 +#define ISO_FLAG_ASSOCIATED 0x04 +#define ISO_FLAG_PERMISSIONS 0x08 +#define ISO_FLAG_RESERVED5 0x10 +#define ISO_FLAG_RESERVED6 0x20 +#define ISO_FLAG_FINAL_RECORD 0x40 + +#define ISO_PATHTABLE_ENTRY_BASESIZE 8 + +#define ISO_RRIP_DEFAULT_MOVE_DIR_NAME "RR_MOVED" +#define RRIP_DEFAULT_MOVE_DIR_NAME ".rr_moved" + +#define CD9660_BLOCKS(__sector_size, __bytes) \ + howmany((__bytes), (__sector_size)) + +#define CD9660_MEM_ALLOC_ERROR(_F) \ + err(EXIT_FAILURE, "%s, %s l. %d", _F, __FILE__, __LINE__) + +#define CD9660_TYPE_FILE 0x01 +#define CD9660_TYPE_DIR 0x02 +#define CD9660_TYPE_DOT 0x04 +#define CD9660_TYPE_DOTDOT 0x08 +#define CD9660_TYPE_VIRTUAL 0x80 + +#define CD9660_INODE_HASH_SIZE 1024 +#define CD9660_SECTOR_SIZE 2048 + +#define CD9660_END_PADDING 150 + +/* Slight modification of the ISO structure in iso.h */ +typedef struct _iso_directory_record_cd9660 { + u_char length [ISODCL (1, 1)]; /* 711 */ + u_char ext_attr_length [ISODCL (2, 2)]; /* 711 */ + u_char extent [ISODCL (3, 10)]; /* 733 */ + u_char size [ISODCL (11, 18)]; /* 733 */ + u_char date [ISODCL (19, 25)]; /* 7 by 711 */ + u_char flags [ISODCL (26, 26)]; + u_char file_unit_size [ISODCL (27, 27)]; /* 711 */ + u_char interleave [ISODCL (28, 28)]; /* 711 */ + u_char volume_sequence_number [ISODCL (29, 32)]; /* 723 */ + u_char name_len [ISODCL (33, 33)]; /* 711 */ + char name [ISO_FILENAME_MAXLENGTH_WITH_PADDING]; +} iso_directory_record_cd9660; + +/* TODO: Lots of optimization of this structure */ +typedef struct _cd9660node { + u_char type;/* Used internally */ + /* Tree structure */ + struct _cd9660node *parent; /* parent (NULL if root) */ + TAILQ_HEAD(cd9660_children_head, _cd9660node) cn_children; + TAILQ_ENTRY(_cd9660node) cn_next_child; + + struct _cd9660node *dot_record; /* For directories, used mainly in RRIP */ + struct _cd9660node *dot_dot_record; + + fsnode *node; /* pointer to fsnode */ + struct _iso_directory_record_cd9660 *isoDirRecord; + struct iso_extended_attributes *isoExtAttributes; + + /***** SIZE CALCULATION *****/ + /*already stored in isoDirRecord, but this is an int version, and will be + copied to isoDirRecord on writing*/ + uint32_t fileDataSector; + + /* + * same thing, though some notes: + * If a file, this is the file size + * If a directory, this is the size of all its children's + * directory records + * plus necessary padding + */ + int64_t fileDataLength; + + int64_t fileSectorsUsed; + int fileRecordSize;/*copy of a variable, int for quicker calculations*/ + + /* Old name, used for renaming - needs to be optimized but low priority */ + char o_name [ISO_FILENAME_MAXLENGTH_WITH_PADDING]; + + /***** SPACE RESERVED FOR EXTENSIONS *****/ + /* For memory efficiency's sake - we should move this to a separate struct + and point to null if not needed */ + /* For Rock Ridge */ + struct _cd9660node *rr_real_parent, *rr_relocated; + + int64_t susp_entry_size; + int64_t susp_dot_entry_size; + int64_t susp_dot_dot_entry_size; + + /* Continuation area stuff */ + int64_t susp_entry_ce_start; + int64_t susp_dot_ce_start; + int64_t susp_dot_dot_ce_start; + + int64_t susp_entry_ce_length; + int64_t susp_dot_ce_length; + int64_t susp_dot_dot_ce_length; + + /* Data to put at the end of the System Use field */ + int64_t su_tail_size; + char *su_tail_data; + + /*** PATH TABLE STUFF ***/ + int level; /*depth*/ + int ptnumber; + struct _cd9660node *ptnext, *ptprev, *ptlast; + + /* SUSP entries */ + TAILQ_HEAD(susp_linked_list, ISO_SUSP_ATTRIBUTES) head; +} cd9660node; + +typedef struct _path_table_entry +{ + u_char length[ISODCL (1, 1)]; + u_char extended_attribute_length[ISODCL (2, 2)]; + u_char first_sector[ISODCL (3, 6)]; + u_char parent_number[ISODCL (7, 8)]; + u_char name[ISO_FILENAME_MAXLENGTH_WITH_PADDING]; +} path_table_entry; + +typedef struct _volume_descriptor +{ + u_char *volumeDescriptorData; /*ALWAYS 2048 bytes long*/ + int64_t sector; + struct _volume_descriptor *next; +} volume_descriptor; + +typedef struct _iso9660_disk { + int sectorSize; + struct iso_primary_descriptor primaryDescriptor; + struct iso_supplementary_descriptor supplementaryDescriptor; + + volume_descriptor *firstVolumeDescriptor; + + cd9660node *rootNode; + + /* Important sector numbers here */ + /* primaryDescriptor.type_l_path_table*/ + int64_t primaryBigEndianTableSector; + + /* primaryDescriptor.type_m_path_table*/ + int64_t primaryLittleEndianTableSector; + + /* primaryDescriptor.opt_type_l_path_table*/ + int64_t secondaryBigEndianTableSector; + + /* primaryDescriptor.opt_type_m_path_table*/ + int64_t secondaryLittleEndianTableSector; + + /* primaryDescriptor.path_table_size*/ + int pathTableLength; + int64_t dataFirstSector; + + int64_t totalSectors; + /* OPTIONS GO HERE */ + int isoLevel; + + int include_padding_areas; + + int follow_sym_links; + int verbose_level; + int displayHelp; + int keep_bad_images; + + /* SUSP options and variables */ + int64_t susp_continuation_area_start_sector; + int64_t susp_continuation_area_size; + int64_t susp_continuation_area_current_free; + + int rock_ridge_enabled; + /* Other Rock Ridge Variables */ + char *rock_ridge_renamed_dir_name; + int rock_ridge_move_count; + cd9660node *rr_moved_dir; + + int archimedes_enabled; + int chrp_boot; + + /* Spec breaking options */ + u_char allow_deep_trees; + u_char allow_start_dot; + u_char allow_max_name; /* Allow 37 char filenames*/ + u_char allow_illegal_chars; /* ~, !, # */ + u_char allow_lowercase; + u_char allow_multidot; + u_char omit_trailing_period; + + /* BOOT INFORMATION HERE */ + int has_generic_bootimage; /* Default to 0 */ + char *generic_bootimage; + + int is_bootable;/* Default to 0 */ + int64_t boot_catalog_sector; + boot_volume_descriptor *boot_descriptor; + char * boot_image_directory; + + TAILQ_HEAD(boot_image_list,cd9660_boot_image) boot_images; + int image_serialno; + LIST_HEAD(boot_catalog_entries,boot_catalog_entry) boot_entries; + +} iso9660_disk; + +/************ FUNCTIONS **************/ +int cd9660_valid_a_chars(const char *); +int cd9660_valid_d_chars(const char *); +void cd9660_uppercase_characters(char *, int); + +/* ISO Data Types */ +void cd9660_721(uint16_t, unsigned char *); +void cd9660_731(uint32_t, unsigned char *); +void cd9660_722(uint16_t, unsigned char *); +void cd9660_732(uint32_t, unsigned char *); +void cd9660_bothendian_dword(uint32_t dw, unsigned char *); +void cd9660_bothendian_word(uint16_t dw, unsigned char *); +void cd9660_set_date(char *, time_t); +void cd9660_time_8426(unsigned char *, time_t); +void cd9660_time_915(unsigned char *, time_t); + +/*** Boot Functions ***/ +int cd9660_write_generic_bootimage(FILE *); +int cd9660_write_boot(iso9660_disk *, FILE *); +int cd9660_add_boot_disk(iso9660_disk *, const char *); +int cd9660_eltorito_add_boot_option(iso9660_disk *, const char *, + const char *); +int cd9660_setup_boot(iso9660_disk *, int); +int cd9660_setup_boot_volume_descriptor(iso9660_disk *, + volume_descriptor *); + + +/*** Write Functions ***/ +int cd9660_write_image(iso9660_disk *, const char *image); +int cd9660_copy_file(iso9660_disk *, FILE *, off_t, const char *); + +void cd9660_compute_full_filename(cd9660node *, char *); +int cd9660_compute_record_size(iso9660_disk *, cd9660node *); + +/* Debugging functions */ +void debug_print_tree(iso9660_disk *, cd9660node *,int); +void debug_print_path_tree(cd9660node *); +void debug_print_volume_descriptor_information(iso9660_disk *); +void debug_dump_to_xml_ptentry(path_table_entry *,int, int); +void debug_dump_to_xml_path_table(FILE *, off_t, int, int); +void debug_dump_to_xml(FILE *); +int debug_get_encoded_number(unsigned char *, int); +void debug_dump_integer(const char *, char *,int); +void debug_dump_string(const char *,unsigned char *,int); +void debug_dump_directory_record_9_1(unsigned char *); +void debug_dump_to_xml_volume_descriptor(unsigned char *,int); + +void cd9660_pad_string_spaces(char *, int); + +#endif diff --git a/usr.sbin/makefs/cd9660/Makefile.inc b/usr.sbin/makefs/cd9660/Makefile.inc new file mode 100644 index 000000000..bffba6601 --- /dev/null +++ b/usr.sbin/makefs/cd9660/Makefile.inc @@ -0,0 +1,15 @@ +# $NetBSD: Makefile.inc,v 1.3 2012/08/10 12:10:29 joerg Exp $ +# + +.PATH: ${.CURDIR}/cd9660 ${NETBSDSRCDIR}/sys/fs/cd9660 + +CPPFLAGS+=-I${NETBSDSRCDIR}/sys/fs/cd9660 + +SRCS+= cd9660_strings.c cd9660_debug.c cd9660_eltorito.c +SRCS+= cd9660_write.c cd9660_conversion.c iso9660_rrip.c cd9660_archimedes.c + +.if !defined(HOSTPROGNAME) +.for f in cd9660_debug cd9660_write +COPTS.${f}.c+= -Wno-pointer-sign +.endfor +.endif diff --git a/usr.sbin/makefs/cd9660/cd9660_archimedes.c b/usr.sbin/makefs/cd9660/cd9660_archimedes.c new file mode 100644 index 000000000..45bf2ace1 --- /dev/null +++ b/usr.sbin/makefs/cd9660/cd9660_archimedes.c @@ -0,0 +1,130 @@ +/* $NetBSD: cd9660_archimedes.c,v 1.2 2013/01/28 21:03:28 christos Exp $ */ + +/*- + * Copyright (c) 1998, 2009 Ben Harris + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * cd9660_archimedes.c - support for RISC OS "ARCHIMEDES" extension + * + * RISC OS CDFS looks for a special block at the end of the System Use + * Field for each file. If present, this contains the RISC OS load + * and exec address (used to hold the file timestamp and type), the + * file attributes, and a flag indicating whether the first character + * of the filename should be replaced with '!' (since many special + * RISC OS filenames do). + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: cd9660_archimedes.c,v 1.2 2013/01/28 21:03:28 christos Exp $"); +#endif /* !__lint */ + +#include +#include +#include +#include +#include + +#include "makefs.h" +#include "cd9660.h" +#include "cd9660_archimedes.h" + +/* + * Convert a Unix time_t (non-leap seconds since 1970-01-01) to a RISC + * OS time (non-leap(?) centiseconds since 1900-01-01(?)). + */ + +static u_int64_t +riscos_date(time_t unixtime) +{ + u_int64_t base; + + base = 31536000ULL * 70 + 86400 * 17; + return (((u_int64_t)unixtime) + base)*100; +} + +/* + * Add "ARCHIMEDES" metadata to a node if that seems appropriate. + * + * We touch regular files with names matching /,[0-9a-f]{3}$/ and + * directories matching /^!/. + */ +static void +archimedes_convert_node(cd9660node *node) +{ + struct ISO_ARCHIMEDES *arc; + size_t len; + int type = -1; + uint64_t stamp; + + if (node->su_tail_data != NULL) + /* Something else already has the tail. */ + return; + + len = strlen(node->node->name); + if (len < 1) return; + + if (len >= 4 && node->node->name[len-4] == ',') + /* XXX should support ,xxx and ,lxa */ + type = strtoul(node->node->name + len - 3, NULL, 16); + if (type == -1 && node->node->name[0] != '!') + return; + if (type == -1) type = 0; + + assert(sizeof(*arc) == 32); + arc = ecalloc(1, sizeof(*arc)); + + stamp = riscos_date(node->node->inode->st.st_mtime); + + memcpy(arc->magic, "ARCHIMEDES", 10); + cd9660_731(0xfff00000 | (type << 8) | (stamp >> 32), arc->loadaddr); + cd9660_731(stamp & 0x00ffffffffULL, arc->execaddr); + arc->ro_attr = RO_ACCESS_UR | RO_ACCESS_OR; + arc->cdfs_attr = node->node->name[0] == '!' ? CDFS_PLING : 0; + node->su_tail_data = (void *)arc; + node->su_tail_size = sizeof(*arc); +} + +/* + * Add "ARCHIMEDES" metadata to an entire tree recursively. + */ +void +archimedes_convert_tree(cd9660node *node) +{ + cd9660node *cn; + + assert(node != NULL); + + archimedes_convert_node(node); + + /* Recurse on children. */ + TAILQ_FOREACH(cn, &node->cn_children, cn_next_child) + archimedes_convert_tree(cn); +} diff --git a/usr.sbin/makefs/cd9660/cd9660_archimedes.h b/usr.sbin/makefs/cd9660/cd9660_archimedes.h new file mode 100644 index 000000000..61338d269 --- /dev/null +++ b/usr.sbin/makefs/cd9660/cd9660_archimedes.h @@ -0,0 +1,48 @@ +/* $NetBSD: cd9660_archimedes.h,v 1.1 2009/01/10 22:06:29 bjh21 Exp $ */ + +/*- + * Copyright (c) 1998, 2009 Ben Harris + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * cd9660_archimedes.c - support for RISC OS "ARCHIMEDES" extension + */ + +struct ISO_ARCHIMEDES { + char magic[10]; /* "ARCHIMEDES" */ + unsigned char loadaddr[4]; /* Load address, little-endian */ + unsigned char execaddr[4]; /* Exec address, little-endian */ + unsigned char ro_attr; /* RISC OS attributes */ +#define RO_ACCESS_UR 0x01 /* Owner read */ +#define RO_ACCESS_UW 0x02 /* Owner write */ +#define RO_ACCESS_L 0x04 /* Locked */ +#define RO_ACCESS_OR 0x10 /* Public read */ +#define RO_ACCESS_OW 0x20 /* Public write */ + unsigned char cdfs_attr; /* Extra attributes for CDFS */ +#define CDFS_PLING 0x01 /* Filename begins with '!' */ + char reserved[12]; +}; + +extern void archimedes_convert_tree(cd9660node *); diff --git a/usr.sbin/makefs/cd9660/cd9660_conversion.c b/usr.sbin/makefs/cd9660/cd9660_conversion.c new file mode 100644 index 000000000..ae1e954c8 --- /dev/null +++ b/usr.sbin/makefs/cd9660/cd9660_conversion.c @@ -0,0 +1,203 @@ +/* $NetBSD: cd9660_conversion.c,v 1.4 2007/03/14 14:11:17 christos Exp $ */ + +/* + * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan + * Perez-Rathke and Ram Vedam. All rights reserved. + * + * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys, + * Alan Perez-Rathke and Ram Vedam. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ +#include "cd9660.h" + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: cd9660_conversion.c,v 1.4 2007/03/14 14:11:17 christos Exp $"); +#endif /* !__lint */ + + +static char cd9660_compute_gm_offset(time_t); + +#if 0 +static inline int +cd9660_pad_even(length) +int length; +{ + return length + (length & 0x01); +} +#endif + +/* +* These can probably be implemented using a macro +*/ + +/* Little endian */ +void +cd9660_721(uint16_t w, unsigned char *twochar) +{ +#if BYTE_ORDER == BIG_ENDIAN + w = bswap16(w); +#endif + memcpy(twochar,&w,2); +} + +void +cd9660_731(uint32_t w, unsigned char *fourchar) +{ +#if BYTE_ORDER == BIG_ENDIAN + w = bswap32(w); +#endif + memcpy(fourchar,&w,4); +} + +/* Big endian */ +void +cd9660_722(uint16_t w, unsigned char *twochar) +{ +#if BYTE_ORDER == LITTLE_ENDIAN + w = bswap16(w); +#endif + memcpy(twochar,&w,2); +} + +void +cd9660_732(uint32_t w, unsigned char *fourchar) +{ +#if BYTE_ORDER == LITTLE_ENDIAN + w = bswap32(w); +#endif + memcpy(fourchar,&w,4); +} + +/** +* Convert a dword into a double endian string of eight characters +* @param int The double word to convert +* @param char* The string to write the both endian double word to - It is assumed this is allocated and at least +* eight characters long +*/ +void +cd9660_bothendian_dword(uint32_t dw, unsigned char *eightchar) +{ + uint32_t le, be; +#if BYTE_ORDER == LITTLE_ENDIAN + le = dw; + be = bswap32(dw); +#endif +#if BYTE_ORDER == BIG_ENDIAN + be = dw; + le = bswap32(dw); +#endif + memcpy(eightchar, &le, 4); + memcpy((eightchar+4), &be, 4); +} + +/** +* Convert a word into a double endian string of four characters +* @param int The word to convert +* @param char* The string to write the both endian word to - It is assumed this is allocated and at least +* four characters long +*/ +void +cd9660_bothendian_word(uint16_t dw, unsigned char *fourchar) +{ + uint16_t le, be; +#if BYTE_ORDER == LITTLE_ENDIAN + le = dw; + be = bswap16(dw); +#endif +#if BYTE_ORDER == BIG_ENDIAN + be = dw; + le = bswap16(dw); +#endif + memcpy(fourchar, &le, 2); + memcpy((fourchar+2), &be, 2); +} + +void +cd9660_pad_string_spaces(char *str, int len) +{ + int i; + + for (i = 0; i < len; i ++) { + if (str[i] == '\0') + str[i] = 0x20; + } +} + +static char +cd9660_compute_gm_offset(time_t tim) +{ + struct tm t, gm; + + (void)localtime_r(&tim, &t); + (void)gmtime_r(&tim, &gm); + gm.tm_year -= t.tm_year; + gm.tm_yday -= t.tm_yday; + gm.tm_hour -= t.tm_hour; + gm.tm_min -= t.tm_min; + if (gm.tm_year < 0) + gm.tm_yday = -1; + else if (gm.tm_year > 0) + gm.tm_yday = 1; + + return (char)(-(gm.tm_min + 60* (24 * gm.tm_yday + gm.tm_hour)) / 15); +} + +/* Long dates: 17 characters */ +void +cd9660_time_8426(unsigned char *buf, time_t tim) +{ + struct tm t; + char temp[18]; + + (void)localtime_r(&tim, &t); + (void)snprintf(temp, sizeof(temp), "%04i%02i%02i%02i%02i%02i%02i", + 1900+(int)t.tm_year, + (int)t.tm_mon+1, + (int)t.tm_mday, + (int)t.tm_hour, + (int)t.tm_min, + (int)t.tm_sec, + 0); + (void)memcpy(buf, temp, 16); + buf[16] = cd9660_compute_gm_offset(tim); +} + +/* Short dates: 7 characters */ +void +cd9660_time_915(unsigned char *buf, time_t tim) +{ + struct tm t; + + (void)localtime_r(&tim, &t); + buf[0] = t.tm_year; + buf[1] = t.tm_mon+1; + buf[2] = t.tm_mday; + buf[3] = t.tm_hour; + buf[4] = t.tm_min; + buf[5] = t.tm_sec; + buf[6] = cd9660_compute_gm_offset(tim); +} diff --git a/usr.sbin/makefs/cd9660/cd9660_debug.c b/usr.sbin/makefs/cd9660/cd9660_debug.c new file mode 100644 index 000000000..33e2b3e01 --- /dev/null +++ b/usr.sbin/makefs/cd9660/cd9660_debug.c @@ -0,0 +1,498 @@ +/* $NetBSD: cd9660_debug.c,v 1.13 2013/10/19 17:16:37 christos Exp $ */ + +/* + * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan + * Perez-Rathke and Ram Vedam. All rights reserved. + * + * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys, + * Alan Perez-Rathke and Ram Vedam. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#include + +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: cd9660_debug.c,v 1.13 2013/10/19 17:16:37 christos Exp $"); +#endif /* !__lint */ + +#if !HAVE_NBTOOL_CONFIG_H +#include +#endif + +#include "makefs.h" +#include "cd9660.h" +#include "iso9660_rrip.h" + +static void debug_print_susp_attrs(cd9660node *, int); +static void debug_dump_to_xml_padded_hex_output(const char *, unsigned char *, + int); + +static inline void +print_n_tabs(int n) +{ + int i; + + for (i = 1; i <= n; i ++) + printf("\t"); +} + +#if 0 +void +debug_print_rrip_info(n) +cd9660node *n; +{ + struct ISO_SUSP_ATTRIBUTES *t; + TAILQ_FOREACH(t, &node->head, rr_ll) { + + } +} +#endif + +static void +debug_print_susp_attrs(cd9660node *n, int indent) +{ + struct ISO_SUSP_ATTRIBUTES *t; + + TAILQ_FOREACH(t, &n->head, rr_ll) { + print_n_tabs(indent); + printf("-"); + printf("%c%c: L:%i",t->attr.su_entry.SP.h.type[0], + t->attr.su_entry.SP.h.type[1], + (int)t->attr.su_entry.SP.h.length[0]); + printf("\n"); + } +} + +void +debug_print_tree(iso9660_disk *diskStructure, cd9660node *node, int level) +{ +#if !HAVE_NBTOOL_CONFIG_H + cd9660node *cn; + + print_n_tabs(level); + if (node->type & CD9660_TYPE_DOT) { + printf(". (%i)\n", + isonum_733(node->isoDirRecord->extent)); + } else if (node->type & CD9660_TYPE_DOTDOT) { + printf("..(%i)\n", + isonum_733(node->isoDirRecord->extent)); + } else if (node->isoDirRecord->name[0]=='\0') { + printf("(ROOT) (%" PRIu32 " to %" PRId64 ")\n", + node->fileDataSector, + node->fileDataSector + + node->fileSectorsUsed - 1); + } else { + printf("%s (%s) (%" PRIu32 " to %" PRId64 ")\n", + node->isoDirRecord->name, + (node->isoDirRecord->flags[0] + & ISO_FLAG_DIRECTORY) ? "DIR" : "FILE", + node->fileDataSector, + (node->fileSectorsUsed == 0) ? + node->fileDataSector : + node->fileDataSector + + node->fileSectorsUsed - 1); + } + if (diskStructure->rock_ridge_enabled) + debug_print_susp_attrs(node, level + 1); + TAILQ_FOREACH(cn, &node->cn_children, cn_next_child) + debug_print_tree(diskStructure, cn, level + 1); +#else + printf("Sorry, debugging is not supported in host-tools mode.\n"); +#endif +} + +void +debug_print_path_tree(cd9660node *n) +{ + cd9660node *iterator = n; + + /* Only display this message when called with the root node */ + if (n->parent == NULL) + printf("debug_print_path_table: Dumping path table contents\n"); + + while (iterator != NULL) { + if (iterator->isoDirRecord->name[0] == '\0') + printf("0) (ROOT)\n"); + else + printf("%i) %s\n", iterator->level, + iterator->isoDirRecord->name); + + iterator = iterator->ptnext; + } +} + +void +debug_print_volume_descriptor_information(iso9660_disk *diskStructure) +{ + volume_descriptor *tmp = diskStructure->firstVolumeDescriptor; + char temp[CD9660_SECTOR_SIZE]; + + printf("==Listing Volume Descriptors==\n"); + + while (tmp != NULL) { + memset(temp, 0, CD9660_SECTOR_SIZE); + memcpy(temp, tmp->volumeDescriptorData + 1, 5); + printf("Volume descriptor in sector %" PRId64 + ": type %i, ID %s\n", + tmp->sector, tmp->volumeDescriptorData[0], temp); + switch(tmp->volumeDescriptorData[0]) { + case 0:/*boot record*/ + break; + + case 1: /* PVD */ + break; + + case 2: /* SVD */ + break; + + case 3: /* Volume Partition Descriptor */ + break; + + case 255: /* terminator */ + break; + } + tmp = tmp->next; + } + + printf("==Done Listing Volume Descriptors==\n"); +} + +void +debug_dump_to_xml_ptentry(path_table_entry *pttemp, int num, int mode) +{ + printf("\n" ,num); + printf("%i\n", pttemp->length[0]); + printf("%i\n", + pttemp->extended_attribute_length[0]); + printf("%i\n", + debug_get_encoded_number(pttemp->parent_number,mode)); + debug_dump_to_xml_padded_hex_output("name", + pttemp->name, pttemp->length[0]); + printf("\n"); +} + +void +debug_dump_to_xml_path_table(FILE *fd, off_t sector, int size, int mode) +{ + path_table_entry pttemp; + int t = 0; + int n = 0; + + if (fseeko(fd, CD9660_SECTOR_SIZE * sector, SEEK_SET) == -1) + err(1, "fseeko"); + + while (t < size) { + /* Read fixed data first */ + fread(&pttemp, 1, 8, fd); + t += 8; + /* Read variable */ + fread(((unsigned char*)&pttemp) + 8, 1, pttemp.length[0], fd); + t += pttemp.length[0]; + debug_dump_to_xml_ptentry(&pttemp, n, mode); + n++; + } + +} + +/* + * XML Debug output functions + * Dump hierarchy of CD, as well as volume info, to XML + * Can be used later to diff against a standard, + * or just provide easy to read detailed debug output + */ +void +debug_dump_to_xml(FILE *fd) +{ + unsigned char buf[CD9660_SECTOR_SIZE]; + off_t sector; + int t, t2; + struct iso_primary_descriptor primaryVD; + struct _boot_volume_descriptor bootVD; + + memset(&primaryVD, 0, sizeof(primaryVD)); + printf("\n"); + + /* Display Volume Descriptors */ + sector = 16; + do { + if (fseeko(fd, CD9660_SECTOR_SIZE * sector, SEEK_SET) == -1) + err(1, "fseeko"); + fread(buf, 1, CD9660_SECTOR_SIZE, fd); + t = (int)((unsigned char)buf[0]); + switch (t) { + case 0: + memcpy(&bootVD, buf, CD9660_SECTOR_SIZE); + break; + case 1: + memcpy(&primaryVD, buf, CD9660_SECTOR_SIZE); + break; + } + debug_dump_to_xml_volume_descriptor(buf, sector); + sector++; + } while (t != 255); + + t = debug_get_encoded_number((u_char *)primaryVD.type_l_path_table, + 731); + t2 = debug_get_encoded_number((u_char *)primaryVD.path_table_size, 733); + printf("Path table 1 located at sector %i and is %i bytes long\n", + t,t2); + debug_dump_to_xml_path_table(fd, t, t2, 721); + + t = debug_get_encoded_number((u_char *)primaryVD.type_m_path_table, + 731); + debug_dump_to_xml_path_table(fd, t, t2, 722); + + printf("\n"); +} + +static void +debug_dump_to_xml_padded_hex_output(const char *element, unsigned char *buf, + int len) +{ + int i; + int t; + + printf("<%s>",element); + for (i = 0; i < len; i++) { + t = (unsigned char)buf[i]; + if (t >= 32 && t < 127) + printf("%c",t); + } + printf("\n",element); + + printf("<%s:hex>",element); + for (i = 0; i < len; i++) { + t = (unsigned char)buf[i]; + printf(" %x",t); + } + printf("\n",element); +} + +int +debug_get_encoded_number(unsigned char* buf, int mode) +{ +#if !HAVE_NBTOOL_CONFIG_H + switch (mode) { + /* 711: Single bite */ + case 711: + return isonum_711(buf); + + /* 712: Single signed byte */ + case 712: + return isonum_712((signed char *)buf); + + /* 721: 16 bit LE */ + case 721: + return isonum_721(buf); + + /* 731: 32 bit LE */ + case 731: + return isonum_731(buf); + + /* 722: 16 bit BE */ + case 722: + return isonum_722(buf); + + /* 732: 32 bit BE */ + case 732: + return isonum_732(buf); + + /* 723: 16 bit bothE */ + case 723: + return isonum_723(buf); + + /* 733: 32 bit bothE */ + case 733: + return isonum_733(buf); + } +#endif + return 0; +} + +void +debug_dump_integer(const char *element, char* buf, int mode) +{ + printf("<%s>%i\n", element, + debug_get_encoded_number((unsigned char *)buf, mode), element); +} + +void +debug_dump_string(const char *element __unused, unsigned char *buf __unused, int len __unused) +{ + +} + +void +debug_dump_directory_record_9_1(unsigned char* buf) +{ + printf("\n"); + debug_dump_integer("length", + ((struct iso_directory_record*) buf)->length, 711); + debug_dump_integer("ext_attr_length", + ((struct iso_directory_record*) buf)->ext_attr_length,711); + debug_dump_integer("extent", + (char *)((struct iso_directory_record*) buf)->extent, 733); + debug_dump_integer("size", + (char *)((struct iso_directory_record*) buf)->size, 733); + debug_dump_integer("flags", + ((struct iso_directory_record*) buf)->flags, 711); + debug_dump_integer("file_unit_size", + ((struct iso_directory_record*) buf)->file_unit_size,711); + debug_dump_integer("interleave", + ((struct iso_directory_record*) buf)->interleave, 711); + debug_dump_integer("volume_sequence_number", + ((struct iso_directory_record*) buf)->volume_sequence_number, + 723); + debug_dump_integer("name_len", + ((struct iso_directory_record*) buf)->name_len, 711); + debug_dump_to_xml_padded_hex_output("name", + (u_char *)((struct iso_directory_record*) buf)->name, + debug_get_encoded_number((u_char *) + ((struct iso_directory_record*) buf)->length, 711)); + printf("\n"); +} + + +void +debug_dump_to_xml_volume_descriptor(unsigned char* buf, int sector) +{ + printf("\n", sector); + printf(""); + switch(buf[0]) { + case 0: + printf("boot"); + break; + + case 1: + printf("primary"); + break; + + case 2: + printf("supplementary"); + break; + + case 3: + printf("volume partition descriptor"); + break; + + case 255: + printf("terminator"); + break; + } + + printf("\n"); + switch(buf[0]) { + case 1: + debug_dump_integer("type", + ((struct iso_primary_descriptor*)buf)->type, 711); + debug_dump_to_xml_padded_hex_output("id", + (u_char *)((struct iso_primary_descriptor*) buf)->id, + ISODCL ( 2, 6)); + debug_dump_integer("version", + ((struct iso_primary_descriptor*)buf)->version, + 711); + debug_dump_to_xml_padded_hex_output("system_id", + (u_char *)((struct iso_primary_descriptor*)buf)->system_id, + ISODCL(9,40)); + debug_dump_to_xml_padded_hex_output("volume_id", + (u_char *)((struct iso_primary_descriptor*)buf)->volume_id, + ISODCL(41,72)); + debug_dump_integer("volume_space_size", + ((struct iso_primary_descriptor*)buf)->volume_space_size, + 733); + debug_dump_integer("volume_set_size", + ((struct iso_primary_descriptor*)buf)->volume_set_size, + 733); + debug_dump_integer("volume_sequence_number", + ((struct iso_primary_descriptor*)buf)->volume_sequence_number, + 723); + debug_dump_integer("logical_block_size", + ((struct iso_primary_descriptor*)buf)->logical_block_size, + 723); + debug_dump_integer("path_table_size", + ((struct iso_primary_descriptor*)buf)->path_table_size, + 733); + debug_dump_integer("type_l_path_table", + ((struct iso_primary_descriptor*)buf)->type_l_path_table, + 731); + debug_dump_integer("opt_type_l_path_table", + ((struct iso_primary_descriptor*)buf)->opt_type_l_path_table, + 731); + debug_dump_integer("type_m_path_table", + ((struct iso_primary_descriptor*)buf)->type_m_path_table, + 732); + debug_dump_integer("opt_type_m_path_table", + ((struct iso_primary_descriptor*)buf)->opt_type_m_path_table,732); + debug_dump_directory_record_9_1( + (u_char *)((struct iso_primary_descriptor*)buf)->root_directory_record); + debug_dump_to_xml_padded_hex_output("volume_set_id", + (u_char *)((struct iso_primary_descriptor*) buf)->volume_set_id, + ISODCL (191, 318)); + debug_dump_to_xml_padded_hex_output("publisher_id", + (u_char *)((struct iso_primary_descriptor*) buf)->publisher_id, + ISODCL (319, 446)); + debug_dump_to_xml_padded_hex_output("preparer_id", + (u_char *)((struct iso_primary_descriptor*) buf)->preparer_id, + ISODCL (447, 574)); + debug_dump_to_xml_padded_hex_output("application_id", + (u_char *)((struct iso_primary_descriptor*) buf)->application_id, + ISODCL (575, 702)); + debug_dump_to_xml_padded_hex_output("copyright_file_id", + (u_char *)((struct iso_primary_descriptor*) buf)->copyright_file_id, + ISODCL (703, 739)); + debug_dump_to_xml_padded_hex_output("abstract_file_id", + (u_char *)((struct iso_primary_descriptor*) buf)->abstract_file_id, + ISODCL (740, 776)); + debug_dump_to_xml_padded_hex_output("bibliographic_file_id", + (u_char *)((struct iso_primary_descriptor*) buf)->bibliographic_file_id, + ISODCL (777, 813)); + + debug_dump_to_xml_padded_hex_output("creation_date", + (u_char *)((struct iso_primary_descriptor*) buf)->creation_date, + ISODCL (814, 830)); + debug_dump_to_xml_padded_hex_output("modification_date", + (u_char *)((struct iso_primary_descriptor*) buf)->modification_date, + ISODCL (831, 847)); + debug_dump_to_xml_padded_hex_output("expiration_date", + (u_char *)((struct iso_primary_descriptor*) buf)->expiration_date, + ISODCL (848, 864)); + debug_dump_to_xml_padded_hex_output("effective_date", + (u_char *)((struct iso_primary_descriptor*) buf)->effective_date, + ISODCL (865, 881)); + + debug_dump_to_xml_padded_hex_output("file_structure_version", + (u_char *)((struct iso_primary_descriptor*) buf)->file_structure_version, + ISODCL(882,882)); + break; + } + printf("\n"); +} + diff --git a/usr.sbin/makefs/cd9660/cd9660_eltorito.c b/usr.sbin/makefs/cd9660/cd9660_eltorito.c new file mode 100644 index 000000000..dc93425a0 --- /dev/null +++ b/usr.sbin/makefs/cd9660/cd9660_eltorito.c @@ -0,0 +1,698 @@ +/* $NetBSD: cd9660_eltorito.c,v 1.20 2013/01/28 21:03:28 christos Exp $ */ + +/* + * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan + * Perez-Rathke and Ram Vedam. All rights reserved. + * + * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys, + * Alan Perez-Rathke and Ram Vedam. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + + +#include "cd9660.h" +#include "cd9660_eltorito.h" +#include +#include + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: cd9660_eltorito.c,v 1.20 2013/01/28 21:03:28 christos Exp $"); +#endif /* !__lint */ + +#ifdef DEBUG +#define ELTORITO_DPRINTF(__x) printf __x +#else +#define ELTORITO_DPRINTF(__x) +#endif + +#include + +static struct boot_catalog_entry *cd9660_init_boot_catalog_entry(void); +static struct boot_catalog_entry *cd9660_boot_setup_validation_entry(char); +static struct boot_catalog_entry *cd9660_boot_setup_default_entry( + struct cd9660_boot_image *); +static struct boot_catalog_entry *cd9660_boot_setup_section_head(char); +static struct boot_catalog_entry *cd9660_boot_setup_validation_entry(char); +#if 0 +static u_char cd9660_boot_get_system_type(struct cd9660_boot_image *); +#endif + +int +cd9660_add_boot_disk(iso9660_disk *diskStructure, const char *boot_info) +{ + struct stat stbuf; + const char *mode_msg; + char *temp; + char *sysname; + char *filename; + struct cd9660_boot_image *new_image, *tmp_image; + + assert(boot_info != NULL); + + if (*boot_info == '\0') { + warnx("Error: Boot disk information must be in the " + "format 'system;filename'"); + return 0; + } + + /* First decode the boot information */ + temp = estrdup(boot_info); + + sysname = temp; + filename = strchr(sysname, ';'); + if (filename == NULL) { + warnx("supply boot disk information in the format " + "'system;filename'"); + free(temp); + return 0; + } + + *filename++ = '\0'; + + if (diskStructure->verbose_level > 0) { + printf("Found bootdisk with system %s, and filename %s\n", + sysname, filename); + } + new_image = ecalloc(1, sizeof(*new_image)); + new_image->loadSegment = 0; /* default for now */ + + /* Decode System */ + if (strcmp(sysname, "i386") == 0) + new_image->system = ET_SYS_X86; + else if (strcmp(sysname, "powerpc") == 0) + new_image->system = ET_SYS_PPC; + else if (strcmp(sysname, "macppc") == 0 || + strcmp(sysname, "mac68k") == 0) + new_image->system = ET_SYS_MAC; + else { + warnx("boot disk system must be " + "i386, powerpc, macppc, or mac68k"); + free(temp); + free(new_image); + return 0; + } + + + new_image->filename = estrdup(filename); + + free(temp); + + /* Get information about the file */ + if (lstat(new_image->filename, &stbuf) == -1) + err(EXIT_FAILURE, "%s: lstat(\"%s\")", __func__, + new_image->filename); + + switch (stbuf.st_size) { + case 1440 * 1024: + new_image->targetMode = ET_MEDIA_144FDD; + mode_msg = "Assigned boot image to 1.44 emulation mode"; + break; + case 1200 * 1024: + new_image->targetMode = ET_MEDIA_12FDD; + mode_msg = "Assigned boot image to 1.2 emulation mode"; + break; + case 2880 * 1024: + new_image->targetMode = ET_MEDIA_288FDD; + mode_msg = "Assigned boot image to 2.88 emulation mode"; + break; + default: + new_image->targetMode = ET_MEDIA_NOEM; + mode_msg = "Assigned boot image to no emulation mode"; + break; + } + + if (diskStructure->verbose_level > 0) + printf("%s\n", mode_msg); + + new_image->size = stbuf.st_size; + new_image->num_sectors = + howmany(new_image->size, diskStructure->sectorSize) * + howmany(diskStructure->sectorSize, 512); + if (diskStructure->verbose_level > 0) { + printf("New image has size %d, uses %d 512-byte sectors\n", + new_image->size, new_image->num_sectors); + } + new_image->sector = -1; + /* Bootable by default */ + new_image->bootable = ET_BOOTABLE; + /* Add boot disk */ + + /* Group images for the same platform together. */ + TAILQ_FOREACH(tmp_image, &diskStructure->boot_images, image_list) { + if (tmp_image->system != new_image->system) + break; + } + + if (tmp_image == NULL) { + TAILQ_INSERT_HEAD(&diskStructure->boot_images, new_image, + image_list); + } else + TAILQ_INSERT_BEFORE(tmp_image, new_image, image_list); + + new_image->serialno = diskStructure->image_serialno++; + + /* TODO : Need to do anything about the boot image in the tree? */ + diskStructure->is_bootable = 1; + + return 1; +} + +int +cd9660_eltorito_add_boot_option(iso9660_disk *diskStructure, + const char *option_string, const char *value) +{ + char *eptr; + struct cd9660_boot_image *image; + + assert(option_string != NULL); + + /* Find the last image added */ + TAILQ_FOREACH(image, &diskStructure->boot_images, image_list) { + if (image->serialno + 1 == diskStructure->image_serialno) + break; + } + if (image == NULL) + errx(EXIT_FAILURE, "Attempted to add boot option, " + "but no boot images have been specified"); + + if (strcmp(option_string, "no-emul-boot") == 0) { + image->targetMode = ET_MEDIA_NOEM; + } else if (strcmp(option_string, "no-boot") == 0) { + image->bootable = ET_NOT_BOOTABLE; + } else if (strcmp(option_string, "hard-disk-boot") == 0) { + image->targetMode = ET_MEDIA_HDD; + } else if (strcmp(option_string, "boot-load-segment") == 0) { + image->loadSegment = strtoul(value, &eptr, 16); + if (eptr == value || *eptr != '\0' || errno != ERANGE) { + warn("%s: strtoul", __func__); + return 0; + } + } else { + return 0; + } + return 1; +} + +static struct boot_catalog_entry * +cd9660_init_boot_catalog_entry(void) +{ + return ecalloc(1, sizeof(struct boot_catalog_entry)); +} + +static struct boot_catalog_entry * +cd9660_boot_setup_validation_entry(char sys) +{ + struct boot_catalog_entry *entry; + boot_catalog_validation_entry *ve; + int16_t checksum; + unsigned char *csptr; + size_t i; + entry = cd9660_init_boot_catalog_entry(); + + ve = &entry->entry_data.VE; + + ve->header_id[0] = 1; + ve->platform_id[0] = sys; + ve->key[0] = 0x55; + ve->key[1] = 0xAA; + + /* Calculate checksum */ + checksum = 0; + cd9660_721(0, ve->checksum); + csptr = (unsigned char*)ve; + for (i = 0; i < sizeof(*ve); i += 2) { + checksum += (int16_t)csptr[i]; + checksum += 256 * (int16_t)csptr[i + 1]; + } + checksum = -checksum; + cd9660_721(checksum, ve->checksum); + + ELTORITO_DPRINTF(("%s: header_id %d, platform_id %d, key[0] %d, key[1] %d, " + "checksum %04x\n", __func__, ve->header_id[0], ve->platform_id[0], + ve->key[0], ve->key[1], checksum)); + return entry; +} + +static struct boot_catalog_entry * +cd9660_boot_setup_default_entry(struct cd9660_boot_image *disk) +{ + struct boot_catalog_entry *default_entry; + boot_catalog_initial_entry *ie; + + default_entry = cd9660_init_boot_catalog_entry(); + if (default_entry == NULL) + return NULL; + + ie = &default_entry->entry_data.IE; + + ie->boot_indicator[0] = disk->bootable; + ie->media_type[0] = disk->targetMode; + cd9660_721(disk->loadSegment, ie->load_segment); + ie->system_type[0] = disk->system; + cd9660_721(disk->num_sectors, ie->sector_count); + cd9660_731(disk->sector, ie->load_rba); + + ELTORITO_DPRINTF(("%s: boot indicator %d, media type %d, " + "load segment %04x, system type %d, sector count %d, " + "load rba %d\n", __func__, ie->boot_indicator[0], + ie->media_type[0], disk->loadSegment, ie->system_type[0], + disk->num_sectors, disk->sector)); + return default_entry; +} + +static struct boot_catalog_entry * +cd9660_boot_setup_section_head(char platform) +{ + struct boot_catalog_entry *entry; + boot_catalog_section_header *sh; + + entry = cd9660_init_boot_catalog_entry(); + if (entry == NULL) + return NULL; + + sh = &entry->entry_data.SH; + /* More by default. The last one will manually be set to 0x91 */ + sh->header_indicator[0] = ET_SECTION_HEADER_MORE; + sh->platform_id[0] = platform; + sh->num_section_entries[0] = 0; + return entry; +} + +static struct boot_catalog_entry * +cd9660_boot_setup_section_entry(struct cd9660_boot_image *disk) +{ + struct boot_catalog_entry *entry; + boot_catalog_section_entry *se; + if ((entry = cd9660_init_boot_catalog_entry()) == NULL) + return NULL; + + se = &entry->entry_data.SE; + + se->boot_indicator[0] = ET_BOOTABLE; + se->media_type[0] = disk->targetMode; + cd9660_721(disk->loadSegment, se->load_segment); + cd9660_721(disk->num_sectors, se->sector_count); + cd9660_731(disk->sector, se->load_rba); + return entry; +} + +#if 0 +static u_char +cd9660_boot_get_system_type(struct cd9660_boot_image *disk) +{ + /* + For hard drive booting, we need to examine the MBR to figure + out what the partition type is + */ + return 0; +} +#endif + +/* + * Set up the BVD, Boot catalog, and the boot entries, but do no writing + */ +int +cd9660_setup_boot(iso9660_disk *diskStructure, int first_sector) +{ + int sector; + int used_sectors; + int num_entries = 0; + int catalog_sectors; + struct boot_catalog_entry *x86_head, *mac_head, *ppc_head, + *valid_entry, *default_entry, *temp, *head, **headp, *next; + struct cd9660_boot_image *tmp_disk; + + headp = NULL; + x86_head = mac_head = ppc_head = NULL; + + /* If there are no boot disks, don't bother building boot information */ + if (TAILQ_EMPTY(&diskStructure->boot_images)) + return 0; + + /* Point to catalog: For now assume it consumes one sector */ + ELTORITO_DPRINTF(("Boot catalog will go in sector %d\n", first_sector)); + diskStructure->boot_catalog_sector = first_sector; + cd9660_bothendian_dword(first_sector, + diskStructure->boot_descriptor->boot_catalog_pointer); + + /* Step 1: Generate boot catalog */ + /* Step 1a: Validation entry */ + valid_entry = cd9660_boot_setup_validation_entry(ET_SYS_X86); + if (valid_entry == NULL) + return -1; + + /* + * Count how many boot images there are, + * and how many sectors they consume. + */ + num_entries = 1; + used_sectors = 0; + + TAILQ_FOREACH(tmp_disk, &diskStructure->boot_images, image_list) { + used_sectors += tmp_disk->num_sectors; + + /* One default entry per image */ + num_entries++; + } + catalog_sectors = howmany(num_entries * 0x20, diskStructure->sectorSize); + used_sectors += catalog_sectors; + + if (diskStructure->verbose_level > 0) { + printf("%s: there will be %i entries consuming %i sectors. " + "Catalog is %i sectors\n", __func__, num_entries, + used_sectors, catalog_sectors); + } + + /* Populate sector numbers */ + sector = first_sector + catalog_sectors; + TAILQ_FOREACH(tmp_disk, &diskStructure->boot_images, image_list) { + tmp_disk->sector = sector; + sector += tmp_disk->num_sectors; + } + + LIST_INSERT_HEAD(&diskStructure->boot_entries, valid_entry, ll_struct); + + /* Step 1b: Initial/default entry */ + /* TODO : PARAM */ + tmp_disk = TAILQ_FIRST(&diskStructure->boot_images); + default_entry = cd9660_boot_setup_default_entry(tmp_disk); + if (default_entry == NULL) { + warnx("Error: memory allocation failed in cd9660_setup_boot"); + return -1; + } + + LIST_INSERT_AFTER(valid_entry, default_entry, ll_struct); + + /* Todo: multiple default entries? */ + + tmp_disk = TAILQ_NEXT(tmp_disk, image_list); + + temp = default_entry; + + /* If multiple boot images are given : */ + while (tmp_disk != NULL) { + /* Step 2: Section header */ + switch (tmp_disk->system) { + case ET_SYS_X86: + headp = &x86_head; + break; + case ET_SYS_PPC: + headp = &ppc_head; + break; + case ET_SYS_MAC: + headp = &mac_head; + break; + default: + warnx("%s: internal error: unknown system type", + __func__); + return -1; + } + + if (*headp == NULL) { + head = + cd9660_boot_setup_section_head(tmp_disk->system); + if (head == NULL) { + warnx("Error: memory allocation failed in " + "cd9660_setup_boot"); + return -1; + } + LIST_INSERT_AFTER(default_entry, head, ll_struct); + *headp = head; + } else + head = *headp; + + head->entry_data.SH.num_section_entries[0]++; + + /* Step 2a: Section entry and extensions */ + temp = cd9660_boot_setup_section_entry(tmp_disk); + if (temp == NULL) { + warn("%s: cd9660_boot_setup_section_entry", __func__); + return -1; + } + + while ((next = LIST_NEXT(head, ll_struct)) != NULL && + next->entry_type == ET_ENTRY_SE) + head = next; + + LIST_INSERT_AFTER(head, temp, ll_struct); + tmp_disk = TAILQ_NEXT(tmp_disk, image_list); + } + + /* TODO: Remaining boot disks when implemented */ + + return first_sector + used_sectors; +} + +int +cd9660_setup_boot_volume_descriptor(iso9660_disk *diskStructure, + volume_descriptor *bvd) +{ + boot_volume_descriptor *bvdData = + (boot_volume_descriptor*)bvd->volumeDescriptorData; + + bvdData->boot_record_indicator[0] = ISO_VOLUME_DESCRIPTOR_BOOT; + memcpy(bvdData->identifier, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5); + bvdData->version[0] = 1; + memcpy(bvdData->boot_system_identifier, ET_ID, 23); + memcpy(bvdData->identifier, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5); + diskStructure->boot_descriptor = + (boot_volume_descriptor*) bvd->volumeDescriptorData; + return 1; +} + +static int +cd9660_write_mbr_partition_entry(FILE *fd, int idx, off_t sector_start, + off_t nsectors, int type) +{ + uint8_t val; + uint32_t lba; + + if (fseeko(fd, (off_t)(idx) * 16 + 0x1be, SEEK_SET) == -1) + err(1, "fseeko"); + + val = 0x80; /* Bootable */ + fwrite(&val, sizeof(val), 1, fd); + + val = 0xff; /* CHS begin */ + fwrite(&val, sizeof(val), 1, fd); + fwrite(&val, sizeof(val), 1, fd); + fwrite(&val, sizeof(val), 1, fd); + + val = type; /* Part type */ + fwrite(&val, sizeof(val), 1, fd); + + val = 0xff; /* CHS end */ + fwrite(&val, sizeof(val), 1, fd); + fwrite(&val, sizeof(val), 1, fd); + fwrite(&val, sizeof(val), 1, fd); + + /* LBA extent */ + lba = htole32(sector_start); + fwrite(&lba, sizeof(lba), 1, fd); + lba = htole32(nsectors); + fwrite(&lba, sizeof(lba), 1, fd); + + return 0; +} + +static int +cd9660_write_apm_partition_entry(FILE *fd, int idx, int total_partitions, + off_t sector_start, off_t nsectors, off_t sector_size, + const char *part_name, const char *part_type) +{ + uint32_t apm32, part_status; + uint16_t apm16; + + /* See Apple Tech Note 1189 for the details about the pmPartStatus + * flags. + * Below the flags which are default: + * - IsValid 0x01 + * - IsAllocated 0x02 + * - IsReadable 0x10 + * - IsWritable 0x20 + */ + part_status = APPLE_PS_VALID | APPLE_PS_ALLOCATED | APPLE_PS_READABLE | + APPLE_PS_WRITABLE; + + if (fseeko(fd, (off_t)(idx + 1) * sector_size, SEEK_SET) == -1) + err(1, "fseeko"); + + /* Signature */ + apm16 = htobe16(0x504d); + fwrite(&apm16, sizeof(apm16), 1, fd); + apm16 = 0; + fwrite(&apm16, sizeof(apm16), 1, fd); + + /* Total number of partitions */ + apm32 = htobe32(total_partitions); + fwrite(&apm32, sizeof(apm32), 1, fd); + /* Bounds */ + apm32 = htobe32(sector_start); + fwrite(&apm32, sizeof(apm32), 1, fd); + apm32 = htobe32(nsectors); + fwrite(&apm32, sizeof(apm32), 1, fd); + + fwrite(part_name, strlen(part_name) + 1, 1, fd); + fseek(fd, 32 - strlen(part_name) - 1, SEEK_CUR); + fwrite(part_type, strlen(part_type) + 1, 1, fd); + fseek(fd, 32 - strlen(part_type) - 1, SEEK_CUR); + + apm32 = 0; + /* pmLgDataStart */ + fwrite(&apm32, sizeof(apm32), 1, fd); + /* pmDataCnt */ + apm32 = htobe32(nsectors); + fwrite(&apm32, sizeof(apm32), 1, fd); + /* pmPartStatus */ + apm32 = htobe32(part_status); + fwrite(&apm32, sizeof(apm32), 1, fd); + + return 0; +} + +int +cd9660_write_boot(iso9660_disk *diskStructure, FILE *fd) +{ + struct boot_catalog_entry *e; + struct cd9660_boot_image *t; + int apm_partitions = 0; + int mbr_partitions = 0; + + /* write boot catalog */ + if (fseeko(fd, (off_t)diskStructure->boot_catalog_sector * + diskStructure->sectorSize, SEEK_SET) == -1) + err(1, "fseeko"); + + if (diskStructure->verbose_level > 0) { + printf("Writing boot catalog to sector %" PRId64 "\n", + diskStructure->boot_catalog_sector); + } + LIST_FOREACH(e, &diskStructure->boot_entries, ll_struct) { + if (diskStructure->verbose_level > 0) { + printf("Writing catalog entry of type %d\n", + e->entry_type); + } + /* + * It doesnt matter which one gets written + * since they are the same size + */ + fwrite(&(e->entry_data.VE), 1, 32, fd); + } + if (diskStructure->verbose_level > 0) + printf("Finished writing boot catalog\n"); + + /* copy boot images */ + TAILQ_FOREACH(t, &diskStructure->boot_images, image_list) { + if (diskStructure->verbose_level > 0) { + printf("Writing boot image from %s to sectors %d\n", + t->filename, t->sector); + } + cd9660_copy_file(diskStructure, fd, t->sector, t->filename); + + if (t->system == ET_SYS_MAC) + apm_partitions++; + if (t->system == ET_SYS_PPC) + mbr_partitions++; + } + + /* some systems need partition tables as well */ + if (mbr_partitions > 0 || diskStructure->chrp_boot) { + uint16_t sig; + + fseek(fd, 0x1fe, SEEK_SET); + sig = htole16(0xaa55); + fwrite(&sig, sizeof(sig), 1, fd); + + mbr_partitions = 0; + + /* Write ISO9660 descriptor, enclosing the whole disk */ + if (diskStructure->chrp_boot) + cd9660_write_mbr_partition_entry(fd, mbr_partitions++, + 0, diskStructure->totalSectors * + (diskStructure->sectorSize / 512), 0x96); + + /* Write all partition entries */ + TAILQ_FOREACH(t, &diskStructure->boot_images, image_list) { + if (t->system != ET_SYS_PPC) + continue; + cd9660_write_mbr_partition_entry(fd, mbr_partitions++, + t->sector * (diskStructure->sectorSize / 512), + t->num_sectors * (diskStructure->sectorSize / 512), + 0x41 /* PReP Boot */); + } + } + + if (apm_partitions > 0) { + /* Write DDR and global APM info */ + uint32_t apm32; + uint16_t apm16; + int total_parts; + + fseek(fd, 0, SEEK_SET); + apm16 = htobe16(0x4552); + fwrite(&apm16, sizeof(apm16), 1, fd); + /* Device block size */ + apm16 = htobe16(512); + fwrite(&apm16, sizeof(apm16), 1, fd); + /* Device block count */ + apm32 = htobe32(diskStructure->totalSectors * + (diskStructure->sectorSize / 512)); + fwrite(&apm32, sizeof(apm32), 1, fd); + /* Device type/id */ + apm16 = htobe16(1); + fwrite(&apm16, sizeof(apm16), 1, fd); + fwrite(&apm16, sizeof(apm16), 1, fd); + + /* Count total needed entries */ + total_parts = 2 + apm_partitions; /* Self + ISO9660 */ + + /* Write self-descriptor */ + cd9660_write_apm_partition_entry(fd, 0, total_parts, 1, + total_parts, 512, "Apple", "Apple_partition_map"); + + /* Write all partition entries */ + apm_partitions = 0; + TAILQ_FOREACH(t, &diskStructure->boot_images, image_list) { + if (t->system != ET_SYS_MAC) + continue; + + cd9660_write_apm_partition_entry(fd, + 1 + apm_partitions++, total_parts, + t->sector * (diskStructure->sectorSize / 512), + t->num_sectors * (diskStructure->sectorSize / 512), + 512, "CD Boot", "Apple_Bootstrap"); + } + + /* Write ISO9660 descriptor, enclosing the whole disk */ + cd9660_write_apm_partition_entry(fd, 2 + apm_partitions, + total_parts, 0, diskStructure->totalSectors * + (diskStructure->sectorSize / 512), 512, "ISO9660", + "CD_ROM_Mode_1"); + } + + return 0; +} diff --git a/usr.sbin/makefs/cd9660/cd9660_eltorito.h b/usr.sbin/makefs/cd9660/cd9660_eltorito.h new file mode 100644 index 000000000..266148395 --- /dev/null +++ b/usr.sbin/makefs/cd9660/cd9660_eltorito.h @@ -0,0 +1,162 @@ +/* $NetBSD: cd9660_eltorito.h,v 1.5 2009/07/04 14:31:38 ahoka Exp $ */ + +/* + * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan + * Perez-Rathke and Ram Vedam. All rights reserved. + * + * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys, + * Alan Perez-Rathke and Ram Vedam. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#ifndef _CD9660_ELTORITO_H_ +#define _CD9660_ELTORITO_H_ + +/* Boot defines */ +#define ET_ID "EL TORITO SPECIFICATION" +#define ET_SYS_X86 0 +#define ET_SYS_PPC 1 +#define ET_SYS_MAC 2 + +#define ET_BOOT_ENTRY_SIZE 0x20 + +#define ET_BOOTABLE 0x88 +#define ET_NOT_BOOTABLE 0 + +#define ET_MEDIA_NOEM 0 +#define ET_MEDIA_12FDD 1 +#define ET_MEDIA_144FDD 2 +#define ET_MEDIA_288FDD 3 +#define ET_MEDIA_HDD 4 + +#define ET_INDICATOR_HEADERMORE 0x90 +#define ET_INDICATOR_HEADERLAST 0x91 +#define ET_INDICATOR_EXTENSION 0x44 + +/*** Boot Structures ***/ + +typedef struct _boot_volume_descriptor { + u_char boot_record_indicator [ISODCL(0x00,0x00)]; + u_char identifier [ISODCL(0x01,0x05)]; + u_char version [ISODCL(0x06,0x06)]; + u_char boot_system_identifier [ISODCL(0x07,0x26)]; + u_char unused1 [ISODCL(0x27,0x46)]; + u_char boot_catalog_pointer [ISODCL(0x47,0x4A)]; + u_char unused2 [ISODCL(0x4B,0x7FF)]; +} boot_volume_descriptor; + +typedef struct _boot_catalog_validation_entry { + u_char header_id [ISODCL(0x00,0x00)]; + u_char platform_id [ISODCL(0x01,0x01)]; + u_char reserved1 [ISODCL(0x02,0x03)]; + u_char manufacturer [ISODCL(0x04,0x1B)]; + u_char checksum [ISODCL(0x1C,0x1D)]; + u_char key [ISODCL(0x1E,0x1F)]; +} boot_catalog_validation_entry; + +typedef struct _boot_catalog_initial_entry { + u_char boot_indicator [ISODCL(0x00,0x00)]; + u_char media_type [ISODCL(0x01,0x01)]; + u_char load_segment [ISODCL(0x02,0x03)]; + u_char system_type [ISODCL(0x04,0x04)]; + u_char unused_1 [ISODCL(0x05,0x05)]; + u_char sector_count [ISODCL(0x06,0x07)]; + u_char load_rba [ISODCL(0x08,0x0B)]; + u_char unused_2 [ISODCL(0x0C,0x1F)]; +} boot_catalog_initial_entry; + +#define ET_SECTION_HEADER_MORE 0x90 +#define ET_SECTION_HEADER_LAST 0x91 + +typedef struct _boot_catalog_section_header { + u_char header_indicator [ISODCL(0x00,0x00)]; + u_char platform_id [ISODCL(0x01,0x01)]; + u_char num_section_entries [ISODCL(0x02,0x03)]; + u_char id_string [ISODCL(0x04,0x1F)]; +} boot_catalog_section_header; + +typedef struct _boot_catalog_section_entry { + u_char boot_indicator [ISODCL(0x00,0x00)]; + u_char media_type [ISODCL(0x01,0x01)]; + u_char load_segment [ISODCL(0x02,0x03)]; + u_char system_type [ISODCL(0x04,0x04)]; + u_char unused_1 [ISODCL(0x05,0x05)]; + u_char sector_count [ISODCL(0x06,0x07)]; + u_char load_rba [ISODCL(0x08,0x0B)]; + u_char selection_criteria [ISODCL(0x0C,0x0C)]; + u_char vendor_criteria [ISODCL(0x0D,0x1F)]; +} boot_catalog_section_entry; + +typedef struct _boot_catalog_section_entry_extension { + u_char extension_indicator [ISODCL(0x00,0x00)]; + u_char flags [ISODCL(0x01,0x01)]; + u_char vendor_criteria [ISODCL(0x02,0x1F)]; +} boot_catalog_section_entry_extension; + +#define ET_ENTRY_VE 1 +#define ET_ENTRY_IE 2 +#define ET_ENTRY_SH 3 +#define ET_ENTRY_SE 4 +#define ET_ENTRY_EX 5 + +struct boot_catalog_entry { + char entry_type; + union { + boot_catalog_validation_entry VE; + boot_catalog_initial_entry IE; + boot_catalog_section_header SH; + boot_catalog_section_entry SE; + boot_catalog_section_entry_extension EX; + } entry_data; + + LIST_ENTRY(boot_catalog_entry) ll_struct; +}; + +/* Temporary structure */ +struct cd9660_boot_image { + char *filename; + int size; + int sector; /* copied to LoadRBA */ + int num_sectors; + unsigned int loadSegment; + u_char targetMode; + u_char system; + u_char bootable; + /* + * If the boot image exists in the filesystem + * already, this is a pointer to that node. For the sake + * of simplicity in future versions, this pointer is only + * to the node in the primary volume. This SHOULD be done + * via a hashtable lookup. + */ + struct _cd9660node *boot_image_node; + TAILQ_ENTRY(cd9660_boot_image) image_list; + int serialno; +}; + + +#endif /* _CD9660_ELTORITO_H_ */ + diff --git a/usr.sbin/makefs/cd9660/cd9660_strings.c b/usr.sbin/makefs/cd9660/cd9660_strings.c new file mode 100644 index 000000000..fa822746d --- /dev/null +++ b/usr.sbin/makefs/cd9660/cd9660_strings.c @@ -0,0 +1,127 @@ +/* $NetBSD: cd9660_strings.c,v 1.5 2011/03/23 13:11:51 christos Exp $ */ + +/* + * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan + * Perez-Rathke and Ram Vedam. All rights reserved. + * + * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys, + * Alan Perez-Rathke and Ram Vedam. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#else +#include +#endif + +#include +#include +#include + +#include "makefs.h" +#include "cd9660.h" + +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: cd9660_strings.c,v 1.5 2011/03/23 13:11:51 christos Exp $"); +#endif /* !__lint */ + + +void +cd9660_uppercase_characters(char *str, int len) +{ + int p; + + for (p = 0; p < len; p++) { + if (islower((unsigned char)str[p]) ) + str[p] -= 32; + } +} + +static inline int +cd9660_is_d_char(char c) +{ + return (isupper((unsigned char)c) + || c == '_' + || (c >= '0' && c <= '9')); +} + +static inline int +cd9660_is_a_char(char c) +{ + return (isupper((unsigned char)c) + || c == '_' + || (c >= '%' && c <= '?') + || (c >= ' ' && c <= '\"')); +} + +/* + * Test a string to see if it is composed of valid a characters + * @param const char* The string to test + * @returns int 1 if valid, 2 if valid if characters are converted to + * upper case, 0 otherwise + */ +int +cd9660_valid_a_chars(const char *str) +{ + const char *c = str; + int upperFound = 0; + + while ((*c) != '\0') { + if (!(cd9660_is_a_char(*c))) { + if (islower((unsigned char)*c) ) + upperFound = 1; + else + return 0; + } + c++; + } + return upperFound + 1; +} + +/* + * Test a string to see if it is composed of valid d characters + * @param const char* The string to test + * @returns int 1 if valid, 2 if valid if characters are converted to + * upper case, 0 otherwise + */ +int +cd9660_valid_d_chars(const char *str) +{ + const char *c=str; + int upperFound = 0; + + while ((*c) != '\0') { + if (!(cd9660_is_d_char(*c))) { + if (islower((unsigned char)*c) ) + upperFound = 1; + else + return 0; + } + c++; + } + return upperFound + 1; +} diff --git a/usr.sbin/makefs/cd9660/cd9660_write.c b/usr.sbin/makefs/cd9660/cd9660_write.c new file mode 100644 index 000000000..edb4ba4d1 --- /dev/null +++ b/usr.sbin/makefs/cd9660/cd9660_write.c @@ -0,0 +1,509 @@ +/* $NetBSD: cd9660_write.c,v 1.17 2013/10/19 17:16:37 christos Exp $ */ + +/* + * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan + * Perez-Rathke and Ram Vedam. All rights reserved. + * + * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys, + * Alan Perez-Rathke and Ram Vedam. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#include "cd9660.h" +#include "iso9660_rrip.h" + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: cd9660_write.c,v 1.17 2013/10/19 17:16:37 christos Exp $"); +#endif /* !__lint */ + +#include + +static int cd9660_write_volume_descriptors(iso9660_disk *, FILE *); +static int cd9660_write_path_table(iso9660_disk *, FILE *, off_t, int); +static int cd9660_write_path_tables(iso9660_disk *, FILE *); +static int cd9660_write_file(iso9660_disk *, FILE *, cd9660node *); +static int cd9660_write_filedata(iso9660_disk *, FILE *, off_t, + const unsigned char *, int); +#if 0 +static int cd9660_write_buffered(FILE *, off_t, int, const unsigned char *); +#endif +static void cd9660_write_rr(iso9660_disk *, FILE *, cd9660node *, off_t, off_t); + +/* + * Write the image + * Writes the entire image + * @param const char* The filename for the image + * @returns int 1 on success, 0 on failure + */ +int +cd9660_write_image(iso9660_disk *diskStructure, const char* image) +{ + FILE *fd; + int status; + char buf[CD9660_SECTOR_SIZE]; + + if ((fd = fopen(image, "w+")) == NULL) { + err(EXIT_FAILURE, "%s: Can't open `%s' for writing", __func__, + image); + } + + if (diskStructure->verbose_level > 0) + printf("Writing image\n"); + + if (diskStructure->has_generic_bootimage) { + status = cd9660_copy_file(diskStructure, fd, 0, + diskStructure->generic_bootimage); + if (status == 0) { + warnx("%s: Error writing generic boot image", + __func__); + goto cleanup_bad_image; + } + } + + /* Write the volume descriptors */ + status = cd9660_write_volume_descriptors(diskStructure, fd); + if (status == 0) { + warnx("%s: Error writing volume descriptors to image", + __func__); + goto cleanup_bad_image; + } + + if (diskStructure->verbose_level > 0) + printf("Volume descriptors written\n"); + + /* + * Write the path tables: there are actually four, but right + * now we are only concearned with two. + */ + status = cd9660_write_path_tables(diskStructure, fd); + if (status == 0) { + warnx("%s: Error writing path tables to image", __func__); + goto cleanup_bad_image; + } + + if (diskStructure->verbose_level > 0) + printf("Path tables written\n"); + + /* Write the directories and files */ + status = cd9660_write_file(diskStructure, fd, diskStructure->rootNode); + if (status == 0) { + warnx("%s: Error writing files to image", __func__); + goto cleanup_bad_image; + } + + if (diskStructure->is_bootable) { + cd9660_write_boot(diskStructure, fd); + } + + /* Write padding bits. This is temporary */ + memset(buf, 0, CD9660_SECTOR_SIZE); + cd9660_write_filedata(diskStructure, fd, + diskStructure->totalSectors - 1, buf, 1); + + if (diskStructure->verbose_level > 0) + printf("Files written\n"); + fclose(fd); + + if (diskStructure->verbose_level > 0) + printf("Image closed\n"); + return 1; + +cleanup_bad_image: + fclose(fd); + if (!diskStructure->keep_bad_images) + unlink(image); + if (diskStructure->verbose_level > 0) + printf("Bad image cleaned up\n"); + return 0; +} + +static int +cd9660_write_volume_descriptors(iso9660_disk *diskStructure, FILE *fd) +{ + volume_descriptor *vd_temp = diskStructure->firstVolumeDescriptor; + while (vd_temp != NULL) { + cd9660_write_filedata(diskStructure, fd, vd_temp->sector, + vd_temp->volumeDescriptorData, 1); + vd_temp = vd_temp->next; + } + return 1; +} + +/* + * Write out an individual path table + * Used just to keep redundant code to a minimum + * @param FILE *fd Valid file pointer + * @param int Sector to start writing path table to + * @param int Endian mode : BIG_ENDIAN or LITTLE_ENDIAN + * @returns int 1 on success, 0 on failure + */ +static int +cd9660_write_path_table(iso9660_disk *diskStructure, FILE *fd, off_t sector, + int mode) +{ + int path_table_sectors = CD9660_BLOCKS(diskStructure->sectorSize, + diskStructure->pathTableLength); + unsigned char *buffer; + unsigned char *buffer_head; + int len; + path_table_entry temp_entry; + cd9660node *ptcur; + + buffer = ecalloc(path_table_sectors, diskStructure->sectorSize); + buffer_head = buffer; + + ptcur = diskStructure->rootNode; + + while (ptcur != NULL) { + memset(&temp_entry, 0, sizeof(path_table_entry)); + temp_entry.length[0] = ptcur->isoDirRecord->name_len[0]; + temp_entry.extended_attribute_length[0] = + ptcur->isoDirRecord->ext_attr_length[0]; + memcpy(temp_entry.name, ptcur->isoDirRecord->name, + temp_entry.length[0] + 1); + + /* round up */ + len = temp_entry.length[0] + 8 + (temp_entry.length[0] & 0x01); + + /* todo: function pointers instead */ + if (mode == LITTLE_ENDIAN) { + cd9660_731(ptcur->fileDataSector, + temp_entry.first_sector); + cd9660_721((ptcur->parent == NULL ? + 1 : ptcur->parent->ptnumber), + temp_entry.parent_number); + } else { + cd9660_732(ptcur->fileDataSector, + temp_entry.first_sector); + cd9660_722((ptcur->parent == NULL ? + 1 : ptcur->parent->ptnumber), + temp_entry.parent_number); + } + + + memcpy(buffer, &temp_entry, len); + buffer += len; + + ptcur = ptcur->ptnext; + } + + return cd9660_write_filedata(diskStructure, fd, sector, buffer_head, + path_table_sectors); +} + + +/* + * Write out the path tables to disk + * Each file descriptor should be pointed to by the PVD, so we know which + * sector to copy them to. One thing to watch out for: the only path tables + * stored are in the endian mode that the application is compiled for. So, + * the first thing to do is write out that path table, then to write the one + * in the other endian mode requires to convert the endianness of each entry + * in the table. The best way to do this would be to create a temporary + * path_table_entry structure, then for each path table entry, copy it to + * the temporary entry, translate, then copy that to disk. + * + * @param FILE* Valid file descriptor + * @returns int 0 on failure, 1 on success + */ +static int +cd9660_write_path_tables(iso9660_disk *diskStructure, FILE *fd) +{ + if (cd9660_write_path_table(diskStructure, fd, + diskStructure->primaryLittleEndianTableSector, LITTLE_ENDIAN) == 0) + return 0; + + if (cd9660_write_path_table(diskStructure, fd, + diskStructure->primaryBigEndianTableSector, BIG_ENDIAN) == 0) + return 0; + + /* @TODO: handle remaining two path tables */ + return 1; +} + +/* + * Write a file to disk + * Writes a file, its directory record, and its data to disk + * This file is designed to be called RECURSIVELY, so initially call it + * with the root node. All of the records should store what sector the + * file goes in, so no computation should be necessary. + * + * @param int fd Valid file descriptor + * @param struct cd9660node* writenode Pointer to the file to be written + * @returns int 0 on failure, 1 on success + */ +static int +cd9660_write_file(iso9660_disk *diskStructure, FILE *fd, cd9660node *writenode) +{ + char *buf; + char *temp_file_name; + int ret; + off_t working_sector; + int cur_sector_offset; + iso_directory_record_cd9660 temp_record; + cd9660node *temp; + int rv = 0; + + /* Todo : clean up variables */ + + temp_file_name = ecalloc(CD9660MAXPATH + 1, 1); + buf = emalloc(diskStructure->sectorSize); + if ((writenode->level != 0) && + !(writenode->node->type & S_IFDIR)) { + fsinode *inode = writenode->node->inode; + /* Only attempt to write unwritten files that have length. */ + if ((inode->flags & FI_WRITTEN) != 0) { + INODE_WARNX(("%s: skipping written inode %d", __func__, + (int)inode->st.st_ino)); + } else if (writenode->fileDataLength > 0) { + INODE_WARNX(("%s: writing inode %d blocks at %" PRIu32, + __func__, (int)inode->st.st_ino, inode->ino)); + inode->flags |= FI_WRITTEN; + cd9660_compute_full_filename(writenode, + temp_file_name); + ret = cd9660_copy_file(diskStructure, fd, + writenode->fileDataSector, temp_file_name); + if (ret == 0) + goto out; + } + } else { + /* + * Here is a new revelation that ECMA didnt explain + * (at least not well). + * ALL . and .. records store the name "\0" and "\1" + * resepctively. So, for each directory, we have to + * make a new node. + * + * This is where it gets kinda messy, since we have to + * be careful of sector boundaries + */ + cur_sector_offset = 0; + working_sector = writenode->fileDataSector; + if (fseeko(fd, working_sector * diskStructure->sectorSize, + SEEK_SET) == -1) + err(1, "fseeko"); + + /* + * Now loop over children, writing out their directory + * records - beware of sector boundaries + */ + TAILQ_FOREACH(temp, &writenode->cn_children, cn_next_child) { + /* + * Copy the temporary record and adjust its size + * if necessary + */ + memcpy(&temp_record, temp->isoDirRecord, + sizeof(iso_directory_record_cd9660)); + + temp_record.length[0] = + cd9660_compute_record_size(diskStructure, temp); + + if (temp_record.length[0] + cur_sector_offset >= + diskStructure->sectorSize) { + cur_sector_offset = 0; + working_sector++; + + /* Seek to the next sector. */ + if (fseeko(fd, working_sector * + diskStructure->sectorSize, SEEK_SET) == -1) + err(1, "fseeko"); + } + /* Write out the basic ISO directory record */ + (void)fwrite(&temp_record, 1, + temp->isoDirRecord->length[0], fd); + if (diskStructure->rock_ridge_enabled) { + cd9660_write_rr(diskStructure, fd, temp, + cur_sector_offset, working_sector); + } + if (fseeko(fd, working_sector * + diskStructure->sectorSize + cur_sector_offset + + temp_record.length[0] - temp->su_tail_size, + SEEK_SET) == -1) + err(1, "fseeko"); + if (temp->su_tail_size > 0) + fwrite(temp->su_tail_data, 1, + temp->su_tail_size, fd); + if (ferror(fd)) { + warnx("%s: write error", __func__); + goto out; + } + cur_sector_offset += temp_record.length[0]; + + } + + /* + * Recurse on children. + */ + TAILQ_FOREACH(temp, &writenode->cn_children, cn_next_child) { + if ((ret = cd9660_write_file(diskStructure, fd, temp)) + == 0) + goto out; + } + } + rv = 1; +out: + free(temp_file_name); + free(buf); + return rv; +} + +/* + * Wrapper function to write a buffer (one sector) to disk. + * Seeks and writes the buffer. + * NOTE: You dont NEED to use this function, but it might make your + * life easier if you have to write things that align to a sector + * (such as volume descriptors). + * + * @param int fd Valid file descriptor + * @param int sector Sector number to write to + * @param const unsigned char* Buffer to write. This should be the + * size of a sector, and if only a portion + * is written, the rest should be set to 0. + */ +static int +cd9660_write_filedata(iso9660_disk *diskStructure, FILE *fd, off_t sector, + const unsigned char *buf, int numsecs) +{ + off_t curpos; + size_t success; + + curpos = ftello(fd); + + if (fseeko(fd, sector * diskStructure->sectorSize, SEEK_SET) == -1) + err(1, "fseeko"); + + success = fwrite(buf, diskStructure->sectorSize * numsecs, 1, fd); + + if (fseeko(fd, curpos, SEEK_SET) == -1) + err(1, "fseeko"); + + if (success == 1) + success = diskStructure->sectorSize * numsecs; + return success; +} + +#if 0 +static int +cd9660_write_buffered(FILE *fd, off_t offset, int buff_len, + const unsigned char* buffer) +{ + static int working_sector = -1; + static char buf[CD9660_SECTOR_SIZE]; + + return 0; +} +#endif + +int +cd9660_copy_file(iso9660_disk *diskStructure, FILE *fd, off_t start_sector, + const char *filename) +{ + FILE *rf; + int bytes_read; + off_t sector = start_sector; + int buf_size = diskStructure->sectorSize; + char *buf; + + buf = emalloc(buf_size); + if ((rf = fopen(filename, "rb")) == NULL) { + warn("%s: cannot open %s", __func__, filename); + free(buf); + return 0; + } + + if (diskStructure->verbose_level > 1) + printf("Writing file: %s\n",filename); + + if (fseeko(fd, start_sector * diskStructure->sectorSize, SEEK_SET) == -1) + err(1, "fseeko"); + + while (!feof(rf)) { + bytes_read = fread(buf,1,buf_size,rf); + if (ferror(rf)) { + warn("%s: fread", __func__); + free(buf); + (void)fclose(rf); + return 0; + } + + fwrite(buf,1,bytes_read,fd); + if (ferror(fd)) { + warn("%s: fwrite", __func__); + free(buf); + (void)fclose(rf); + return 0; + } + sector++; + } + + fclose(rf); + free(buf); + return 1; +} + +static void +cd9660_write_rr(iso9660_disk *diskStructure, FILE *fd, cd9660node *writenode, + off_t offset, off_t sector) +{ + int in_ca = 0; + struct ISO_SUSP_ATTRIBUTES *myattr; + + offset += writenode->isoDirRecord->length[0]; + if (fseeko(fd, sector * diskStructure->sectorSize + offset, SEEK_SET) == + -1) + err(1, "fseeko"); + /* Offset now points at the end of the record */ + TAILQ_FOREACH(myattr, &writenode->head, rr_ll) { + fwrite(&(myattr->attr), CD9660_SUSP_ENTRY_SIZE(myattr), 1, fd); + + if (!in_ca) { + offset += CD9660_SUSP_ENTRY_SIZE(myattr); + if (myattr->last_in_suf) { + /* + * Point the offset to the start of this + * record's CE area + */ + if (fseeko(fd, ((off_t)diskStructure-> + susp_continuation_area_start_sector * + diskStructure->sectorSize) + + writenode->susp_entry_ce_start, + SEEK_SET) == -1) + err(1, "fseeko"); + in_ca = 1; + } + } + } + + /* + * If we had to go to the continuation area, head back to + * where we should be. + */ + if (in_ca) + if (fseeko(fd, sector * diskStructure->sectorSize + offset, + SEEK_SET) == -1) + err(1, "fseeko"); +} diff --git a/usr.sbin/makefs/cd9660/iso9660_rrip.c b/usr.sbin/makefs/cd9660/iso9660_rrip.c new file mode 100644 index 000000000..9189be049 --- /dev/null +++ b/usr.sbin/makefs/cd9660/iso9660_rrip.c @@ -0,0 +1,838 @@ +/* $NetBSD: iso9660_rrip.c,v 1.13 2013/07/30 16:02:23 reinoud Exp $ */ + +/* + * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan + * Perez-Rathke and Ram Vedam. All rights reserved. + * + * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys, + * Alan Perez-Rathke and Ram Vedam. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ +/* This will hold all the function definitions + * defined in iso9660_rrip.h + */ + +#include "makefs.h" +#include "cd9660.h" +#include "iso9660_rrip.h" +#include +#include +#include + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: iso9660_rrip.c,v 1.13 2013/07/30 16:02:23 reinoud Exp $"); +#endif /* !__lint */ + +static void cd9660_rrip_initialize_inode(cd9660node *); +static int cd9660_susp_handle_continuation(iso9660_disk *, cd9660node *); +static int cd9660_susp_handle_continuation_common(iso9660_disk *, cd9660node *, + int); + +int +cd9660_susp_initialize(iso9660_disk *diskStructure, cd9660node *node, + cd9660node *parent, cd9660node *grandparent) +{ + cd9660node *cn; + int r; + + /* Make sure the node is not NULL. If it is, there are major problems */ + assert(node != NULL); + + if (!(node->type & CD9660_TYPE_DOT) && + !(node->type & CD9660_TYPE_DOTDOT)) + TAILQ_INIT(&(node->head)); + if (node->dot_record != 0) + TAILQ_INIT(&(node->dot_record->head)); + if (node->dot_dot_record != 0) + TAILQ_INIT(&(node->dot_dot_record->head)); + + /* SUSP specific entries here */ + if ((r = cd9660_susp_initialize_node(diskStructure, node)) < 0) + return r; + + /* currently called cd9660node_rrip_init_links */ + r = cd9660_rrip_initialize_node(diskStructure, node, parent, grandparent); + if (r < 0) + return r; + + /* + * See if we need a CE record, and set all of the + * associated counters. + * + * This should be called after all extensions. After + * this is called, no new records should be added. + */ + if ((r = cd9660_susp_handle_continuation(diskStructure, node)) < 0) + return r; + + /* Recurse on children. */ + TAILQ_FOREACH(cn, &node->cn_children, cn_next_child) { + if ((r = cd9660_susp_initialize(diskStructure, cn, node, parent)) < 0) + return 0; + } + return 1; +} + +int +cd9660_susp_finalize(iso9660_disk *diskStructure, cd9660node *node) +{ + cd9660node *temp; + int r; + + assert(node != NULL); + + if (node == diskStructure->rootNode) + diskStructure->susp_continuation_area_current_free = 0; + + if ((r = cd9660_susp_finalize_node(diskStructure, node)) < 0) + return r; + if ((r = cd9660_rrip_finalize_node(diskStructure, node)) < 0) + return r; + + TAILQ_FOREACH(temp, &node->cn_children, cn_next_child) { + if ((r = cd9660_susp_finalize(diskStructure, temp)) < 0) + return r; + } + return 1; +} + +/* + * If we really wanted to speed things up, we could have some sort of + * lookup table on the SUSP entry type that calls a functor. Or, we could + * combine the functions. These functions are kept separate to allow + * easier addition of other extensions. + + * For the sake of simplicity and clarity, we won't be doing that for now. + */ + +/* + * SUSP needs to update the following types: + * CE (continuation area) + */ +int +cd9660_susp_finalize_node(iso9660_disk *diskStructure, cd9660node *node) +{ + struct ISO_SUSP_ATTRIBUTES *t; + + /* Handle CE counters */ + if (node->susp_entry_ce_length > 0) { + node->susp_entry_ce_start = + diskStructure->susp_continuation_area_current_free; + diskStructure->susp_continuation_area_current_free += + node->susp_entry_ce_length; + } + + TAILQ_FOREACH(t, &node->head, rr_ll) { + if (t->susp_type != SUSP_TYPE_SUSP || + t->entry_type != SUSP_ENTRY_SUSP_CE) + continue; + cd9660_bothendian_dword( + diskStructure-> + susp_continuation_area_start_sector, + t->attr.su_entry.CE.ca_sector); + + cd9660_bothendian_dword( + diskStructure-> + susp_continuation_area_start_sector, + t->attr.su_entry.CE.ca_sector); + cd9660_bothendian_dword(node->susp_entry_ce_start, + t->attr.su_entry.CE.offset); + cd9660_bothendian_dword(node->susp_entry_ce_length, + t->attr.su_entry.CE.length); + } + return 0; +} + +int +cd9660_rrip_finalize_node(iso9660_disk *diskStructure __unused, + cd9660node *node) +{ + struct ISO_SUSP_ATTRIBUTES *t; + + TAILQ_FOREACH(t, &node->head, rr_ll) { + if (t->susp_type != SUSP_TYPE_RRIP) + continue; + switch (t->entry_type) { + case SUSP_ENTRY_RRIP_CL: + /* Look at rr_relocated*/ + if (node->rr_relocated == NULL) + return -1; + cd9660_bothendian_dword( + node->rr_relocated->fileDataSector, + (unsigned char *) + t->attr.rr_entry.CL.dir_loc); + break; + case SUSP_ENTRY_RRIP_PL: + /* Look at rr_real_parent */ + if (node->parent == NULL || + node->parent->rr_real_parent == NULL) + return -1; + cd9660_bothendian_dword( + node->parent->rr_real_parent->fileDataSector, + (unsigned char *) + t->attr.rr_entry.PL.dir_loc); + break; + } + } + return 0; +} + +static int +cd9660_susp_handle_continuation_common(iso9660_disk *diskStructure, + cd9660node *node, int space) +{ + int ca_used, susp_used, susp_used_pre_ce, working; + struct ISO_SUSP_ATTRIBUTES *temp, *pre_ce, *last, *CE, *ST; + + pre_ce = last = NULL; + working = 254 - space; + if (node->su_tail_size > 0) + /* Allow 4 bytes for "ST" record. */ + working -= node->su_tail_size + 4; + /* printf("There are %i bytes to work with\n",working); */ + + susp_used_pre_ce = susp_used = 0; + ca_used = 0; + TAILQ_FOREACH(temp, &node->head, rr_ll) { + if (working < 0) + break; + /* + * printf("SUSP Entry found, length is %i\n", + * CD9660_SUSP_ENTRY_SIZE(temp)); + */ + working -= CD9660_SUSP_ENTRY_SIZE(temp); + if (working >= 0) { + last = temp; + susp_used += CD9660_SUSP_ENTRY_SIZE(temp); + } + if (working >= 28) { + /* + * Remember the last entry after which we + * could insert a "CE" entry. + */ + pre_ce = last; + susp_used_pre_ce = susp_used; + } + } + + /* A CE entry is needed */ + if (working <= 0) { + CE = cd9660node_susp_create_node(SUSP_TYPE_SUSP, + SUSP_ENTRY_SUSP_CE, "CE", SUSP_LOC_ENTRY); + cd9660_susp_ce(CE, node); + /* This will automatically insert at the appropriate location */ + if (pre_ce != NULL) + TAILQ_INSERT_AFTER(&node->head, pre_ce, CE, rr_ll); + else + TAILQ_INSERT_HEAD(&node->head, CE, rr_ll); + last = CE; + susp_used = susp_used_pre_ce + 28; + /* Count how much CA data is necessary */ + for (temp = TAILQ_NEXT(last, rr_ll); temp != NULL; + temp = TAILQ_NEXT(temp, rr_ll)) { + ca_used += CD9660_SUSP_ENTRY_SIZE(temp); + } + } + + /* An ST entry is needed */ + if (node->su_tail_size > 0) { + ST = cd9660node_susp_create_node(SUSP_TYPE_SUSP, + SUSP_ENTRY_SUSP_ST, "ST", SUSP_LOC_ENTRY); + cd9660_susp_st(ST, node); + if (last != NULL) + TAILQ_INSERT_AFTER(&node->head, last, ST, rr_ll); + else + TAILQ_INSERT_HEAD(&node->head, ST, rr_ll); + last = ST; + susp_used += 4; + } + if (last != NULL) + last->last_in_suf = 1; + + node->susp_entry_size = susp_used; + node->susp_entry_ce_length = ca_used; + + diskStructure->susp_continuation_area_size += ca_used; + return 1; +} + +/* See if a continuation entry is needed for each of the different types */ +static int +cd9660_susp_handle_continuation(iso9660_disk *diskStructure, cd9660node *node) +{ + assert (node != NULL); + + /* Entry */ + if (cd9660_susp_handle_continuation_common(diskStructure, + node,(int)(node->isoDirRecord->length[0])) < 0) + return 0; + + return 1; +} + +int +cd9660_susp_initialize_node(iso9660_disk *diskStructure, cd9660node *node) +{ + struct ISO_SUSP_ATTRIBUTES *temp; + + /* + * Requirements/notes: + * CE: is added for us where needed + * ST: not sure if it is even required, but if so, should be + * handled by the CE code + * PD: isnt needed (though might be added for testing) + * SP: is stored ONLY on the . record of the root directory + * ES: not sure + */ + + /* Check for root directory, add SP and ER if needed. */ + if (node->type & CD9660_TYPE_DOT) { + if (node->parent == diskStructure->rootNode) { + temp = cd9660node_susp_create_node(SUSP_TYPE_SUSP, + SUSP_ENTRY_SUSP_SP, "SP", SUSP_LOC_DOT); + cd9660_susp_sp(temp, node); + + /* Should be first entry. */ + TAILQ_INSERT_HEAD(&node->head, temp, rr_ll); + } + } + return 1; +} + +static void +cd9660_rrip_initialize_inode(cd9660node *node) +{ + struct ISO_SUSP_ATTRIBUTES *attr; + + /* + * Inode dependent values - this may change, + * but for now virtual files and directories do + * not have an inode structure + */ + + if ((node->node != NULL) && (node->node->inode != NULL)) { + /* PX - POSIX attributes */ + attr = cd9660node_susp_create_node(SUSP_TYPE_RRIP, + SUSP_ENTRY_RRIP_PX, "PX", SUSP_LOC_ENTRY); + cd9660node_rrip_px(attr, node->node); + + TAILQ_INSERT_TAIL(&node->head, attr, rr_ll); + + /* TF - timestamp */ + attr = cd9660node_susp_create_node(SUSP_TYPE_RRIP, + SUSP_ENTRY_RRIP_TF, "TF", SUSP_LOC_ENTRY); + cd9660node_rrip_tf(attr, node->node); + TAILQ_INSERT_TAIL(&node->head, attr, rr_ll); + + /* SL - Symbolic link */ + /* ?????????? Dan - why is this here? */ + if (TAILQ_EMPTY(&node->cn_children) && + node->node->inode != NULL && + S_ISLNK(node->node->inode->st.st_mode)) + cd9660_createSL(node); + + /* PN - device number */ + if (node->node->inode != NULL && + ((S_ISCHR(node->node->inode->st.st_mode) || + S_ISBLK(node->node->inode->st.st_mode)))) { + attr = + cd9660node_susp_create_node(SUSP_TYPE_RRIP, + SUSP_ENTRY_RRIP_PN, "PN", + SUSP_LOC_ENTRY); + cd9660node_rrip_pn(attr, node->node); + TAILQ_INSERT_TAIL(&node->head, attr, rr_ll); + } + } +} + +int +cd9660_rrip_initialize_node(iso9660_disk *diskStructure, cd9660node *node, + cd9660node *parent, cd9660node *grandparent) +{ + struct ISO_SUSP_ATTRIBUTES *current = NULL; + + assert(node != NULL); + + if (node->type & CD9660_TYPE_DOT) { + /* + * Handle ER - should be the only entry to appear on + * a "." record + */ + if (node->parent == diskStructure->rootNode) { + cd9660_susp_ER(node, 1, SUSP_RRIP_ER_EXT_ID, + SUSP_RRIP_ER_EXT_DES, SUSP_RRIP_ER_EXT_SRC); + } + if (parent != NULL && parent->node != NULL && + parent->node->inode != NULL) { + /* PX - POSIX attributes */ + current = cd9660node_susp_create_node(SUSP_TYPE_RRIP, + SUSP_ENTRY_RRIP_PX, "PX", SUSP_LOC_ENTRY); + cd9660node_rrip_px(current, parent->node); + TAILQ_INSERT_TAIL(&node->head, current, rr_ll); + } + } else if (node->type & CD9660_TYPE_DOTDOT) { + if (grandparent != NULL && grandparent->node != NULL && + grandparent->node->inode != NULL) { + /* PX - POSIX attributes */ + current = cd9660node_susp_create_node(SUSP_TYPE_RRIP, + SUSP_ENTRY_RRIP_PX, "PX", SUSP_LOC_ENTRY); + cd9660node_rrip_px(current, grandparent->node); + TAILQ_INSERT_TAIL(&node->head, current, rr_ll); + } + /* Handle PL */ + if (parent != NULL && parent->rr_real_parent != NULL) { + current = cd9660node_susp_create_node(SUSP_TYPE_RRIP, + SUSP_ENTRY_RRIP_PL, "PL", SUSP_LOC_DOTDOT); + cd9660_rrip_PL(current,node); + TAILQ_INSERT_TAIL(&node->head, current, rr_ll); + } + } else { + cd9660_rrip_initialize_inode(node); + + /* + * Not every node needs a NM set - only if the name is + * actually different. IE: If a file is TEST -> TEST, + * no NM. test -> TEST, need a NM + * + * The rr_moved_dir needs to be assigned a NM record as well. + */ + if (node == diskStructure->rr_moved_dir) { + cd9660_rrip_add_NM(node, RRIP_DEFAULT_MOVE_DIR_NAME); + } + else if ((node->node != NULL) && + ((strlen(node->node->name) != + (uint8_t)node->isoDirRecord->name_len[0]) || + (memcmp(node->node->name,node->isoDirRecord->name, + (uint8_t)node->isoDirRecord->name_len[0]) != 0))) { + cd9660_rrip_NM(node); + } + + + + /* Rock ridge directory relocation code here. */ + + /* First handle the CL for the placeholder file. */ + if (node->rr_relocated != NULL) { + current = cd9660node_susp_create_node(SUSP_TYPE_RRIP, + SUSP_ENTRY_RRIP_CL, "CL", SUSP_LOC_ENTRY); + cd9660_rrip_CL(current, node); + TAILQ_INSERT_TAIL(&node->head, current, rr_ll); + } + + /* Handle RE*/ + if (node->rr_real_parent != NULL) { + current = cd9660node_susp_create_node(SUSP_TYPE_RRIP, + SUSP_ENTRY_RRIP_RE, "RE", SUSP_LOC_ENTRY); + cd9660_rrip_RE(current,node); + TAILQ_INSERT_TAIL(&node->head, current, rr_ll); + } + } + return 1; +} + +struct ISO_SUSP_ATTRIBUTES* +cd9660node_susp_create_node(int susp_type, int entry_type, const char *type_id, + int write_loc) +{ + struct ISO_SUSP_ATTRIBUTES* temp; + + temp = emalloc(sizeof(*temp)); + temp->susp_type = susp_type; + temp->entry_type = entry_type; + temp->last_in_suf = 0; + /* Phase this out */ + temp->type_of[0] = type_id[0]; + temp->type_of[1] = type_id[1]; + temp->write_location = write_loc; + + /* + * Since the first four bytes is common, lets go ahead and + * set the type identifier, since we are passing that to this + * function anyhow. + */ + temp->attr.su_entry.SP.h.type[0] = type_id[0]; + temp->attr.su_entry.SP.h.type[1] = type_id[1]; + return temp; +} + +int +cd9660_rrip_PL(struct ISO_SUSP_ATTRIBUTES* p, cd9660node *node __unused) +{ + p->attr.rr_entry.PL.h.length[0] = 12; + p->attr.rr_entry.PL.h.version[0] = 1; + return 1; +} + +int +cd9660_rrip_CL(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *node __unused) +{ + p->attr.rr_entry.CL.h.length[0] = 12; + p->attr.rr_entry.CL.h.version[0] = 1; + return 1; +} + +int +cd9660_rrip_RE(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *node __unused) +{ + p->attr.rr_entry.RE.h.length[0] = 4; + p->attr.rr_entry.RE.h.version[0] = 1; + return 1; +} + +void +cd9660_createSL(cd9660node *node) +{ + struct ISO_SUSP_ATTRIBUTES* current; + int path_count, dir_count, done, i, j, dir_copied; + char temp_cr[255]; + char temp_sl[255]; /* used in copying continuation entry*/ + char* sl_ptr; + + sl_ptr = node->node->symlink; + + done = 0; + path_count = 0; + dir_count = 0; + dir_copied = 0; + current = cd9660node_susp_create_node(SUSP_TYPE_RRIP, + SUSP_ENTRY_RRIP_SL, "SL", SUSP_LOC_ENTRY); + + current->attr.rr_entry.SL.h.version[0] = 1; + current->attr.rr_entry.SL.flags[0] = SL_FLAGS_NONE; + + if (*sl_ptr == '/') { + temp_cr[0] = SL_FLAGS_ROOT; + temp_cr[1] = 0; + memcpy(current->attr.rr_entry.SL.component + path_count, + temp_cr, 2); + path_count += 2; + sl_ptr++; + } + + for (i = 0; i < (dir_count + 2); i++) + temp_cr[i] = '\0'; + + while (!done) { + while ((*sl_ptr != '/') && (*sl_ptr != '\0')) { + dir_copied = 1; + if (*sl_ptr == '.') { + if ((*(sl_ptr + 1) == '/') || (*(sl_ptr + 1) + == '\0')) { + temp_cr[0] = SL_FLAGS_CURRENT; + sl_ptr++; + } else if(*(sl_ptr + 1) == '.') { + if ((*(sl_ptr + 2) == '/') || + (*(sl_ptr + 2) == '\0')) { + temp_cr[0] = SL_FLAGS_PARENT; + sl_ptr += 2; + } + } else { + temp_cr[dir_count+2] = *sl_ptr; + sl_ptr++; + dir_count++; + } + } else { + temp_cr[dir_count + 2] = *sl_ptr; + sl_ptr++; + dir_count++; + } + } + + if ((path_count + dir_count) >= 249) { + current->attr.rr_entry.SL.flags[0] |= SL_FLAGS_CONTINUE; + + j = 0; + + if (path_count <= 249) { + while(j != (249 - path_count)) { + temp_sl[j] = temp_cr[j]; + j++; + } + temp_sl[0] = SL_FLAGS_CONTINUE; + temp_sl[1] = j - 2; + memcpy( + current->attr.rr_entry.SL.component + + path_count, + temp_sl, j); + } + + path_count += j; + current->attr.rr_entry.SL.h.length[0] = path_count + 5; + TAILQ_INSERT_TAIL(&node->head, current, rr_ll); + current= cd9660node_susp_create_node(SUSP_TYPE_RRIP, + SUSP_ENTRY_RRIP_SL, "SL", SUSP_LOC_ENTRY); + current->attr.rr_entry.SL.h.version[0] = 1; + current->attr.rr_entry.SL.flags[0] = SL_FLAGS_NONE; + + path_count = 0; + + if (dir_count > 2) { + while (j != dir_count + 2) { + current->attr.rr_entry.SL.component[ + path_count + 2] = temp_cr[j]; + j++; + path_count++; + } + current->attr.rr_entry.SL.component[1] + = path_count; + path_count+= 2; + } else { + while(j != dir_count) { + current->attr.rr_entry.SL.component[ + path_count+2] = temp_cr[j]; + j++; + path_count++; + } + } + } else { + if (dir_copied == 1) { + temp_cr[1] = dir_count; + memcpy(current->attr.rr_entry.SL.component + + path_count, + temp_cr, dir_count + 2); + path_count += dir_count + 2; + } + } + + if (*sl_ptr == '\0') { + done = 1; + current->attr.rr_entry.SL.h.length[0] = path_count + 5; + TAILQ_INSERT_TAIL(&node->head, current, rr_ll); + } else { + sl_ptr++; + dir_count = 0; + dir_copied = 0; + for(i = 0; i < 255; i++) { + temp_cr[i] = '\0'; + } + } + } +} + +int +cd9660node_rrip_px(struct ISO_SUSP_ATTRIBUTES *v, fsnode *pxinfo) +{ + v->attr.rr_entry.PX.h.length[0] = 36; + v->attr.rr_entry.PX.h.version[0] = 1; + cd9660_bothendian_dword(pxinfo->inode->st.st_mode, + v->attr.rr_entry.PX.mode); + cd9660_bothendian_dword(pxinfo->inode->st.st_nlink, + v->attr.rr_entry.PX.links); + cd9660_bothendian_dword(pxinfo->inode->st.st_uid, + v->attr.rr_entry.PX.uid); + cd9660_bothendian_dword(pxinfo->inode->st.st_gid, + v->attr.rr_entry.PX.gid); + + /* Ignoring the serial number for now */ + return 1; +} + +int +cd9660node_rrip_pn(struct ISO_SUSP_ATTRIBUTES *pn_field, fsnode *fnode) +{ + pn_field->attr.rr_entry.PN.h.length[0] = 20; + pn_field->attr.rr_entry.PN.h.version[0] = 1; + + if (sizeof (fnode->inode->st.st_dev) > 32) + cd9660_bothendian_dword((uint64_t)fnode->inode->st.st_dev >> 32, + pn_field->attr.rr_entry.PN.high); + else + cd9660_bothendian_dword(0, pn_field->attr.rr_entry.PN.high); + + cd9660_bothendian_dword(fnode->inode->st.st_dev & 0xffffffff, + pn_field->attr.rr_entry.PN.low); + return 1; +} + +#if 0 +int +cd9660node_rrip_nm(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *file_node) +{ + int nm_length = strlen(file_node->isoDirRecord->name) + 5; + p->attr.rr_entry.NM.h.type[0] = 'N'; + p->attr.rr_entry.NM.h.type[1] = 'M'; + sprintf(p->attr.rr_entry.NM.altname, "%s", file_node->isoDirRecord->name); + p->attr.rr_entry.NM.h.length[0] = (unsigned char)nm_length; + p->attr.rr_entry.NM.h.version[0] = (unsigned char)1; + p->attr.rr_entry.NM.flags[0] = (unsigned char) NM_PARENT; + return 1; +} +#endif + +int +cd9660node_rrip_tf(struct ISO_SUSP_ATTRIBUTES *p, fsnode *_node) +{ + p->attr.rr_entry.TF.flags[0] = TF_MODIFY | TF_ACCESS | TF_ATTRIBUTES; + p->attr.rr_entry.TF.h.length[0] = 5; + p->attr.rr_entry.TF.h.version[0] = 1; + + /* + * Need to add creation time, backup time, + * expiration time, and effective time. + */ + + cd9660_time_915(p->attr.rr_entry.TF.timestamp, + _node->inode->st.st_atime); + p->attr.rr_entry.TF.h.length[0] += 7; + + cd9660_time_915(p->attr.rr_entry.TF.timestamp + 7, + _node->inode->st.st_mtime); + p->attr.rr_entry.TF.h.length[0] += 7; + + cd9660_time_915(p->attr.rr_entry.TF.timestamp + 14, + _node->inode->st.st_ctime); + p->attr.rr_entry.TF.h.length[0] += 7; + return 1; +} + +int +cd9660_susp_sp(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *spinfo __unused) +{ + p->attr.su_entry.SP.h.length[0] = 7; + p->attr.su_entry.SP.h.version[0] = 1; + p->attr.su_entry.SP.check[0] = 0xBE; + p->attr.su_entry.SP.check[1] = 0xEF; + p->attr.su_entry.SP.len_skp[0] = 0; + return 1; +} + +int +cd9660_susp_st(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *stinfo __unused) +{ + p->attr.su_entry.ST.h.type[0] = 'S'; + p->attr.su_entry.ST.h.type[1] = 'T'; + p->attr.su_entry.ST.h.length[0] = 4; + p->attr.su_entry.ST.h.version[0] = 1; + return 1; +} + +int +cd9660_susp_ce(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *spinfo __unused) +{ + p->attr.su_entry.CE.h.length[0] = 28; + p->attr.su_entry.CE.h.version[0] = 1; + /* Other attributes dont matter right now, will be updated later */ + return 1; +} + +int +cd9660_susp_pd(struct ISO_SUSP_ATTRIBUTES *p __unused, int length __unused) +{ + return 1; +} + +void +cd9660_rrip_add_NM(cd9660node *node, const char *name) +{ + int working,len; + const char *p; + struct ISO_SUSP_ATTRIBUTES *r; + + /* + * Each NM record has 254 byes to work with. This means that + * the name data itself only has 249 bytes to work with. So, a + * name with 251 characters would require two nm records. + */ + p = name; + working = 1; + while (working) { + r = cd9660node_susp_create_node(SUSP_TYPE_RRIP, + SUSP_ENTRY_RRIP_NM, "NM", SUSP_LOC_ENTRY); + r->attr.rr_entry.NM.h.version[0] = 1; + r->attr.rr_entry.NM.flags[0] = RRIP_NM_FLAGS_NONE; + len = strlen(p); + + if (len > 249) { + len = 249; + r->attr.rr_entry.NM.flags[0] = RRIP_NM_FLAGS_CONTINUE; + } else { + working = 0; + } + memcpy(r->attr.rr_entry.NM.altname, p, len); + r->attr.rr_entry.NM.h.length[0] = 5 + len; + + TAILQ_INSERT_TAIL(&node->head, r, rr_ll); + + p += len; + } +} + +void +cd9660_rrip_NM(cd9660node *node) +{ + cd9660_rrip_add_NM(node, node->node->name); +} + +struct ISO_SUSP_ATTRIBUTES* +cd9660_susp_ER(cd9660node *node, + u_char ext_version, const char* ext_id, const char* ext_des, + const char* ext_src) +{ + int l; + struct ISO_SUSP_ATTRIBUTES *r; + + r = cd9660node_susp_create_node(SUSP_TYPE_SUSP, + SUSP_ENTRY_SUSP_ER, "ER", SUSP_LOC_DOT); + + /* Fixed data is 8 bytes */ + r->attr.su_entry.ER.h.length[0] = 8; + r->attr.su_entry.ER.h.version[0] = 1; + + r->attr.su_entry.ER.len_id[0] = (u_char)strlen(ext_id); + r->attr.su_entry.ER.len_des[0] = (u_char)strlen(ext_des); + r->attr.su_entry.ER.len_src[0] = (u_char)strlen(ext_src); + + l = r->attr.su_entry.ER.len_id[0] + + r->attr.su_entry.ER.len_src[0] + + r->attr.su_entry.ER.len_des[0]; + + /* Everything must fit. */ + assert(l + r->attr.su_entry.ER.h.length[0] <= 254); + + r->attr.su_entry.ER.h.length[0] += (u_char)l; + + + r->attr.su_entry.ER.ext_ver[0] = ext_version; + memcpy(r->attr.su_entry.ER.ext_data, ext_id, + (int)r->attr.su_entry.ER.len_id[0]); + l = (int) r->attr.su_entry.ER.len_id[0]; + memcpy(r->attr.su_entry.ER.ext_data + l,ext_des, + (int)r->attr.su_entry.ER.len_des[0]); + + l += (int)r->attr.su_entry.ER.len_des[0]; + memcpy(r->attr.su_entry.ER.ext_data + l,ext_src, + (int)r->attr.su_entry.ER.len_src[0]); + + TAILQ_INSERT_TAIL(&node->head, r, rr_ll); + return r; +} + +struct ISO_SUSP_ATTRIBUTES* +cd9660_susp_ES(struct ISO_SUSP_ATTRIBUTES *last __unused, cd9660node *node __unused) +{ + return NULL; +} diff --git a/usr.sbin/makefs/cd9660/iso9660_rrip.h b/usr.sbin/makefs/cd9660/iso9660_rrip.h new file mode 100644 index 000000000..1f54ae840 --- /dev/null +++ b/usr.sbin/makefs/cd9660/iso9660_rrip.h @@ -0,0 +1,290 @@ +/* $NetBSD: iso9660_rrip.h,v 1.6 2013/01/28 21:03:28 christos Exp $ */ + +/* + * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan + * Perez-Rathke and Ram Vedam. All rights reserved. + * + * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys, + * Alan Perez-Rathke and Ram Vedam. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ +#ifndef __ISO9660_RRIP_H__ +#define __ISO9660_RRIP_H__ + +/* + * This will hold all the functions needed to + * write an ISO 9660 image with Rock Ridge Extensions + */ + +/* For writing must use ISO_RRIP_EXTREF structure */ + +#include "makefs.h" +#include +#include "cd9660.h" +#include + +#define PX_LENGTH 0x2C +#define PN_LENGTH 0x14 +#define TF_CREATION 0x00 +#define TF_MODIFY 0x01 +#define TF_ACCESS 0x02 +#define TF_ATTRIBUTES 0x04 +#define TF_BACKUP 0x08 +#define TF_EXPIRATION 0x10 +#define TF_EFFECTIVE 0x20 +#define TF_LONGFORM 0x40 +#define NM_CONTINUE 0x80 +#define NM_CURRENT 0x100 +#define NM_PARENT 0x200 + + +#define SUSP_LOC_ENTRY 0x01 +#define SUSP_LOC_DOT 0x02 +#define SUSP_LOC_DOTDOT 0x04 + +#define SUSP_TYPE_SUSP 1 +#define SUSP_TYPE_RRIP 2 + +#define SUSP_ENTRY_SUSP_CE 1 +#define SUSP_ENTRY_SUSP_PD 2 +#define SUSP_ENTRY_SUSP_SP 3 +#define SUSP_ENTRY_SUSP_ST 4 +#define SUSP_ENTRY_SUSP_ER 5 +#define SUSP_ENTRY_SUSP_ES 6 + +#define SUSP_ENTRY_RRIP_PX 1 +#define SUSP_ENTRY_RRIP_PN 2 +#define SUSP_ENTRY_RRIP_SL 3 +#define SUSP_ENTRY_RRIP_NM 4 +#define SUSP_ENTRY_RRIP_CL 5 +#define SUSP_ENTRY_RRIP_PL 6 +#define SUSP_ENTRY_RRIP_RE 7 +#define SUSP_ENTRY_RRIP_TF 8 +#define SUSP_ENTRY_RRIP_SF 9 + +#define SUSP_RRIP_ER_EXT_ID "IEEE_P1282" +#define SUSP_RRIP_ER_EXT_DES "THE IEEE P1282 PROTOCOL PROVIDES SUPPORT FOR POSIX FILE SYSTEM SEMANTICS." +#define SUSP_RRIP_ER_EXT_SRC "PLEASE CONTACT THE IEEE STANDARDS DEPARTMENT, PISCATAWAY, NJ, USA FOR THE P1282 SPECIFICATION." + +#define SL_FLAGS_NONE 0 +#define SL_FLAGS_CONTINUE 1 +#define SL_FLAGS_CURRENT 2 +#define SL_FLAGS_PARENT 4 +#define SL_FLAGS_ROOT 8 + +typedef struct { + ISO_SUSP_HEADER h; + u_char mode [ISODCL(5,12)]; + u_char links [ISODCL(13,20)]; + u_char uid [ISODCL(21,28)]; + u_char gid [ISODCL(29,36)]; + u_char serial [ISODCL(37,44)];/* Not used */ +} ISO_RRIP_PX; + +typedef struct { + ISO_SUSP_HEADER h; + u_char high [ISODCL(5,12)]; + u_char low [ISODCL(13,20)]; +} ISO_RRIP_PN; + +typedef struct { + ISO_SUSP_HEADER h; + u_char flags [ISODCL ( 4, 4)]; + u_char component [ISODCL ( 4, 256)]; + u_int nBytes; +} ISO_RRIP_SL; + +typedef struct { + ISO_SUSP_HEADER h; + u_char flags [ISODCL ( 4, 4)]; + u_char timestamp [ISODCL ( 5, 256)]; +} ISO_RRIP_TF; + +#define RRIP_NM_FLAGS_NONE 0x00 +#define RRIP_NM_FLAGS_CONTINUE 0x01 +#define RRIP_NM_FLAGS_CURRENT 0x02 +#define RRIP_NM_FLAGS_PARENT 0x04 + +typedef struct { + ISO_SUSP_HEADER h; + u_char flags [ISODCL ( 4, 4)]; + u_char altname [ISODCL ( 4, 256)]; +} ISO_RRIP_NM; + +/* Note that this is the same structure as cd9660_rrip.h : ISO_RRIP_CONT */ +typedef struct { + ISO_SUSP_HEADER h; + u_char ca_sector [ISODCL ( 5, 12)]; + u_char offset [ISODCL ( 13, 20)]; + u_char length [ISODCL ( 21, 28)]; +} ISO_SUSP_CE; + +typedef struct { + ISO_SUSP_HEADER h; + u_char padding_area [ISODCL ( 4, 256)]; +} ISO_SUSP_PD; + +typedef struct { + ISO_SUSP_HEADER h; + u_char check [ISODCL ( 4, 5)]; + u_char len_skp [ISODCL ( 6, 6)]; +} ISO_SUSP_SP; + +typedef struct { + ISO_SUSP_HEADER h; +} ISO_SUSP_ST; + +typedef struct { + ISO_SUSP_HEADER h; + u_char len_id [ISODCL ( 4, 4)]; + u_char len_des [ISODCL ( 5, 5)]; + u_char len_src [ISODCL ( 6, 6)]; + u_char ext_ver [ISODCL ( 7, 7)]; + u_char ext_data [ISODCL (8,256)]; +/* u_char ext_id [ISODCL ( 8, 256)]; + u_char ext_des [ISODCL ( 257, 513)]; + u_char ext_src [ISODCL ( 514, 770)];*/ +} ISO_SUSP_ER; + +typedef struct { + ISO_SUSP_HEADER h; + u_char ext_seq [ISODCL ( 4, 4)]; +} ISO_SUSP_ES; + +typedef union { + ISO_RRIP_PX PX; + ISO_RRIP_PN PN; + ISO_RRIP_SL SL; + ISO_RRIP_NM NM; + ISO_RRIP_CLINK CL; + ISO_RRIP_PLINK PL; + ISO_RRIP_RELDIR RE; + ISO_RRIP_TF TF; +} rrip_entry; + +typedef union { + ISO_SUSP_CE CE; + ISO_SUSP_PD PD; + ISO_SUSP_SP SP; + ISO_SUSP_ST ST; + ISO_SUSP_ER ER; + ISO_SUSP_ES ES; +} susp_entry; + +typedef union { + susp_entry su_entry; + rrip_entry rr_entry; +} SUSP_ENTRIES; + +struct ISO_SUSP_ATTRIBUTES { + SUSP_ENTRIES attr; + int type; + char type_of[2]; + char last_in_suf; /* last entry in the System Use Field? */ + /* Dan's addons - will merge later. This allows use of a switch */ + char susp_type; /* SUSP or RRIP */ + char entry_type; /* Record type */ + char write_location; + TAILQ_ENTRY(ISO_SUSP_ATTRIBUTES) rr_ll; +}; + +#define CD9660_SUSP_ENTRY_SIZE(entry)\ + ((int) ((entry)->attr.su_entry.SP.h.length[0])) + +/* Recursive function - move later to func pointer code*/ +int cd9660_susp_finalize(iso9660_disk *, cd9660node *); + +/* These two operate on single nodes */ +int cd9660_susp_finalize_node(iso9660_disk *, cd9660node *); +int cd9660_rrip_finalize_node(iso9660_disk *, cd9660node *); + +/* POSIX File attribute */ +int cd9660node_rrip_px(struct ISO_SUSP_ATTRIBUTES *, fsnode *); + +/* Device number */ +int cd9660node_rrip_pn(struct ISO_SUSP_ATTRIBUTES *, fsnode *); + +/* Symbolic link */ +int cd9660node_rrip_SL(struct ISO_SUSP_ATTRIBUTES *, fsnode *); + +/* Alternate Name function */ +void cd9660_rrip_NM(cd9660node *); +void cd9660_rrip_add_NM(cd9660node *,const char *); + +/* Parent and child link function */ +int cd9660_rrip_PL(struct ISO_SUSP_ATTRIBUTES *, cd9660node *); +int cd9660_rrip_CL(struct ISO_SUSP_ATTRIBUTES *, cd9660node *); +int cd9660_rrip_RE(struct ISO_SUSP_ATTRIBUTES *, cd9660node *); + +int cd9660node_rrip_tf(struct ISO_SUSP_ATTRIBUTES *, fsnode *); + + + +/* + * Relocation directory function. I'm not quite sure what + * sort of parameters are needed, but personally I don't think + * any parameters are needed except for the memory address where + * the information needs to be put in + */ +int cd9660node_rrip_re(void *, fsnode *); + +/* + * Don't know if this function is needed because it apparently is an + * optional feature that does not really need to be implemented but I + * thought I should add it anyway. + */ +int cd9660_susp_ce (struct ISO_SUSP_ATTRIBUTES *, cd9660node *); +int cd9660_susp_pd (struct ISO_SUSP_ATTRIBUTES *, int); +int cd9660_susp_sp (struct ISO_SUSP_ATTRIBUTES *, cd9660node *); +int cd9660_susp_st (struct ISO_SUSP_ATTRIBUTES *, cd9660node *); + +struct ISO_SUSP_ATTRIBUTES *cd9660_susp_ER(cd9660node *, u_char, const char *, + const char *, const char *); +struct ISO_SUSP_ATTRIBUTES *cd9660_susp_ES(struct ISO_SUSP_ATTRIBUTES*, + cd9660node *); + + +/* Helper functions */ + +/* Common SUSP/RRIP functions */ +int cd9660_susp_initialize(iso9660_disk *, cd9660node *, cd9660node *, + cd9660node *); +int cd9660_susp_initialize_node(iso9660_disk *, cd9660node *); +struct ISO_SUSP_ATTRIBUTES *cd9660node_susp_create_node(int, int, const char *, + int); +struct ISO_SUSP_ATTRIBUTES *cd9660node_susp_add_entry(cd9660node *, + struct ISO_SUSP_ATTRIBUTES *, struct ISO_SUSP_ATTRIBUTES *, int); + +/* RRIP specific functions */ +int cd9660_rrip_initialize_node(iso9660_disk *, cd9660node *, cd9660node *, + cd9660node *); +void cd9660_createSL(cd9660node *); + +/* Functions that probably can be removed */ +/* int cd9660node_initialize_node(int, char *); */ + + +#endif diff --git a/usr.sbin/makefs/chfs.c b/usr.sbin/makefs/chfs.c new file mode 100644 index 000000000..de14246c0 --- /dev/null +++ b/usr.sbin/makefs/chfs.c @@ -0,0 +1,222 @@ +/*- + * Copyright (c) 2012 Department of Software Engineering, + * University of Szeged, Hungary + * Copyright (c) 2012 Tamas Toth + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by the Department of Software Engineering, University of Szeged, Hungary + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "makefs.h" +#include "chfs_makefs.h" + +#include "chfs/chfs_mkfs.h" + +static void chfs_validate(const char *, fsnode *, fsinfo_t *); +static int chfs_create_image(const char *, fsinfo_t *); +static int chfs_populate_dir(const char *, fsnode *, fsnode *, fsinfo_t *); + + +void +chfs_prep_opts(fsinfo_t *fsopts) +{ + chfs_opt_t *chfs_opts = ecalloc(1, sizeof(*chfs_opts)); + + const option_t chfs_options[] = { + { 'p', "pagesize", &chfs_opts->pagesize, OPT_INT32, + 1, INT_MAX, "page size" }, + { 'e', "eraseblock", &chfs_opts->eraseblock, OPT_INT32, + 1, INT_MAX, "eraseblock size" }, + { 'm', "mediatype", &chfs_opts->mediatype, OPT_INT32, + 0, 1, "type of the media, 0 (nor) or 1 (nand)" }, + { .name = NULL } + }; + + chfs_opts->pagesize = -1; + chfs_opts->eraseblock = -1; + chfs_opts->mediatype = -1; + + fsopts->size = 0; + fsopts->fs_specific = chfs_opts; + fsopts->fs_options = copy_opts(chfs_options); +} + +void +chfs_cleanup_opts(fsinfo_t *fsopts) +{ + free(fsopts->fs_specific); + free(fsopts->fs_options); +} + +int +chfs_parse_opts(const char *option, fsinfo_t *fsopts) +{ + + assert(option != NULL); + assert(fsopts != NULL); + + return set_option(fsopts->fs_options, option, NULL, 0) != -1; +} + +void +chfs_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts) +{ + struct timeval start; + + assert(image != NULL); + assert(dir != NULL); + assert(root != NULL); + assert(fsopts != NULL); + + TIMER_START(start); + chfs_validate(dir, root, fsopts); + TIMER_RESULTS(start, "chfs_validate"); + + printf("Creating `%s'\n", image); + TIMER_START(start); + if (chfs_create_image(image, fsopts) == -1) { + errx(EXIT_FAILURE, "Image file `%s' not created", image); + } + TIMER_RESULTS(start, "chfs_create_image"); + + fsopts->curinode = CHFS_ROOTINO; + root->inode->ino = CHFS_ROOTINO; + + printf("Populating `%s'\n", image); + TIMER_START(start); + write_eb_header(fsopts); + if (!chfs_populate_dir(dir, root, root, fsopts)) { + errx(EXIT_FAILURE, "Image file `%s' not populated", image); + } + TIMER_RESULTS(start, "chfs_populate_dir"); + + padblock(fsopts); + + if (close(fsopts->fd) == -1) { + err(EXIT_FAILURE, "Closing `%s'", image); + } + fsopts->fd = -1; + + printf("Image `%s' complete\n", image); +} + +static void +chfs_validate(const char* dir, fsnode *root, fsinfo_t *fsopts) +{ + chfs_opt_t *chfs_opts; + assert(dir != NULL); + assert(root != NULL); + assert(fsopts != NULL); + + chfs_opts = fsopts->fs_specific; + + if (chfs_opts->pagesize == -1) { + chfs_opts->pagesize = DEFAULT_PAGESIZE; + } + if (chfs_opts->eraseblock == -1) { + chfs_opts->eraseblock = DEFAULT_ERASEBLOCK; + } + if (chfs_opts->mediatype == -1) { + chfs_opts->mediatype = DEFAULT_MEDIATYPE; + } +} + +static int +chfs_create_image(const char *image, fsinfo_t *fsopts) +{ + assert(image != NULL); + assert(fsopts != NULL); + + if ((fsopts->fd = open(image, O_RDWR | O_CREAT | O_TRUNC, 0666)) == -1) { + warn("Can't open `%s' for writing", image); + return -1; + } + + return fsopts->fd; +} + +static int +chfs_populate_dir(const char *dir, fsnode *root, fsnode *parent, + fsinfo_t *fsopts) +{ + fsnode *cur; + char path[MAXPATHLEN + 1]; + + assert(dir != NULL); + assert(root != NULL); + assert(fsopts != NULL); + + for (cur = root->next; cur != NULL; cur = cur->next) { + if ((cur->inode->flags & FI_ALLOCATED) == 0) { + cur->inode->flags |= FI_ALLOCATED; + if (cur != root) { + fsopts->curinode++; + cur->inode->ino = fsopts->curinode; + cur->parent = parent; + } + } + + if (cur->inode->flags & FI_WRITTEN) { + continue; // hard link + } + cur->inode->flags |= FI_WRITTEN; + + write_vnode(fsopts, cur); + write_dirent(fsopts, cur); + if (!S_ISDIR(cur->type & S_IFMT)) { + write_file(fsopts, cur, dir); + } + } + + for (cur = root; cur != NULL; cur = cur->next) { + if (cur->child == NULL) { + continue; + } + if ((size_t)snprintf(path, sizeof(path), "%s/%s", dir, + cur->name) >= sizeof(path)) { + errx(EXIT_FAILURE, "Pathname too long"); + } + if (!chfs_populate_dir(path, cur->child, cur, fsopts)) { + return 0; + } + } + + return 1; +} + diff --git a/usr.sbin/makefs/chfs/Makefile.inc b/usr.sbin/makefs/chfs/Makefile.inc new file mode 100644 index 000000000..254c4a3ab --- /dev/null +++ b/usr.sbin/makefs/chfs/Makefile.inc @@ -0,0 +1,11 @@ +CHFS= ${NETBSDSRCDIR}/sys/ufs/chfs + +.PATH: ${.CURDIR}/chfs ${NETBSDSRCDIR}/sys/ufs/chfs + +CPPFLAGS+= -I${CHFS} + +SRCS+= chfs_mkfs.c +.if !defined(HOSTPROG) +LDADD+= -lz +DPADD+= ${LIBZ} +.endif diff --git a/usr.sbin/makefs/chfs/chfs_mkfs.c b/usr.sbin/makefs/chfs/chfs_mkfs.c new file mode 100644 index 000000000..4c96f66b8 --- /dev/null +++ b/usr.sbin/makefs/chfs/chfs_mkfs.c @@ -0,0 +1,307 @@ +/*- + * Copyright (c) 2012 Department of Software Engineering, + * University of Szeged, Hungary + * Copyright (c) 2012 Tamas Toth + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by the Department of Software Engineering, University of Szeged, Hungary + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#if defined(__minix) +#include +#endif + +#include "makefs.h" +#include "chfs_makefs.h" + +#include "media.h" +#include "ebh.h" + +#include "chfs/chfs_mkfs.h" + +static uint32_t img_ofs = 0; +static uint64_t version = 0; +static uint64_t max_serial = 0; +static int lebnumber = 0; + +static const unsigned char ffbuf[16] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +static void +buf_write(fsinfo_t *fsopts, const void *buf, size_t len) +{ + ssize_t retval; + const char *charbuf = buf; + + while (len > 0) { + retval = write(fsopts->fd, charbuf, len); + + if (retval == -1) { + err(EXIT_FAILURE, "ERROR while writing"); + } + + len -= retval; + charbuf += retval; + img_ofs += retval; + } +} + +void +padblock(fsinfo_t *fsopts) +{ + chfs_opt_t *chfs_opts = fsopts->fs_specific; + while (img_ofs % chfs_opts->eraseblock) { + buf_write(fsopts, ffbuf, MIN(sizeof(ffbuf), + chfs_opts->eraseblock - (img_ofs % chfs_opts->eraseblock))); + } +} + +static void +padword(fsinfo_t *fsopts) +{ + if (img_ofs % 4) { + buf_write(fsopts, ffbuf, 4 - img_ofs % 4); + } +} + +static void +pad_block_if_less_than(fsinfo_t *fsopts, int req) +{ + chfs_opt_t *chfs_opts = fsopts->fs_specific; + if ((img_ofs % chfs_opts->eraseblock) + req > + (uint32_t)chfs_opts->eraseblock) { + padblock(fsopts); + write_eb_header(fsopts); + } +} + +void +write_eb_header(fsinfo_t *fsopts) +{ + chfs_opt_t *opts; + struct chfs_eb_hdr ebhdr; + char *buf; + + opts = fsopts->fs_specific; + +#define MINSIZE MAX(MAX(CHFS_EB_EC_HDR_SIZE, CHFS_EB_HDR_NOR_SIZE), \ + CHFS_EB_HDR_NAND_SIZE) + if ((uint32_t)opts->pagesize < MINSIZE) + errx(EXIT_FAILURE, "pagesize cannot be less than %zu", MINSIZE); + buf = emalloc(opts->pagesize); + memset(buf, 0xFF, opts->pagesize); + + ebhdr.ec_hdr.magic = htole32(CHFS_MAGIC_BITMASK); + ebhdr.ec_hdr.erase_cnt = htole32(1); + ebhdr.ec_hdr.crc_ec = htole32(crc32(0, + (uint8_t *)&ebhdr.ec_hdr + 8, 4)); + + memcpy(buf, &ebhdr.ec_hdr, CHFS_EB_EC_HDR_SIZE); + + buf_write(fsopts, buf, opts->pagesize); + + memset(buf, 0xFF, opts->pagesize); + + if (opts->mediatype == TYPE_NAND) { + ebhdr.u.nand_hdr.lid = htole32(lebnumber++); + ebhdr.u.nand_hdr.serial = htole64(++(max_serial)); + ebhdr.u.nand_hdr.crc = htole32(crc32(0, + (uint8_t *)&ebhdr.u.nand_hdr + 4, + CHFS_EB_HDR_NAND_SIZE - 4)); + memcpy(buf, &ebhdr.u.nand_hdr, CHFS_EB_HDR_NAND_SIZE); + } else { + ebhdr.u.nor_hdr.lid = htole32(lebnumber++); + ebhdr.u.nor_hdr.crc = htole32(crc32(0, + (uint8_t *)&ebhdr.u.nor_hdr + 4, + CHFS_EB_HDR_NOR_SIZE - 4)); + memcpy(buf, &ebhdr.u.nor_hdr, CHFS_EB_HDR_NOR_SIZE); + } + + buf_write(fsopts, buf, opts->pagesize); + free(buf); +} + +void +write_vnode(fsinfo_t *fsopts, fsnode *node) +{ + struct chfs_flash_vnode fvnode; + memset(&fvnode, 0, sizeof(fvnode)); + + fvnode.magic = htole16(CHFS_FS_MAGIC_BITMASK); + fvnode.type = htole16(CHFS_NODETYPE_VNODE); + fvnode.length = htole32(CHFS_PAD(sizeof(fvnode))); + fvnode.hdr_crc = htole32(crc32(0, (uint8_t *)&fvnode, + CHFS_NODE_HDR_SIZE - 4)); + fvnode.vno = htole64(node->inode->ino); + fvnode.version = htole64(version++); + fvnode.mode = htole32(node->inode->st.st_mode); + fvnode.dn_size = htole32(node->inode->st.st_size); + fvnode.atime = htole32(node->inode->st.st_atime); + fvnode.ctime = htole32(node->inode->st.st_ctime); + fvnode.mtime = htole32(node->inode->st.st_mtime); + fvnode.gid = htole32(node->inode->st.st_uid); + fvnode.uid = htole32(node->inode->st.st_gid); + fvnode.node_crc = htole32(crc32(0, (uint8_t *)&fvnode, + sizeof(fvnode) - 4)); + + pad_block_if_less_than(fsopts, sizeof(fvnode)); + buf_write(fsopts, &fvnode, sizeof(fvnode)); + padword(fsopts); +} + +void +write_dirent(fsinfo_t *fsopts, fsnode *node) +{ + struct chfs_flash_dirent_node fdirent; + char *name; + + name = emalloc(strlen(node->name)); + memcpy(name, node->name, strlen(node->name)); + + memset(&fdirent, 0, sizeof(fdirent)); + fdirent.magic = htole16(CHFS_FS_MAGIC_BITMASK); + fdirent.type = htole16(CHFS_NODETYPE_DIRENT); + fdirent.length = htole32(CHFS_PAD(sizeof(fdirent) + strlen(name))); + fdirent.hdr_crc = htole32(crc32(0, (uint8_t *)&fdirent, + CHFS_NODE_HDR_SIZE - 4)); + fdirent.vno = htole64(node->inode->ino); + if (node->parent != NULL) { + fdirent.pvno = htole64(node->parent->inode->ino); + } else { + fdirent.pvno = htole64(node->inode->ino); + } + + fdirent.version = htole64(version++); + fdirent.mctime = 0; + fdirent.nsize = htole32(strlen(name)); + fdirent.dtype = htole32(IFTOCHT(node->type & S_IFMT)); + fdirent.name_crc = htole32(crc32(0, (uint8_t *)name, fdirent.nsize)); + fdirent.node_crc = htole32(crc32(0, (uint8_t *)&fdirent, + sizeof(fdirent) - 4)); + + pad_block_if_less_than(fsopts, sizeof(fdirent) + fdirent.nsize); + buf_write(fsopts, &fdirent, sizeof(fdirent)); + buf_write(fsopts, name, fdirent.nsize); + padword(fsopts); +} + +void +write_file(fsinfo_t *fsopts, fsnode *node, const char *dir) +{ + int fd; + ssize_t len; + char *name = node->name; + chfs_opt_t *opts; + unsigned char *buf; + uint32_t fileofs = 0; + + opts = fsopts->fs_specific; + buf = emalloc(opts->pagesize); + if (node->type == S_IFREG || node->type == S_IFSOCK) { + char *longname; + if (asprintf(&longname, "%s/%s", dir, name) == 1) + goto out; + + fd = open(longname, O_RDONLY, 0444); + if (fd == -1) + err(EXIT_FAILURE, "Cannot open `%s'", longname); + + while ((len = read(fd, buf, opts->pagesize))) { + if (len < 0) { + warn("ERROR while reading %s", longname); + free(longname); + free(buf); + close(fd); + return; + } + + write_data(fsopts, node, buf, len, fileofs); + fileofs += len; + } + free(longname); + close(fd); + } else if (node->type == S_IFLNK) { + len = strlen(node->symlink); + memcpy(buf, node->symlink, len); + write_data(fsopts, node, buf, len, 0); + } else if (node->type == S_IFCHR || node->type == S_IFBLK || + node->type == S_IFIFO) { + len = sizeof(dev_t); + memcpy(buf, &(node->inode->st.st_rdev), len); + write_data(fsopts, node, buf, len, 0); + } + + free(buf); + return; +out: + err(EXIT_FAILURE, "Memory allocation failed"); +} + +void +write_data(fsinfo_t *fsopts, fsnode *node, unsigned char *buf, size_t len, + uint32_t ofs) +{ + struct chfs_flash_data_node fdata; + + memset(&fdata, 0, sizeof(fdata)); + if (len == 0) { + return; + } + + pad_block_if_less_than(fsopts, sizeof(fdata) + len); + + fdata.magic = htole16(CHFS_FS_MAGIC_BITMASK); + fdata.type = htole16(CHFS_NODETYPE_DATA); + fdata.length = htole32(CHFS_PAD(sizeof(fdata) + len)); + fdata.hdr_crc = htole32(crc32(0, (uint8_t *)&fdata, + CHFS_NODE_HDR_SIZE - 4)); + fdata.vno = htole64(node->inode->ino); + fdata.data_length = htole32(len); + fdata.offset = htole32(ofs); + fdata.data_crc = htole32(crc32(0, (uint8_t *)buf, len)); + fdata.node_crc = htole32(crc32(0, + (uint8_t *)&fdata, sizeof(fdata) - 4)); + + buf_write(fsopts, &fdata, sizeof(fdata)); + buf_write(fsopts, buf, len); + padword(fsopts); +} diff --git a/usr.sbin/makefs/chfs/chfs_mkfs.h b/usr.sbin/makefs/chfs/chfs_mkfs.h new file mode 100644 index 000000000..83229926a --- /dev/null +++ b/usr.sbin/makefs/chfs/chfs_mkfs.h @@ -0,0 +1,44 @@ +/*- + * Copyright (c) 2012 Department of Software Engineering, + * University of Szeged, Hungary + * Copyright (c) 2012 Tamas Toth + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by the Department of Software Engineering, University of Szeged, Hungary + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _CHFS_MKFS_H +#define _CHFS_MKFS_H + +#include "chfs.h" + +void padblock(fsinfo_t *); +void write_eb_header(fsinfo_t *); +void write_vnode(fsinfo_t *, fsnode *); +void write_dirent(fsinfo_t *, fsnode *); +void write_file(fsinfo_t *, fsnode *, const char *); +void write_data(fsinfo_t *, fsnode *, unsigned char *, size_t, uint32_t); +#endif + diff --git a/usr.sbin/makefs/chfs_makefs.h b/usr.sbin/makefs/chfs_makefs.h new file mode 100644 index 000000000..e722a9f5d --- /dev/null +++ b/usr.sbin/makefs/chfs_makefs.h @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 2012 Department of Software Engineering, + * University of Szeged, Hungary + * Copyright (c) 2012 Tamas Toth + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by the Department of Software Engineering, University of Szeged, Hungary + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _CHFS_MAKEFS_H +#define _CHFS_MAKEFS_H + +#define TYPE_NOR 0 +#define TYPE_NAND 1 + +#define DEFAULT_PAGESIZE 2048 +#define DEFAULT_ERASEBLOCK 131072 +#define DEFAULT_MEDIATYPE TYPE_NAND + +typedef struct { + int pagesize; /* page size */ + int eraseblock; /* eraseblock size */ + int mediatype; /* type of the media, 0 (nor) or 1 (nand) */ +} chfs_opt_t; + +#endif diff --git a/usr.sbin/makefs/ffs.c b/usr.sbin/makefs/ffs.c new file mode 100644 index 000000000..723c213b8 --- /dev/null +++ b/usr.sbin/makefs/ffs.c @@ -0,0 +1,1155 @@ +/* $NetBSD: ffs.c,v 1.63 2013/06/23 02:06:06 dholland Exp $ */ + +/* + * Copyright (c) 2001 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Luke Mewburn for Wasabi Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Copyright (c) 1982, 1986, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ffs_alloc.c 8.19 (Berkeley) 7/13/95 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: ffs.c,v 1.63 2013/06/23 02:06:06 dholland Exp $"); +#endif /* !__lint */ + +#include + +#if !HAVE_NBTOOL_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "makefs.h" +#include "ffs.h" + +#if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS +#include +#endif + +#include +#include +#include +#include + +#include "ffs/ufs_inode.h" +#include "ffs/newfs_extern.h" +#include "ffs/ffs_extern.h" + +#undef DIP +#define DIP(dp, field) \ + ((ffs_opts->version == 1) ? \ + (dp)->ffs1_din.di_##field : (dp)->ffs2_din.di_##field) + +/* + * Various file system defaults (cribbed from newfs(8)). + */ +#define DFL_FRAGSIZE 1024 /* fragment size */ +#define DFL_BLKSIZE 8192 /* block size */ +#define DFL_SECSIZE 512 /* sector size */ +#define DFL_CYLSPERGROUP 65536 /* cylinders per group */ +#define DFL_FRAGSPERINODE 4 /* fragments per inode */ +#define DFL_ROTDELAY 0 /* rotational delay */ +#define DFL_NRPOS 1 /* rotational positions */ +#define DFL_RPM 3600 /* rpm of disk */ +#define DFL_NSECTORS 64 /* # of sectors */ +#define DFL_NTRACKS 16 /* # of tracks */ + + +typedef struct { + u_char *buf; /* buf for directory */ + doff_t size; /* full size of buf */ + doff_t cur; /* offset of current entry */ +} dirbuf_t; + + +static int ffs_create_image(const char *, fsinfo_t *); +static void ffs_dump_fsinfo(fsinfo_t *); +static void ffs_dump_dirbuf(dirbuf_t *, const char *, int); +static void ffs_make_dirbuf(dirbuf_t *, const char *, fsnode *, int); +static int ffs_populate_dir(const char *, fsnode *, fsinfo_t *); +static void ffs_size_dir(fsnode *, fsinfo_t *); +static void ffs_validate(const char *, fsnode *, fsinfo_t *); +static void ffs_write_file(union dinode *, uint32_t, void *, fsinfo_t *); +static void ffs_write_inode(union dinode *, uint32_t, const fsinfo_t *); +static void *ffs_build_dinode1(struct ufs1_dinode *, dirbuf_t *, fsnode *, + fsnode *, fsinfo_t *); +static void *ffs_build_dinode2(struct ufs2_dinode *, dirbuf_t *, fsnode *, + fsnode *, fsinfo_t *); + + + + /* publically visible functions */ +void +ffs_prep_opts(fsinfo_t *fsopts) +{ + ffs_opt_t *ffs_opts = ecalloc(1, sizeof(*ffs_opts)); + + const option_t ffs_options[] = { + { 'b', "bsize", &ffs_opts->bsize, OPT_INT32, + 1, INT_MAX, "block size" }, + { 'f', "fsize", &ffs_opts->fsize, OPT_INT32, + 1, INT_MAX, "fragment size" }, + { 'd', "density", &ffs_opts->density, OPT_INT32, + 1, INT_MAX, "bytes per inode" }, + { 'm', "minfree", &ffs_opts->minfree, OPT_INT32, + 0, 99, "minfree" }, + { 'M', "maxbpf", &ffs_opts->maxbpg, OPT_INT32, + 1, INT_MAX, "max blocks per file in a cg" }, + { 'a', "avgfilesize", &ffs_opts->avgfilesize, OPT_INT32, + 1, INT_MAX, "expected average file size" }, + { 'n', "avgfpdir", &ffs_opts->avgfpdir, OPT_INT32, + 1, INT_MAX, "expected # of files per directory" }, + { 'x', "extent", &ffs_opts->maxbsize, OPT_INT32, + 1, INT_MAX, "maximum # extent size" }, + { 'g', "maxbpcg", &ffs_opts->maxblkspercg, OPT_INT32, + 1, INT_MAX, "max # of blocks per group" }, + { 'v', "version", &ffs_opts->version, OPT_INT32, + 1, 2, "UFS version" }, + { 'o', "optimization", NULL, OPT_STRBUF, + 0, 0, "Optimization (time|space)" }, + { 'l', "label", ffs_opts->label, OPT_STRARRAY, + 1, sizeof(ffs_opts->label), "UFS label" }, + { .name = NULL } + }; + + ffs_opts->bsize= -1; + ffs_opts->fsize= -1; + ffs_opts->cpg= -1; + ffs_opts->density= -1; + ffs_opts->minfree= -1; + ffs_opts->optimization= -1; + ffs_opts->maxcontig= -1; + ffs_opts->maxbpg= -1; + ffs_opts->avgfilesize= -1; + ffs_opts->avgfpdir= -1; + ffs_opts->version = 1; + + fsopts->fs_specific = ffs_opts; + fsopts->fs_options = copy_opts(ffs_options); +} + +void +ffs_cleanup_opts(fsinfo_t *fsopts) +{ + free(fsopts->fs_specific); + free(fsopts->fs_options); +} + +int +ffs_parse_opts(const char *option, fsinfo_t *fsopts) +{ + ffs_opt_t *ffs_opts = fsopts->fs_specific; + option_t *ffs_options = fsopts->fs_options; + char buf[1024]; + + int rv; + + assert(option != NULL); + assert(fsopts != NULL); + assert(ffs_opts != NULL); + + if (debug & DEBUG_FS_PARSE_OPTS) + printf("ffs_parse_opts: got `%s'\n", option); + + rv = set_option(ffs_options, option, buf, sizeof(buf)); + if (rv == -1) + return 0; + + if (ffs_options[rv].name == NULL) + abort(); + + switch (ffs_options[rv].letter) { + case 'o': + if (strcmp(buf, "time") == 0) { + ffs_opts->optimization = FS_OPTTIME; + } else if (strcmp(buf, "space") == 0) { + ffs_opts->optimization = FS_OPTSPACE; + } else { + warnx("Invalid optimization `%s'", buf); + return 0; + } + break; + default: + break; + } + return 1; +} + + +void +ffs_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts) +{ + struct fs *superblock; + struct timeval start; + + assert(image != NULL); + assert(dir != NULL); + assert(root != NULL); + assert(fsopts != NULL); + + if (debug & DEBUG_FS_MAKEFS) + printf("ffs_makefs: image %s directory %s root %p\n", + image, dir, root); + + /* validate tree and options */ + TIMER_START(start); + ffs_validate(dir, root, fsopts); + TIMER_RESULTS(start, "ffs_validate"); + + printf("Calculated size of `%s': %lld bytes, %lld inodes\n", + image, (long long)fsopts->size, (long long)fsopts->inodes); + + /* create image */ + TIMER_START(start); + if (ffs_create_image(image, fsopts) == -1) + errx(1, "Image file `%s' not created.", image); + TIMER_RESULTS(start, "ffs_create_image"); + + fsopts->curinode = UFS_ROOTINO; + + if (debug & DEBUG_FS_MAKEFS) + putchar('\n'); + + /* populate image */ + printf("Populating `%s'\n", image); + TIMER_START(start); + if (! ffs_populate_dir(dir, root, fsopts)) + errx(1, "Image file `%s' not populated.", image); + TIMER_RESULTS(start, "ffs_populate_dir"); + + /* ensure no outstanding buffers remain */ + if (debug & DEBUG_FS_MAKEFS) + bcleanup(); + + /* update various superblock parameters */ + superblock = fsopts->superblock; + superblock->fs_fmod = 0; + superblock->fs_old_cstotal.cs_ndir = superblock->fs_cstotal.cs_ndir; + superblock->fs_old_cstotal.cs_nbfree = superblock->fs_cstotal.cs_nbfree; + superblock->fs_old_cstotal.cs_nifree = superblock->fs_cstotal.cs_nifree; + superblock->fs_old_cstotal.cs_nffree = superblock->fs_cstotal.cs_nffree; + + /* write out superblock; image is now complete */ + ffs_write_superblock(fsopts->superblock, fsopts); + if (close(fsopts->fd) == -1) + err(1, "Closing `%s'", image); + fsopts->fd = -1; + printf("Image `%s' complete\n", image); +} + + /* end of public functions */ + + +static void +ffs_validate(const char *dir, fsnode *root, fsinfo_t *fsopts) +{ + int32_t ncg = 1; +#if notyet + int32_t spc, nspf, ncyl, fssize; +#endif + ffs_opt_t *ffs_opts = fsopts->fs_specific; + + assert(dir != NULL); + assert(root != NULL); + assert(fsopts != NULL); + assert(ffs_opts != NULL); + + if (debug & DEBUG_FS_VALIDATE) { + printf("ffs_validate: before defaults set:\n"); + ffs_dump_fsinfo(fsopts); + } + + /* set FFS defaults */ + if (fsopts->sectorsize == -1) + fsopts->sectorsize = DFL_SECSIZE; + if (ffs_opts->fsize == -1) + ffs_opts->fsize = MAX(DFL_FRAGSIZE, fsopts->sectorsize); + if (ffs_opts->bsize == -1) + ffs_opts->bsize = MIN(DFL_BLKSIZE, 8 * ffs_opts->fsize); + if (ffs_opts->cpg == -1) + ffs_opts->cpg = DFL_CYLSPERGROUP; + else + ffs_opts->cpgflg = 1; + /* fsopts->density is set below */ + if (ffs_opts->nsectors == -1) + ffs_opts->nsectors = DFL_NSECTORS; + if (ffs_opts->minfree == -1) + ffs_opts->minfree = MINFREE; + if (ffs_opts->optimization == -1) + ffs_opts->optimization = DEFAULTOPT; + if (ffs_opts->maxcontig == -1) + ffs_opts->maxcontig = + MAX(1, MIN(MAXPHYS, FFS_MAXBSIZE) / ffs_opts->bsize); + /* XXX ondisk32 */ + if (ffs_opts->maxbpg == -1) + ffs_opts->maxbpg = ffs_opts->bsize / sizeof(int32_t); + if (ffs_opts->avgfilesize == -1) + ffs_opts->avgfilesize = AVFILESIZ; + if (ffs_opts->avgfpdir == -1) + ffs_opts->avgfpdir = AFPDIR; + + /* calculate size of tree */ + ffs_size_dir(root, fsopts); + fsopts->inodes += UFS_ROOTINO; /* include first two inodes */ + + if (debug & DEBUG_FS_VALIDATE) + printf("ffs_validate: size of tree: %lld bytes, %lld inodes\n", + (long long)fsopts->size, (long long)fsopts->inodes); + + /* add requested slop */ + fsopts->size += fsopts->freeblocks; + fsopts->inodes += fsopts->freefiles; + if (fsopts->freefilepc > 0) + fsopts->inodes = + fsopts->inodes * (100 + fsopts->freefilepc) / 100; + if (fsopts->freeblockpc > 0) + fsopts->size = + fsopts->size * (100 + fsopts->freeblockpc) / 100; + + /* add space needed for superblocks */ + /* + * The old SBOFF (SBLOCK_UFS1) is used here because makefs is + * typically used for small filesystems where space matters. + * XXX make this an option. + */ + fsopts->size += (SBLOCK_UFS1 + SBLOCKSIZE) * ncg; + /* add space needed to store inodes, x3 for blockmaps, etc */ + if (ffs_opts->version == 1) + fsopts->size += ncg * DINODE1_SIZE * + roundup(fsopts->inodes / ncg, + ffs_opts->bsize / DINODE1_SIZE); + else + fsopts->size += ncg * DINODE2_SIZE * + roundup(fsopts->inodes / ncg, + ffs_opts->bsize / DINODE2_SIZE); + + /* add minfree */ + if (ffs_opts->minfree > 0) + fsopts->size = + fsopts->size * (100 + ffs_opts->minfree) / 100; + /* + * XXX any other fs slop to add, such as csum's, bitmaps, etc ?? + */ + + if (fsopts->size < fsopts->minsize) /* ensure meets minimum size */ + fsopts->size = fsopts->minsize; + + /* round up to the next block */ + fsopts->size = roundup(fsopts->size, ffs_opts->bsize); + + /* calculate density if necessary */ + if (ffs_opts->density == -1) + ffs_opts->density = fsopts->size / fsopts->inodes + 1; + + if (debug & DEBUG_FS_VALIDATE) { + printf("ffs_validate: after defaults set:\n"); + ffs_dump_fsinfo(fsopts); + printf("ffs_validate: dir %s; %lld bytes, %lld inodes\n", + dir, (long long)fsopts->size, (long long)fsopts->inodes); + } + /* now check calculated sizes vs requested sizes */ + if (fsopts->maxsize > 0 && fsopts->size > fsopts->maxsize) { + errx(1, "`%s' size of %lld is larger than the maxsize of %lld.", + dir, (long long)fsopts->size, (long long)fsopts->maxsize); + } +} + + +static void +ffs_dump_fsinfo(fsinfo_t *f) +{ + + ffs_opt_t *fs = f->fs_specific; + + printf("fsopts at %p\n", f); + + printf("\tsize %lld, inodes %lld, curinode %u\n", + (long long)f->size, (long long)f->inodes, f->curinode); + + printf("\tminsize %lld, maxsize %lld\n", + (long long)f->minsize, (long long)f->maxsize); + printf("\tfree files %lld, freefile %% %d\n", + (long long)f->freefiles, f->freefilepc); + printf("\tfree blocks %lld, freeblock %% %d\n", + (long long)f->freeblocks, f->freeblockpc); + printf("\tneedswap %d, sectorsize %d\n", f->needswap, f->sectorsize); + + printf("\tbsize %d, fsize %d, cpg %d, density %d\n", + fs->bsize, fs->fsize, fs->cpg, fs->density); + printf("\tnsectors %d, rpm %d, minfree %d\n", + fs->nsectors, fs->rpm, fs->minfree); + printf("\tmaxcontig %d, maxbpg %d\n", + fs->maxcontig, fs->maxbpg); + printf("\toptimization %s\n", + fs->optimization == FS_OPTSPACE ? "space" : "time"); +} + + +static int +ffs_create_image(const char *image, fsinfo_t *fsopts) +{ +#if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS + struct statvfs sfs; +#endif + struct fs *fs; + char *buf; + int i, bufsize; + off_t bufrem; + int oflags = O_RDWR | O_CREAT; + + assert (image != NULL); + assert (fsopts != NULL); + + /* create image */ + if (fsopts->offset == 0) + oflags |= O_TRUNC; + if ((fsopts->fd = open(image, oflags, 0666)) == -1) { + warn("Can't open `%s' for writing", image); + return (-1); + } + + /* zero image */ +#if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS + if (fstatvfs(fsopts->fd, &sfs) == -1) { +#endif + bufsize = 8192; +#if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS + warn("can't fstatvfs `%s', using default %d byte chunk", + image, bufsize); + } else + bufsize = sfs.f_iosize; +#endif + bufrem = fsopts->size; + + if (fsopts->sparse) { + if (ftruncate(fsopts->fd, bufrem) == -1) { + printf ("ERROR in truncate. Sparse option disabled\n"); + fsopts->sparse = 0; + } else { + bufrem = 0; /* File truncated at bufrem. Remaining is 0 */ + buf = NULL; + } + } + + if (fsopts->offset != 0) + if (lseek(fsopts->fd, fsopts->offset, SEEK_SET) == -1) { + warn("can't seek"); + return -1; + } + + if ((debug & DEBUG_FS_CREATE_IMAGE) && fsopts->sparse == 0) + printf( + "zero-ing image `%s', %lld sectors, using %d byte chunks\n", + image, (long long)bufrem, bufsize); + if (bufrem > 0) + buf = ecalloc(1, bufsize); + while (bufrem > 0) { + i = write(fsopts->fd, buf, MIN(bufsize, bufrem)); + if (i == -1) { + warn("zeroing image, %lld bytes to go", + (long long)bufrem); + free(buf); + return (-1); + } + bufrem -= i; + } + if (buf) + free(buf); + + /* make the file system */ + if (debug & DEBUG_FS_CREATE_IMAGE) + printf("calling mkfs(\"%s\", ...)\n", image); + fs = ffs_mkfs(image, fsopts); + fsopts->superblock = (void *)fs; + if (debug & DEBUG_FS_CREATE_IMAGE) { + time_t t; + + t = (time_t)((struct fs *)fsopts->superblock)->fs_time; + printf("mkfs returned %p; fs_time %s", + fsopts->superblock, ctime(&t)); + printf("fs totals: nbfree %lld, nffree %lld, nifree %lld, ndir %lld\n", + (long long)fs->fs_cstotal.cs_nbfree, + (long long)fs->fs_cstotal.cs_nffree, + (long long)fs->fs_cstotal.cs_nifree, + (long long)fs->fs_cstotal.cs_ndir); + } + + if ((off_t)(fs->fs_cstotal.cs_nifree + UFS_ROOTINO) < fsopts->inodes) { + warnx( + "Image file `%s' has %lld free inodes; %lld are required.", + image, + (long long)(fs->fs_cstotal.cs_nifree + UFS_ROOTINO), + (long long)fsopts->inodes); + return (-1); + } + return (fsopts->fd); +} + + +static void +ffs_size_dir(fsnode *root, fsinfo_t *fsopts) +{ + struct direct tmpdir; + fsnode * node; + int curdirsize, this; + ffs_opt_t *ffs_opts = fsopts->fs_specific; + + /* node may be NULL (empty directory) */ + assert(fsopts != NULL); + assert(ffs_opts != NULL); + + if (debug & DEBUG_FS_SIZE_DIR) + printf("ffs_size_dir: entry: bytes %lld inodes %lld\n", + (long long)fsopts->size, (long long)fsopts->inodes); + +#define ADDDIRENT(e) do { \ + tmpdir.d_namlen = strlen((e)); \ + this = UFS_DIRSIZ(0, &tmpdir, 0); \ + if (debug & DEBUG_FS_SIZE_DIR_ADD_DIRENT) \ + printf("ADDDIRENT: was: %s (%d) this %d cur %d\n", \ + e, tmpdir.d_namlen, this, curdirsize); \ + if (this + curdirsize > roundup(curdirsize, UFS_DIRBLKSIZ)) \ + curdirsize = roundup(curdirsize, UFS_DIRBLKSIZ); \ + curdirsize += this; \ + if (debug & DEBUG_FS_SIZE_DIR_ADD_DIRENT) \ + printf("ADDDIRENT: now: %s (%d) this %d cur %d\n", \ + e, tmpdir.d_namlen, this, curdirsize); \ +} while (0); + + /* + * XXX this needs to take into account extra space consumed + * by indirect blocks, etc. + */ +#define ADDSIZE(x) do { \ + fsopts->size += roundup((x), ffs_opts->fsize); \ +} while (0); + + curdirsize = 0; + for (node = root; node != NULL; node = node->next) { + ADDDIRENT(node->name); + if (node == root) { /* we're at "." */ + assert(strcmp(node->name, ".") == 0); + ADDDIRENT(".."); + } else if ((node->inode->flags & FI_SIZED) == 0) { + /* don't count duplicate names */ + node->inode->flags |= FI_SIZED; + if (debug & DEBUG_FS_SIZE_DIR_NODE) + printf("ffs_size_dir: `%s' size %lld\n", + node->name, + (long long)node->inode->st.st_size); + fsopts->inodes++; + if (node->type == S_IFREG) + ADDSIZE(node->inode->st.st_size); + if (node->type == S_IFLNK) { + size_t slen; + + slen = strlen(node->symlink) + 1; + if (slen >= (ffs_opts->version == 1 ? + UFS1_MAXSYMLINKLEN : + UFS2_MAXSYMLINKLEN)) + ADDSIZE(slen); + } + } + if (node->type == S_IFDIR) + ffs_size_dir(node->child, fsopts); + } + ADDSIZE(curdirsize); + + if (debug & DEBUG_FS_SIZE_DIR) + printf("ffs_size_dir: exit: size %lld inodes %lld\n", + (long long)fsopts->size, (long long)fsopts->inodes); +} + +static void * +ffs_build_dinode1(struct ufs1_dinode *dinp, dirbuf_t *dbufp, fsnode *cur, + fsnode *root, fsinfo_t *fsopts) +{ + size_t slen; + void *membuf; + + memset(dinp, 0, sizeof(*dinp)); + dinp->di_mode = cur->inode->st.st_mode; + dinp->di_nlink = cur->inode->nlink; + dinp->di_size = cur->inode->st.st_size; + dinp->di_atime = cur->inode->st.st_atime; + dinp->di_mtime = cur->inode->st.st_mtime; + dinp->di_ctime = cur->inode->st.st_ctime; +#if HAVE_STRUCT_STAT_ST_MTIMENSEC + dinp->di_atimensec = cur->inode->st.st_atimensec; + dinp->di_mtimensec = cur->inode->st.st_mtimensec; + dinp->di_ctimensec = cur->inode->st.st_ctimensec; +#endif +#if HAVE_STRUCT_STAT_ST_FLAGS + dinp->di_flags = cur->inode->st.st_flags; +#endif +#if HAVE_STRUCT_STAT_ST_GEN + dinp->di_gen = cur->inode->st.st_gen; +#endif + dinp->di_uid = cur->inode->st.st_uid; + dinp->di_gid = cur->inode->st.st_gid; + /* not set: di_db, di_ib, di_blocks, di_spare */ + + membuf = NULL; + if (cur == root) { /* "."; write dirbuf */ + membuf = dbufp->buf; + dinp->di_size = dbufp->size; + } else if (S_ISBLK(cur->type) || S_ISCHR(cur->type)) { + dinp->di_size = 0; /* a device */ + dinp->di_rdev = + ufs_rw32(cur->inode->st.st_rdev, fsopts->needswap); + } else if (S_ISLNK(cur->type)) { /* symlink */ + slen = strlen(cur->symlink); + if (slen < UFS1_MAXSYMLINKLEN) { /* short link */ + memcpy(dinp->di_db, cur->symlink, slen); + } else + membuf = cur->symlink; + dinp->di_size = slen; + } + return membuf; +} + +static void * +ffs_build_dinode2(struct ufs2_dinode *dinp, dirbuf_t *dbufp, fsnode *cur, + fsnode *root, fsinfo_t *fsopts) +{ + size_t slen; + void *membuf; + + memset(dinp, 0, sizeof(*dinp)); + dinp->di_mode = cur->inode->st.st_mode; + dinp->di_nlink = cur->inode->nlink; + dinp->di_size = cur->inode->st.st_size; + dinp->di_atime = cur->inode->st.st_atime; + dinp->di_mtime = cur->inode->st.st_mtime; + dinp->di_ctime = cur->inode->st.st_ctime; +#if HAVE_STRUCT_STAT_ST_MTIMENSEC + dinp->di_atimensec = cur->inode->st.st_atimensec; + dinp->di_mtimensec = cur->inode->st.st_mtimensec; + dinp->di_ctimensec = cur->inode->st.st_ctimensec; +#endif +#if HAVE_STRUCT_STAT_ST_FLAGS + dinp->di_flags = cur->inode->st.st_flags; +#endif +#if HAVE_STRUCT_STAT_ST_GEN + dinp->di_gen = cur->inode->st.st_gen; +#endif +#if HAVE_STRUCT_STAT_BIRTHTIME + dinp->di_birthtime = cur->inode->st.st_birthtime; + dinp->di_birthnsec = cur->inode->st.st_birthtimensec; +#endif + dinp->di_uid = cur->inode->st.st_uid; + dinp->di_gid = cur->inode->st.st_gid; + /* not set: di_db, di_ib, di_blocks, di_spare */ + + membuf = NULL; + if (cur == root) { /* "."; write dirbuf */ + membuf = dbufp->buf; + dinp->di_size = dbufp->size; + } else if (S_ISBLK(cur->type) || S_ISCHR(cur->type)) { + dinp->di_size = 0; /* a device */ + dinp->di_rdev = + ufs_rw64(cur->inode->st.st_rdev, fsopts->needswap); + } else if (S_ISLNK(cur->type)) { /* symlink */ + slen = strlen(cur->symlink); + if (slen < UFS2_MAXSYMLINKLEN) { /* short link */ + memcpy(dinp->di_db, cur->symlink, slen); + } else + membuf = cur->symlink; + dinp->di_size = slen; + } + return membuf; +} + +static int +ffs_populate_dir(const char *dir, fsnode *root, fsinfo_t *fsopts) +{ + fsnode *cur; + dirbuf_t dirbuf; + union dinode din; + void *membuf; + char path[MAXPATHLEN + 1]; + ffs_opt_t *ffs_opts = fsopts->fs_specific; + + assert(dir != NULL); + assert(root != NULL); + assert(fsopts != NULL); + assert(ffs_opts != NULL); + + (void)memset(&dirbuf, 0, sizeof(dirbuf)); + + if (debug & DEBUG_FS_POPULATE) + printf("ffs_populate_dir: PASS 1 dir %s node %p\n", dir, root); + + /* + * pass 1: allocate inode numbers, build directory `file' + */ + for (cur = root; cur != NULL; cur = cur->next) { + if ((cur->inode->flags & FI_ALLOCATED) == 0) { + cur->inode->flags |= FI_ALLOCATED; + if (cur == root && cur->parent != NULL) + cur->inode->ino = cur->parent->inode->ino; + else { + cur->inode->ino = fsopts->curinode; + fsopts->curinode++; + } + } + ffs_make_dirbuf(&dirbuf, cur->name, cur, fsopts->needswap); + if (cur == root) { /* we're at "."; add ".." */ + ffs_make_dirbuf(&dirbuf, "..", + cur->parent == NULL ? cur : cur->parent->first, + fsopts->needswap); + root->inode->nlink++; /* count my parent's link */ + } else if (cur->child != NULL) + root->inode->nlink++; /* count my child's link */ + + /* + * XXX possibly write file and long symlinks here, + * ensuring that blocks get written before inodes? + * otoh, this isn't a real filesystem, so who + * cares about ordering? :-) + */ + } + if (debug & DEBUG_FS_POPULATE_DIRBUF) + ffs_dump_dirbuf(&dirbuf, dir, fsopts->needswap); + + /* + * pass 2: write out dirbuf, then non-directories at this level + */ + if (debug & DEBUG_FS_POPULATE) + printf("ffs_populate_dir: PASS 2 dir %s\n", dir); + for (cur = root; cur != NULL; cur = cur->next) { + if (cur->inode->flags & FI_WRITTEN) + continue; /* skip hard-linked entries */ + cur->inode->flags |= FI_WRITTEN; + + if ((size_t)snprintf(path, sizeof(path), "%s/%s/%s", cur->root, + cur->path, cur->name) >= sizeof(path)) + errx(1, "Pathname too long."); + + if (cur->child != NULL) + continue; /* child creates own inode */ + + /* build on-disk inode */ + if (ffs_opts->version == 1) + membuf = ffs_build_dinode1(&din.ffs1_din, &dirbuf, cur, + root, fsopts); + else + membuf = ffs_build_dinode2(&din.ffs2_din, &dirbuf, cur, + root, fsopts); + + if (debug & DEBUG_FS_POPULATE_NODE) { + printf("ffs_populate_dir: writing ino %d, %s", + cur->inode->ino, inode_type(cur->type)); + if (cur->inode->nlink > 1) + printf(", nlink %d", cur->inode->nlink); + putchar('\n'); + } + + if (membuf != NULL) { + ffs_write_file(&din, cur->inode->ino, membuf, fsopts); + } else if (S_ISREG(cur->type)) { + ffs_write_file(&din, cur->inode->ino, path, fsopts); + } else { + assert (! S_ISDIR(cur->type)); + ffs_write_inode(&din, cur->inode->ino, fsopts); + } + } + + /* + * pass 3: write out sub-directories + */ + if (debug & DEBUG_FS_POPULATE) + printf("ffs_populate_dir: PASS 3 dir %s\n", dir); + for (cur = root; cur != NULL; cur = cur->next) { + if (cur->child == NULL) + continue; + if ((size_t)snprintf(path, sizeof(path), "%s/%s", dir, + cur->name) >= sizeof(path)) + errx(1, "Pathname too long."); + if (! ffs_populate_dir(path, cur->child, fsopts)) + return (0); + } + + if (debug & DEBUG_FS_POPULATE) + printf("ffs_populate_dir: DONE dir %s\n", dir); + + /* cleanup */ + if (dirbuf.buf != NULL) + free(dirbuf.buf); + return (1); +} + + +static void +ffs_write_file(union dinode *din, uint32_t ino, void *buf, fsinfo_t *fsopts) +{ + int isfile, ffd; + char *fbuf, *p; + off_t bufleft, chunk, offset; + ssize_t nread; + struct inode in; + struct buf * bp; + ffs_opt_t *ffs_opts = fsopts->fs_specific; + struct vnode vp = { fsopts, NULL }; + + assert (din != NULL); + assert (buf != NULL); + assert (fsopts != NULL); + assert (ffs_opts != NULL); + + isfile = S_ISREG(DIP(din, mode)); + fbuf = NULL; + ffd = -1; + p = NULL; + + in.i_fs = (struct fs *)fsopts->superblock; + in.i_devvp = &vp; + + if (debug & DEBUG_FS_WRITE_FILE) { + printf( + "ffs_write_file: ino %u, din %p, isfile %d, %s, size %lld", + ino, din, isfile, inode_type(DIP(din, mode) & S_IFMT), + (long long)DIP(din, size)); + if (isfile) + printf(", file '%s'\n", (char *)buf); + else + printf(", buffer %p\n", buf); + } + + in.i_number = ino; + in.i_size = DIP(din, size); + if (ffs_opts->version == 1) + memcpy(&in.i_din.ffs1_din, &din->ffs1_din, + sizeof(in.i_din.ffs1_din)); + else + memcpy(&in.i_din.ffs2_din, &din->ffs2_din, + sizeof(in.i_din.ffs2_din)); + + if (DIP(din, size) == 0) + goto write_inode_and_leave; /* mmm, cheating */ + + if (isfile) { + fbuf = emalloc(ffs_opts->bsize); + if ((ffd = open((char *)buf, O_RDONLY, 0444)) == -1) { + warn("Can't open `%s' for reading", (char *)buf); + goto leave_ffs_write_file; + } + } else { + p = buf; + } + + chunk = 0; + for (bufleft = DIP(din, size); bufleft > 0; bufleft -= chunk) { + chunk = MIN(bufleft, ffs_opts->bsize); + if (!isfile) + ; + else if ((nread = read(ffd, fbuf, chunk)) == -1) + err(EXIT_FAILURE, "Reading `%s', %lld bytes to go", + (char *)buf, (long long)bufleft); + else if (nread != chunk) + errx(EXIT_FAILURE, "Reading `%s', %lld bytes to go, " + "read %zd bytes, expected %ju bytes, does " + "metalog size= attribute mismatch source size?", + (char *)buf, (long long)bufleft, nread, + (uintmax_t)chunk); + else + p = fbuf; + offset = DIP(din, size) - bufleft; + if (debug & DEBUG_FS_WRITE_FILE_BLOCK) + printf( + "ffs_write_file: write %p offset %lld size %lld left %lld\n", + p, (long long)offset, + (long long)chunk, (long long)bufleft); + /* + * XXX if holey support is desired, do the check here + * + * XXX might need to write out last bit in fragroundup + * sized chunk. however, ffs_balloc() handles this for us + */ + errno = ffs_balloc(&in, offset, chunk, &bp); + bad_ffs_write_file: + if (errno != 0) + err(1, + "Writing inode %d (%s), bytes %lld + %lld", + ino, + isfile ? (char *)buf : + inode_type(DIP(din, mode) & S_IFMT), + (long long)offset, (long long)chunk); + memcpy(bp->b_data, p, chunk); + errno = bwrite(bp); + if (errno != 0) + goto bad_ffs_write_file; + if (!isfile) + p += chunk; + } + + write_inode_and_leave: + ffs_write_inode(&in.i_din, in.i_number, fsopts); + + leave_ffs_write_file: + if (fbuf) + free(fbuf); + if (ffd != -1) + close(ffd); +} + + +static void +ffs_dump_dirbuf(dirbuf_t *dbuf, const char *dir, int needswap) +{ + doff_t i; + struct direct *de; + uint16_t reclen; + + assert (dbuf != NULL); + assert (dir != NULL); + printf("ffs_dump_dirbuf: dir %s size %d cur %d\n", + dir, dbuf->size, dbuf->cur); + + for (i = 0; i < dbuf->size; ) { + de = (struct direct *)(dbuf->buf + i); + reclen = ufs_rw16(de->d_reclen, needswap); + printf( + " inode %4d %7s offset %4d reclen %3d namlen %3d name %s\n", + ufs_rw32(de->d_fileno, needswap), + inode_type(DTTOIF(de->d_type)), i, reclen, + de->d_namlen, de->d_name); + i += reclen; + assert(reclen > 0); + } +} + +static void +ffs_make_dirbuf(dirbuf_t *dbuf, const char *name, fsnode *node, int needswap) +{ + struct direct de, *dp; + uint16_t llen, reclen; + u_char *newbuf; + + assert (dbuf != NULL); + assert (name != NULL); + assert (node != NULL); + /* create direct entry */ + (void)memset(&de, 0, sizeof(de)); + de.d_fileno = ufs_rw32(node->inode->ino, needswap); + de.d_type = IFTODT(node->type); + de.d_namlen = (uint8_t)strlen(name); + strcpy(de.d_name, name); + reclen = UFS_DIRSIZ(0, &de, needswap); + de.d_reclen = ufs_rw16(reclen, needswap); + + dp = (struct direct *)(dbuf->buf + dbuf->cur); + llen = 0; + if (dp != NULL) + llen = UFS_DIRSIZ(0, dp, needswap); + + if (debug & DEBUG_FS_MAKE_DIRBUF) + printf( + "ffs_make_dirbuf: dbuf siz %d cur %d lastlen %d\n" + " ino %d type %d reclen %d namlen %d name %.30s\n", + dbuf->size, dbuf->cur, llen, + ufs_rw32(de.d_fileno, needswap), de.d_type, reclen, + de.d_namlen, de.d_name); + + if (reclen + dbuf->cur + llen > roundup(dbuf->size, UFS_DIRBLKSIZ)) { + if (debug & DEBUG_FS_MAKE_DIRBUF) + printf("ffs_make_dirbuf: growing buf to %d\n", + dbuf->size + UFS_DIRBLKSIZ); + newbuf = erealloc(dbuf->buf, dbuf->size + UFS_DIRBLKSIZ); + dbuf->buf = newbuf; + dbuf->size += UFS_DIRBLKSIZ; + memset(dbuf->buf + dbuf->size - UFS_DIRBLKSIZ, 0, UFS_DIRBLKSIZ); + dbuf->cur = dbuf->size - UFS_DIRBLKSIZ; + } else if (dp) { /* shrink end of previous */ + dp->d_reclen = ufs_rw16(llen,needswap); + dbuf->cur += llen; + } + dp = (struct direct *)(dbuf->buf + dbuf->cur); + memcpy(dp, &de, reclen); + dp->d_reclen = ufs_rw16(dbuf->size - dbuf->cur, needswap); +} + +/* + * cribbed from sys/ufs/ffs/ffs_alloc.c + */ +static void +ffs_write_inode(union dinode *dp, uint32_t ino, const fsinfo_t *fsopts) +{ + char *buf; + struct ufs1_dinode *dp1; + struct ufs2_dinode *dp2, *dip; + struct cg *cgp; + struct fs *fs; + int cg, cgino, i; + daddr_t d; + char sbbuf[FFS_MAXBSIZE]; + uint32_t initediblk; + ffs_opt_t *ffs_opts = fsopts->fs_specific; + + assert (dp != NULL); + assert (ino > 0); + assert (fsopts != NULL); + assert (ffs_opts != NULL); + + fs = (struct fs *)fsopts->superblock; + cg = ino_to_cg(fs, ino); + cgino = ino % fs->fs_ipg; + if (debug & DEBUG_FS_WRITE_INODE) + printf("ffs_write_inode: din %p ino %u cg %d cgino %d\n", + dp, ino, cg, cgino); + + ffs_rdfs(FFS_FSBTODB(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, &sbbuf, + fsopts); + cgp = (struct cg *)sbbuf; + if (!cg_chkmagic(cgp, fsopts->needswap)) + errx(1, "ffs_write_inode: cg %d: bad magic number", cg); + + assert (isclr(cg_inosused(cgp, fsopts->needswap), cgino)); + + buf = emalloc(fs->fs_bsize); + dp1 = (struct ufs1_dinode *)buf; + dp2 = (struct ufs2_dinode *)buf; + + if (fs->fs_cstotal.cs_nifree == 0) + errx(1, "ffs_write_inode: fs out of inodes for ino %u", + ino); + if (fs->fs_cs(fs, cg).cs_nifree == 0) + errx(1, + "ffs_write_inode: cg %d out of inodes for ino %u", + cg, ino); + setbit(cg_inosused(cgp, fsopts->needswap), cgino); + ufs_add32(cgp->cg_cs.cs_nifree, -1, fsopts->needswap); + fs->fs_cstotal.cs_nifree--; + fs->fs_cs(fs, cg).cs_nifree--; + if (S_ISDIR(DIP(dp, mode))) { + ufs_add32(cgp->cg_cs.cs_ndir, 1, fsopts->needswap); + fs->fs_cstotal.cs_ndir++; + fs->fs_cs(fs, cg).cs_ndir++; + } + + /* + * Initialize inode blocks on the fly for UFS2. + */ + initediblk = ufs_rw32(cgp->cg_initediblk, fsopts->needswap); + if (ffs_opts->version == 2 && + (uint32_t)(cgino + FFS_INOPB(fs)) > initediblk && + initediblk < ufs_rw32(cgp->cg_niblk, fsopts->needswap)) { + memset(buf, 0, fs->fs_bsize); + dip = (struct ufs2_dinode *)buf; + srandom(time(NULL)); + for (i = 0; i < FFS_INOPB(fs); i++) { + dip->di_gen = random() / 2 + 1; + dip++; + } + ffs_wtfs(FFS_FSBTODB(fs, ino_to_fsba(fs, + cg * fs->fs_ipg + initediblk)), + fs->fs_bsize, buf, fsopts); + initediblk += FFS_INOPB(fs); + cgp->cg_initediblk = ufs_rw32(initediblk, fsopts->needswap); + } + + + ffs_wtfs(FFS_FSBTODB(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, &sbbuf, + fsopts); + + /* now write inode */ + d = FFS_FSBTODB(fs, ino_to_fsba(fs, ino)); + ffs_rdfs(d, fs->fs_bsize, buf, fsopts); + if (fsopts->needswap) { + if (ffs_opts->version == 1) + ffs_dinode1_swap(&dp->ffs1_din, + &dp1[ino_to_fsbo(fs, ino)]); + else + ffs_dinode2_swap(&dp->ffs2_din, + &dp2[ino_to_fsbo(fs, ino)]); + } else { + if (ffs_opts->version == 1) + dp1[ino_to_fsbo(fs, ino)] = dp->ffs1_din; + else + dp2[ino_to_fsbo(fs, ino)] = dp->ffs2_din; + } + ffs_wtfs(d, fs->fs_bsize, buf, fsopts); + free(buf); +} + +void +panic(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vwarnx(fmt, ap); + va_end(ap); + exit(1); +} diff --git a/usr.sbin/makefs/ffs.h b/usr.sbin/makefs/ffs.h new file mode 100644 index 000000000..79223bd54 --- /dev/null +++ b/usr.sbin/makefs/ffs.h @@ -0,0 +1,68 @@ +/* $NetBSD: ffs.h,v 1.2 2011/10/09 21:33:43 christos Exp $ */ + +/* + * Copyright (c) 2001-2003 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Luke Mewburn for Wasabi Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FFS_H +#define _FFS_H + +#include +#include + +typedef struct { + char label[MAXVOLLEN]; /* volume name/label */ + int bsize; /* block size */ + int fsize; /* fragment size */ + int cpg; /* cylinders per group */ + int cpgflg; /* cpg was specified by user */ + int density; /* bytes per inode */ + int ntracks; /* number of tracks */ + int nsectors; /* number of sectors */ + int rpm; /* rpm */ + int minfree; /* free space threshold */ + int optimization; /* optimization (space or time) */ + int maxcontig; /* max contiguous blocks to allocate */ + int rotdelay; /* rotational delay between blocks */ + int maxbpg; /* maximum blocks per file in a cyl group */ + int nrpos; /* # of distinguished rotational positions */ + int avgfilesize; /* expected average file size */ + int avgfpdir; /* expected # of files per directory */ + int version; /* filesystem version (1 = FFS, 2 = UFS2) */ + int maxbsize; /* maximum extent size */ + int maxblkspercg; /* max # of blocks per cylinder group */ + /* XXX: support `old' file systems ? */ +} ffs_opt_t; + +#endif /* _FFS_H */ diff --git a/usr.sbin/makefs/ffs/Makefile.inc b/usr.sbin/makefs/ffs/Makefile.inc new file mode 100644 index 000000000..d13bcb358 --- /dev/null +++ b/usr.sbin/makefs/ffs/Makefile.inc @@ -0,0 +1,7 @@ +# $NetBSD: Makefile.inc,v 1.1 2009/01/16 19:39:52 pooka Exp $ +# + +.PATH: ${.CURDIR}/ffs ${NETBSDSRCDIR}/sys/ufs/ffs + +SRCS+= ffs_alloc.c ffs_balloc.c ffs_bswap.c ffs_subr.c ffs_tables.c ufs_bmap.c +SRCS+= buf.c mkfs.c diff --git a/usr.sbin/makefs/ffs/buf.c b/usr.sbin/makefs/ffs/buf.c new file mode 100644 index 000000000..1917c3ac0 --- /dev/null +++ b/usr.sbin/makefs/ffs/buf.c @@ -0,0 +1,220 @@ +/* $NetBSD: buf.c,v 1.21 2013/02/03 03:21:21 christos Exp $ */ + +/* + * Copyright (c) 2001 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Luke Mewburn for Wasabi Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: buf.c,v 1.21 2013/02/03 03:21:21 christos Exp $"); +#endif /* !__lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "makefs.h" +#include "buf.h" + +TAILQ_HEAD(buftailhead,buf) buftail; + +int +bread(struct vnode *vp, daddr_t blkno, int size, struct kauth_cred *u1 __unused, + int u2 __unused, struct buf **bpp) +{ + off_t offset; + ssize_t rv; + fsinfo_t *fs = vp->fs; + + assert (bpp != NULL); + + if (debug & DEBUG_BUF_BREAD) + printf("bread: blkno %lld size %d\n", (long long)blkno, size); + *bpp = getblk(vp, blkno, size, 0, 0); + offset = (*bpp)->b_blkno * fs->sectorsize + fs->offset; + if (debug & DEBUG_BUF_BREAD) + printf("bread: blkno %lld offset %lld bcount %ld\n", + (long long)(*bpp)->b_blkno, (long long) offset, + (*bpp)->b_bcount); + if (lseek((*bpp)->b_fs->fd, offset, SEEK_SET) == -1) + err(1, "%s: lseek %lld (%lld)", __func__, + (long long)(*bpp)->b_blkno, (long long)offset); + rv = read((*bpp)->b_fs->fd, (*bpp)->b_data, (*bpp)->b_bcount); + if (debug & DEBUG_BUF_BREAD) + printf("bread: read %ld (%lld) returned %zd\n", + (*bpp)->b_bcount, (long long)offset, rv); + if (rv == -1) /* read error */ + err(1, "%s: read %ld (%lld) returned %zd", __func__, + (*bpp)->b_bcount, (long long)offset, rv); + else if (rv != (*bpp)->b_bcount) /* short read */ + err(1, "%s: read %ld (%lld) returned %zd", __func__, + (*bpp)->b_bcount, (long long)offset, rv); + else + return (0); +} + +void +brelse(struct buf *bp, int u1 __unused) +{ + + assert (bp != NULL); + assert (bp->b_data != NULL); + + if (bp->b_lblkno < 0) { + /* + * XXX don't remove any buffers with negative logical block + * numbers (lblkno), so that we retain the mapping + * of negative lblkno -> real blkno that ffs_balloc() + * sets up. + * + * if we instead released these buffers, and implemented + * ufs_strategy() (and ufs_bmaparray()) and called those + * from bread() and bwrite() to convert the lblkno to + * a real blkno, we'd add a lot more code & complexity + * and reading off disk, for little gain, because this + * simple hack works for our purpose. + */ + bp->b_bcount = 0; + return; + } + + TAILQ_REMOVE(&buftail, bp, b_tailq); + free(bp->b_data); + free(bp); +} + +int +bwrite(struct buf *bp) +{ + off_t offset; + ssize_t rv; + int bytes; + fsinfo_t *fs = bp->b_fs; + + assert (bp != NULL); + offset = bp->b_blkno * fs->sectorsize + fs->offset; + bytes = bp->b_bcount; + if (debug & DEBUG_BUF_BWRITE) + printf("bwrite: blkno %lld offset %lld bcount %d\n", + (long long)bp->b_blkno, (long long) offset, bytes); + if (lseek(bp->b_fs->fd, offset, SEEK_SET) == -1) + return (errno); + rv = write(bp->b_fs->fd, bp->b_data, bytes); + if (debug & DEBUG_BUF_BWRITE) + printf("bwrite: write %ld (offset %lld) returned %lld\n", + bp->b_bcount, (long long)offset, (long long)rv); + brelse(bp, 0); + if (rv == bytes) + return (0); + else if (rv == -1) /* write error */ + return (errno); + else /* short write ? */ + return (EAGAIN); +} + +void +bcleanup(void) +{ + struct buf *bp; + + /* + * XXX this really shouldn't be necessary, but i'm curious to + * know why there's still some buffers lying around that + * aren't brelse()d + */ + + if (TAILQ_EMPTY(&buftail)) + return; + + printf("bcleanup: unflushed buffers:\n"); + TAILQ_FOREACH(bp, &buftail, b_tailq) { + printf("\tlblkno %10lld blkno %10lld count %6ld bufsize %6ld\n", + (long long)bp->b_lblkno, (long long)bp->b_blkno, + bp->b_bcount, bp->b_bufsize); + } + printf("bcleanup: done\n"); +} + +struct buf * +getblk(struct vnode *vp, daddr_t blkno, int size, int u1 __unused, + int u2 __unused) +{ + static int buftailinitted; + struct buf *bp; + void *n; + + if (debug & DEBUG_BUF_GETBLK) + printf("getblk: blkno %lld size %d\n", (long long)blkno, size); + + bp = NULL; + if (!buftailinitted) { + if (debug & DEBUG_BUF_GETBLK) + printf("getblk: initialising tailq\n"); + TAILQ_INIT(&buftail); + buftailinitted = 1; + } else { + TAILQ_FOREACH(bp, &buftail, b_tailq) { + if (bp->b_lblkno != blkno) + continue; + break; + } + } + if (bp == NULL) { + bp = ecalloc(1, sizeof(*bp)); + bp->b_bufsize = 0; + bp->b_blkno = bp->b_lblkno = blkno; + bp->b_fs = vp->fs; + bp->b_data = NULL; + TAILQ_INSERT_HEAD(&buftail, bp, b_tailq); + } + bp->b_bcount = size; + if (bp->b_data == NULL || bp->b_bcount > bp->b_bufsize) { + n = erealloc(bp->b_data, size); + memset(n, 0, size); + bp->b_data = n; + bp->b_bufsize = size; + } + + return (bp); +} diff --git a/usr.sbin/makefs/ffs/buf.h b/usr.sbin/makefs/ffs/buf.h new file mode 100644 index 000000000..8fd689334 --- /dev/null +++ b/usr.sbin/makefs/ffs/buf.h @@ -0,0 +1,117 @@ +/* $NetBSD: buf.h,v 1.9 2013/01/30 19:19:19 christos Exp $ */ + +/* + * Copyright (c) 2001 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Luke Mewburn for Wasabi Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FFS_BUF_H +#define _FFS_BUF_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +struct componentname { + char *cn_nameptr; + size_t cn_namelen; +}; + +struct makefs_fsinfo; +struct vnode { + struct makefs_fsinfo *fs; + void *v_data; +}; + +#define vput(a) ((void)(a)) + +struct buf { + void * b_data; + long b_bufsize; + long b_bcount; + daddr_t b_blkno; + daddr_t b_lblkno; + struct makefs_fsinfo * b_fs; + + TAILQ_ENTRY(buf) b_tailq; +}; + +struct kauth_cred; +void bcleanup(void); +int bread(struct vnode *, daddr_t, int, struct kauth_cred *, + int, struct buf **); +void brelse(struct buf *, int); +int bwrite(struct buf *); +struct buf * getblk(struct vnode *, daddr_t, int, int, int); + +#define bdwrite(bp) bwrite(bp) +#define clrbuf(bp) memset((bp)->b_data, 0, (u_int)(bp)->b_bcount) + +#define B_MODIFY 0 +#define BC_AGE 0 + +#define min(a, b) MIN((a), (b)) +#define microtime(tv) gettimeofday((tv), NULL) +#define KASSERT(a) +#define IO_SYNC 1 + +struct pool { + size_t size; +}; + +#define pool_init(p, s, a1, a2, a3, a4, a5, a6) (p)->size = (s) +#define pool_get(p, f) ecalloc(1, (p)->size) +#define pool_put(p, a) free(a) +#define pool_destroy(p) + +#define MALLOC_DECLARE(a) +#define malloc_type_attach(a) +#define malloc_type_detach(a) + +#define mutex_enter(m) +#define mutex_exit(m) +#define mutex_init(m, t, i) +#define mutex_destroy(m) + +#define desiredvnodes 10000 +#define NOCRED NULL +#define DEV_BSHIFT 9 + +#endif /* _FFS_BUF_H */ diff --git a/usr.sbin/makefs/ffs/ffs_alloc.c b/usr.sbin/makefs/ffs/ffs_alloc.c new file mode 100644 index 000000000..63ab4638e --- /dev/null +++ b/usr.sbin/makefs/ffs/ffs_alloc.c @@ -0,0 +1,597 @@ +/* $NetBSD: ffs_alloc.c,v 1.27 2013/06/23 22:03:34 dholland Exp $ */ +/* From: NetBSD: ffs_alloc.c,v 1.50 2001/09/06 02:16:01 lukem Exp */ + +/* + * Copyright (c) 2002 Networks Associates Technology, Inc. + * All rights reserved. + * + * This software was developed for the FreeBSD Project by Marshall + * Kirk McKusick and Network Associates Laboratories, the Security + * Research Division of Network Associates, Inc. under DARPA/SPAWAR + * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS + * research program + * + * Copyright (c) 1982, 1986, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ffs_alloc.c 8.19 (Berkeley) 7/13/95 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: ffs_alloc.c,v 1.27 2013/06/23 22:03:34 dholland Exp $"); +#endif /* !__lint */ + +#include +#include + +#include + +#include "makefs.h" + +#include +#include +#include + +#include "ffs/buf.h" +#include "ffs/ufs_inode.h" +#include "ffs/ffs_extern.h" + + +static int scanc(u_int, const u_char *, const u_char *, int); + +static daddr_t ffs_alloccg(struct inode *, int, daddr_t, int); +static daddr_t ffs_alloccgblk(struct inode *, struct buf *, daddr_t); +static daddr_t ffs_hashalloc(struct inode *, int, daddr_t, int, + daddr_t (*)(struct inode *, int, daddr_t, int)); +static int32_t ffs_mapsearch(struct fs *, struct cg *, daddr_t, int); + +/* in ffs_tables.c */ +extern const int inside[], around[]; +extern const u_char * const fragtbl[]; + +/* + * Allocate a block in the file system. + * + * The size of the requested block is given, which must be some + * multiple of fs_fsize and <= fs_bsize. + * A preference may be optionally specified. If a preference is given + * the following hierarchy is used to allocate a block: + * 1) allocate the requested block. + * 2) allocate a rotationally optimal block in the same cylinder. + * 3) allocate a block in the same cylinder group. + * 4) quadradically rehash into other cylinder groups, until an + * available block is located. + * If no block preference is given the following hierarchy is used + * to allocate a block: + * 1) allocate a block in the cylinder group that contains the + * inode for the file. + * 2) quadradically rehash into other cylinder groups, until an + * available block is located. + */ +int +ffs_alloc(struct inode *ip, daddr_t lbn __unused, daddr_t bpref, int size, + daddr_t *bnp) +{ + struct fs *fs = ip->i_fs; + daddr_t bno; + int cg; + + *bnp = 0; + if (size > fs->fs_bsize || ffs_fragoff(fs, size) != 0) { + errx(1, "ffs_alloc: bad size: bsize %d size %d", + fs->fs_bsize, size); + } + if (size == fs->fs_bsize && fs->fs_cstotal.cs_nbfree == 0) + goto nospace; + if (bpref >= fs->fs_size) + bpref = 0; + if (bpref == 0) + cg = ino_to_cg(fs, ip->i_number); + else + cg = dtog(fs, bpref); + bno = ffs_hashalloc(ip, cg, bpref, size, ffs_alloccg); + if (bno > 0) { + DIP_ADD(ip, blocks, size / DEV_BSIZE); + *bnp = bno; + return (0); + } +nospace: + return (ENOSPC); +} + +/* + * Select the desired position for the next block in a file. The file is + * logically divided into sections. The first section is composed of the + * direct blocks. Each additional section contains fs_maxbpg blocks. + * + * If no blocks have been allocated in the first section, the policy is to + * request a block in the same cylinder group as the inode that describes + * the file. If no blocks have been allocated in any other section, the + * policy is to place the section in a cylinder group with a greater than + * average number of free blocks. An appropriate cylinder group is found + * by using a rotor that sweeps the cylinder groups. When a new group of + * blocks is needed, the sweep begins in the cylinder group following the + * cylinder group from which the previous allocation was made. The sweep + * continues until a cylinder group with greater than the average number + * of free blocks is found. If the allocation is for the first block in an + * indirect block, the information on the previous allocation is unavailable; + * here a best guess is made based upon the logical block number being + * allocated. + * + * If a section is already partially allocated, the policy is to + * contiguously allocate fs_maxcontig blocks. The end of one of these + * contiguous blocks and the beginning of the next is physically separated + * so that the disk head will be in transit between them for at least + * fs_rotdelay milliseconds. This is to allow time for the processor to + * schedule another I/O transfer. + */ +/* XXX ondisk32 */ +daddr_t +ffs_blkpref_ufs1(struct inode *ip, daddr_t lbn, int indx, int32_t *bap) +{ + struct fs *fs; + int cg; + int avgbfree, startcg; + + fs = ip->i_fs; + if (indx % fs->fs_maxbpg == 0 || bap[indx - 1] == 0) { + if (lbn < UFS_NDADDR + FFS_NINDIR(fs)) { + cg = ino_to_cg(fs, ip->i_number); + return (fs->fs_fpg * cg + fs->fs_frag); + } + /* + * Find a cylinder with greater than average number of + * unused data blocks. + */ + if (indx == 0 || bap[indx - 1] == 0) + startcg = + ino_to_cg(fs, ip->i_number) + lbn / fs->fs_maxbpg; + else + startcg = dtog(fs, + ufs_rw32(bap[indx - 1], UFS_FSNEEDSWAP(fs)) + 1); + startcg %= fs->fs_ncg; + avgbfree = fs->fs_cstotal.cs_nbfree / fs->fs_ncg; + for (cg = startcg; cg < fs->fs_ncg; cg++) + if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) + return (fs->fs_fpg * cg + fs->fs_frag); + for (cg = 0; cg <= startcg; cg++) + if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) + return (fs->fs_fpg * cg + fs->fs_frag); + return (0); + } + /* + * We just always try to lay things out contiguously. + */ + return ufs_rw32(bap[indx - 1], UFS_FSNEEDSWAP(fs)) + fs->fs_frag; +} + +daddr_t +ffs_blkpref_ufs2(struct inode *ip, daddr_t lbn, int indx, int64_t *bap) +{ + struct fs *fs; + int cg; + int avgbfree, startcg; + + fs = ip->i_fs; + if (indx % fs->fs_maxbpg == 0 || bap[indx - 1] == 0) { + if (lbn < UFS_NDADDR + FFS_NINDIR(fs)) { + cg = ino_to_cg(fs, ip->i_number); + return (fs->fs_fpg * cg + fs->fs_frag); + } + /* + * Find a cylinder with greater than average number of + * unused data blocks. + */ + if (indx == 0 || bap[indx - 1] == 0) + startcg = + ino_to_cg(fs, ip->i_number) + lbn / fs->fs_maxbpg; + else + startcg = dtog(fs, + ufs_rw64(bap[indx - 1], UFS_FSNEEDSWAP(fs)) + 1); + startcg %= fs->fs_ncg; + avgbfree = fs->fs_cstotal.cs_nbfree / fs->fs_ncg; + for (cg = startcg; cg < fs->fs_ncg; cg++) + if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) { + return (fs->fs_fpg * cg + fs->fs_frag); + } + for (cg = 0; cg < startcg; cg++) + if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) { + return (fs->fs_fpg * cg + fs->fs_frag); + } + return (0); + } + /* + * We just always try to lay things out contiguously. + */ + return ufs_rw64(bap[indx - 1], UFS_FSNEEDSWAP(fs)) + fs->fs_frag; +} + +/* + * Implement the cylinder overflow algorithm. + * + * The policy implemented by this algorithm is: + * 1) allocate the block in its requested cylinder group. + * 2) quadradically rehash on the cylinder group number. + * 3) brute force search for a free block. + * + * `size': size for data blocks, mode for inodes + */ +/*VARARGS5*/ +static daddr_t +ffs_hashalloc(struct inode *ip, int cg, daddr_t pref, int size, + daddr_t (*allocator)(struct inode *, int, daddr_t, int)) +{ + struct fs *fs; + daddr_t result; + int i, icg = cg; + + fs = ip->i_fs; + /* + * 1: preferred cylinder group + */ + result = (*allocator)(ip, cg, pref, size); + if (result) + return (result); + /* + * 2: quadratic rehash + */ + for (i = 1; i < fs->fs_ncg; i *= 2) { + cg += i; + if (cg >= fs->fs_ncg) + cg -= fs->fs_ncg; + result = (*allocator)(ip, cg, 0, size); + if (result) + return (result); + } + /* + * 3: brute force search + * Note that we start at i == 2, since 0 was checked initially, + * and 1 is always checked in the quadratic rehash. + */ + cg = (icg + 2) % fs->fs_ncg; + for (i = 2; i < fs->fs_ncg; i++) { + result = (*allocator)(ip, cg, 0, size); + if (result) + return (result); + cg++; + if (cg == fs->fs_ncg) + cg = 0; + } + return (0); +} + +/* + * Determine whether a block can be allocated. + * + * Check to see if a block of the appropriate size is available, + * and if it is, allocate it. + */ +static daddr_t +ffs_alloccg(struct inode *ip, int cg, daddr_t bpref, int size) +{ + struct cg *cgp; + struct buf *bp; + daddr_t bno, blkno; + int error, frags, allocsiz, i; + struct fs *fs = ip->i_fs; + const int needswap = UFS_FSNEEDSWAP(fs); + + if (fs->fs_cs(fs, cg).cs_nbfree == 0 && size == fs->fs_bsize) + return (0); + error = bread(ip->i_devvp, FFS_FSBTODB(fs, cgtod(fs, cg)), + (int)fs->fs_cgsize, NULL, 0, &bp); + if (error) { + return (0); + } + cgp = (struct cg *)bp->b_data; + if (!cg_chkmagic(cgp, needswap) || + (cgp->cg_cs.cs_nbfree == 0 && size == fs->fs_bsize)) { + brelse(bp, 0); + return (0); + } + if (size == fs->fs_bsize) { + bno = ffs_alloccgblk(ip, bp, bpref); + bwrite(bp); + return (bno); + } + /* + * check to see if any fragments are already available + * allocsiz is the size which will be allocated, hacking + * it down to a smaller size if necessary + */ + frags = ffs_numfrags(fs, size); + for (allocsiz = frags; allocsiz < fs->fs_frag; allocsiz++) + if (cgp->cg_frsum[allocsiz] != 0) + break; + if (allocsiz == fs->fs_frag) { + /* + * no fragments were available, so a block will be + * allocated, and hacked up + */ + if (cgp->cg_cs.cs_nbfree == 0) { + brelse(bp, 0); + return (0); + } + bno = ffs_alloccgblk(ip, bp, bpref); + bpref = dtogd(fs, bno); + for (i = frags; i < fs->fs_frag; i++) + setbit(cg_blksfree(cgp, needswap), bpref + i); + i = fs->fs_frag - frags; + ufs_add32(cgp->cg_cs.cs_nffree, i, needswap); + fs->fs_cstotal.cs_nffree += i; + fs->fs_cs(fs, cg).cs_nffree += i; + fs->fs_fmod = 1; + ufs_add32(cgp->cg_frsum[i], 1, needswap); + bdwrite(bp); + return (bno); + } + bno = ffs_mapsearch(fs, cgp, bpref, allocsiz); + for (i = 0; i < frags; i++) + clrbit(cg_blksfree(cgp, needswap), bno + i); + ufs_add32(cgp->cg_cs.cs_nffree, -frags, needswap); + fs->fs_cstotal.cs_nffree -= frags; + fs->fs_cs(fs, cg).cs_nffree -= frags; + fs->fs_fmod = 1; + ufs_add32(cgp->cg_frsum[allocsiz], -1, needswap); + if (frags != allocsiz) + ufs_add32(cgp->cg_frsum[allocsiz - frags], 1, needswap); + blkno = cg * fs->fs_fpg + bno; + bdwrite(bp); + return blkno; +} + +/* + * Allocate a block in a cylinder group. + * + * This algorithm implements the following policy: + * 1) allocate the requested block. + * 2) allocate a rotationally optimal block in the same cylinder. + * 3) allocate the next available block on the block rotor for the + * specified cylinder group. + * Note that this routine only allocates fs_bsize blocks; these + * blocks may be fragmented by the routine that allocates them. + */ +static daddr_t +ffs_alloccgblk(struct inode *ip, struct buf *bp, daddr_t bpref) +{ + struct cg *cgp; + daddr_t blkno; + int32_t bno; + struct fs *fs = ip->i_fs; + const int needswap = UFS_FSNEEDSWAP(fs); + u_int8_t *blksfree; + + cgp = (struct cg *)bp->b_data; + blksfree = cg_blksfree(cgp, needswap); + if (bpref == 0 || dtog(fs, bpref) != ufs_rw32(cgp->cg_cgx, needswap)) { + bpref = ufs_rw32(cgp->cg_rotor, needswap); + } else { + bpref = ffs_blknum(fs, bpref); + bno = dtogd(fs, bpref); + /* + * if the requested block is available, use it + */ + if (ffs_isblock(fs, blksfree, ffs_fragstoblks(fs, bno))) + goto gotit; + } + /* + * Take the next available one in this cylinder group. + */ + bno = ffs_mapsearch(fs, cgp, bpref, (int)fs->fs_frag); + if (bno < 0) + return (0); + cgp->cg_rotor = ufs_rw32(bno, needswap); +gotit: + blkno = ffs_fragstoblks(fs, bno); + ffs_clrblock(fs, blksfree, (long)blkno); + ffs_clusteracct(fs, cgp, blkno, -1); + ufs_add32(cgp->cg_cs.cs_nbfree, -1, needswap); + fs->fs_cstotal.cs_nbfree--; + fs->fs_cs(fs, ufs_rw32(cgp->cg_cgx, needswap)).cs_nbfree--; + fs->fs_fmod = 1; + blkno = ufs_rw32(cgp->cg_cgx, needswap) * fs->fs_fpg + bno; + return (blkno); +} + +/* + * Free a block or fragment. + * + * The specified block or fragment is placed back in the + * free map. If a fragment is deallocated, a possible + * block reassembly is checked. + */ +void +ffs_blkfree(struct inode *ip, daddr_t bno, long size) +{ + struct cg *cgp; + struct buf *bp; + int32_t fragno, cgbno; + int i, error, cg, blk, frags, bbase; + struct fs *fs = ip->i_fs; + const int needswap = UFS_FSNEEDSWAP(fs); + + if (size > fs->fs_bsize || ffs_fragoff(fs, size) != 0 || + ffs_fragnum(fs, bno) + ffs_numfrags(fs, size) > fs->fs_frag) { + errx(1, "blkfree: bad size: bno %lld bsize %d size %ld", + (long long)bno, fs->fs_bsize, size); + } + cg = dtog(fs, bno); + if (bno >= fs->fs_size) { + warnx("bad block %lld, ino %llu", (long long)bno, + (unsigned long long)ip->i_number); + return; + } + error = bread(ip->i_devvp, FFS_FSBTODB(fs, cgtod(fs, cg)), + (int)fs->fs_cgsize, NULL, 0, &bp); + if (error) { + brelse(bp, 0); + return; + } + cgp = (struct cg *)bp->b_data; + if (!cg_chkmagic(cgp, needswap)) { + brelse(bp, 0); + return; + } + cgbno = dtogd(fs, bno); + if (size == fs->fs_bsize) { + fragno = ffs_fragstoblks(fs, cgbno); + if (!ffs_isfreeblock(fs, cg_blksfree(cgp, needswap), fragno)) { + errx(1, "blkfree: freeing free block %lld", + (long long)bno); + } + ffs_setblock(fs, cg_blksfree(cgp, needswap), fragno); + ffs_clusteracct(fs, cgp, fragno, 1); + ufs_add32(cgp->cg_cs.cs_nbfree, 1, needswap); + fs->fs_cstotal.cs_nbfree++; + fs->fs_cs(fs, cg).cs_nbfree++; + } else { + bbase = cgbno - ffs_fragnum(fs, cgbno); + /* + * decrement the counts associated with the old frags + */ + blk = blkmap(fs, cg_blksfree(cgp, needswap), bbase); + ffs_fragacct(fs, blk, cgp->cg_frsum, -1, needswap); + /* + * deallocate the fragment + */ + frags = ffs_numfrags(fs, size); + for (i = 0; i < frags; i++) { + if (isset(cg_blksfree(cgp, needswap), cgbno + i)) { + errx(1, "blkfree: freeing free frag: block %lld", + (long long)(cgbno + i)); + } + setbit(cg_blksfree(cgp, needswap), cgbno + i); + } + ufs_add32(cgp->cg_cs.cs_nffree, i, needswap); + fs->fs_cstotal.cs_nffree += i; + fs->fs_cs(fs, cg).cs_nffree += i; + /* + * add back in counts associated with the new frags + */ + blk = blkmap(fs, cg_blksfree(cgp, needswap), bbase); + ffs_fragacct(fs, blk, cgp->cg_frsum, 1, needswap); + /* + * if a complete block has been reassembled, account for it + */ + fragno = ffs_fragstoblks(fs, bbase); + if (ffs_isblock(fs, cg_blksfree(cgp, needswap), fragno)) { + ufs_add32(cgp->cg_cs.cs_nffree, -fs->fs_frag, needswap); + fs->fs_cstotal.cs_nffree -= fs->fs_frag; + fs->fs_cs(fs, cg).cs_nffree -= fs->fs_frag; + ffs_clusteracct(fs, cgp, fragno, 1); + ufs_add32(cgp->cg_cs.cs_nbfree, 1, needswap); + fs->fs_cstotal.cs_nbfree++; + fs->fs_cs(fs, cg).cs_nbfree++; + } + } + fs->fs_fmod = 1; + bdwrite(bp); +} + + +static int +scanc(u_int size, const u_char *cp, const u_char table[], int mask) +{ + const u_char *end = &cp[size]; + + while (cp < end && (table[*cp] & mask) == 0) + cp++; + return (end - cp); +} + +/* + * Find a block of the specified size in the specified cylinder group. + * + * It is a panic if a request is made to find a block if none are + * available. + */ +static int32_t +ffs_mapsearch(struct fs *fs, struct cg *cgp, daddr_t bpref, int allocsiz) +{ + int32_t bno; + int start, len, loc, i; + int blk, field, subfield, pos; + int ostart, olen; + const int needswap = UFS_FSNEEDSWAP(fs); + + /* + * find the fragment by searching through the free block + * map for an appropriate bit pattern + */ + if (bpref) + start = dtogd(fs, bpref) / NBBY; + else + start = ufs_rw32(cgp->cg_frotor, needswap) / NBBY; + len = howmany(fs->fs_fpg, NBBY) - start; + ostart = start; + olen = len; + loc = scanc((u_int)len, + (const u_char *)&cg_blksfree(cgp, needswap)[start], + (const u_char *)fragtbl[fs->fs_frag], + (1 << (allocsiz - 1 + (fs->fs_frag % NBBY)))); + if (loc == 0) { + len = start + 1; + start = 0; + loc = scanc((u_int)len, + (const u_char *)&cg_blksfree(cgp, needswap)[0], + (const u_char *)fragtbl[fs->fs_frag], + (1 << (allocsiz - 1 + (fs->fs_frag % NBBY)))); + if (loc == 0) { + errx(1, + "ffs_alloccg: map corrupted: start %d len %d offset %d %ld", + ostart, olen, + ufs_rw32(cgp->cg_freeoff, needswap), + (long)cg_blksfree(cgp, needswap) - (long)cgp); + /* NOTREACHED */ + } + } + bno = (start + len - loc) * NBBY; + cgp->cg_frotor = ufs_rw32(bno, needswap); + /* + * found the byte in the map + * sift through the bits to find the selected frag + */ + for (i = bno + NBBY; bno < i; bno += fs->fs_frag) { + blk = blkmap(fs, cg_blksfree(cgp, needswap), bno); + blk <<= 1; + field = around[allocsiz]; + subfield = inside[allocsiz]; + for (pos = 0; pos <= fs->fs_frag - allocsiz; pos++) { + if ((blk & field) == subfield) + return (bno + pos); + field <<= 1; + subfield <<= 1; + } + } + errx(1, "ffs_alloccg: block not in map: bno %lld", (long long)bno); + return (-1); +} diff --git a/usr.sbin/makefs/ffs/ffs_balloc.c b/usr.sbin/makefs/ffs/ffs_balloc.c new file mode 100644 index 000000000..e5dd1d38a --- /dev/null +++ b/usr.sbin/makefs/ffs/ffs_balloc.c @@ -0,0 +1,584 @@ +/* $NetBSD: ffs_balloc.c,v 1.20 2013/06/23 07:28:37 dholland Exp $ */ +/* From NetBSD: ffs_balloc.c,v 1.25 2001/08/08 08:36:36 lukem Exp */ + +/* + * Copyright (c) 1982, 1986, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ffs_balloc.c 8.8 (Berkeley) 6/16/95 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: ffs_balloc.c,v 1.20 2013/06/23 07:28:37 dholland Exp $"); +#endif /* !__lint */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "makefs.h" + +#include +#include +#include + +#include "ffs/buf.h" +#include "ffs/ufs_inode.h" +#include "ffs/ffs_extern.h" + +static int ffs_balloc_ufs1(struct inode *, off_t, int, struct buf **); +static int ffs_balloc_ufs2(struct inode *, off_t, int, struct buf **); + +/* + * Balloc defines the structure of file system storage + * by allocating the physical blocks on a device given + * the inode and the logical block number in a file. + * + * Assume: flags == B_SYNC | B_CLRBUF + */ + +int +ffs_balloc(struct inode *ip, off_t offset, int bufsize, struct buf **bpp) +{ + if (ip->i_fs->fs_magic == FS_UFS2_MAGIC) + return ffs_balloc_ufs2(ip, offset, bufsize, bpp); + else + return ffs_balloc_ufs1(ip, offset, bufsize, bpp); +} + +static int +ffs_balloc_ufs1(struct inode *ip, off_t offset, int bufsize, struct buf **bpp) +{ + daddr_t lbn, lastlbn; + int size; + int32_t nb; + struct buf *bp, *nbp; + struct fs *fs = ip->i_fs; + struct indir indirs[UFS_NIADDR + 2]; + daddr_t newb, pref; + int32_t *bap; + int osize, nsize, num, i, error; + int32_t *allocblk, allociblk[UFS_NIADDR + 1]; + int32_t *allocib; + const int needswap = UFS_FSNEEDSWAP(fs); + + lbn = ffs_lblkno(fs, offset); + size = ffs_blkoff(fs, offset) + bufsize; + if (bpp != NULL) { + *bpp = NULL; + } + + assert(size <= fs->fs_bsize); + if (lbn < 0) + return (EFBIG); + + /* + * If the next write will extend the file into a new block, + * and the file is currently composed of a fragment + * this fragment has to be extended to be a full block. + */ + + lastlbn = ffs_lblkno(fs, ip->i_ffs1_size); + if (lastlbn < UFS_NDADDR && lastlbn < lbn) { + nb = lastlbn; + osize = ffs_blksize(fs, ip, nb); + if (osize < fs->fs_bsize && osize > 0) { + warnx("need to ffs_realloccg; not supported!"); + abort(); + } + } + + /* + * The first UFS_NDADDR blocks are direct blocks + */ + + if (lbn < UFS_NDADDR) { + nb = ufs_rw32(ip->i_ffs1_db[lbn], needswap); + if (nb != 0 && ip->i_ffs1_size >= ffs_lblktosize(fs, lbn + 1)) { + + /* + * The block is an already-allocated direct block + * and the file already extends past this block, + * thus this must be a whole block. + * Just read the block (if requested). + */ + + if (bpp != NULL) { + error = bread(ip->i_devvp, lbn, fs->fs_bsize, + NULL, 0, bpp); + if (error) { + brelse(*bpp, 0); + return (error); + } + } + return (0); + } + if (nb != 0) { + + /* + * Consider need to reallocate a fragment. + */ + + osize = ffs_fragroundup(fs, ffs_blkoff(fs, ip->i_ffs1_size)); + nsize = ffs_fragroundup(fs, size); + if (nsize <= osize) { + + /* + * The existing block is already + * at least as big as we want. + * Just read the block (if requested). + */ + + if (bpp != NULL) { + error = bread(ip->i_devvp, lbn, osize, + NULL, 0, bpp); + if (error) { + brelse(*bpp, 0); + return (error); + } + } + return 0; + } else { + warnx("need to ffs_realloccg; not supported!"); + abort(); + } + } else { + + /* + * the block was not previously allocated, + * allocate a new block or fragment. + */ + + if (ip->i_ffs1_size < ffs_lblktosize(fs, lbn + 1)) + nsize = ffs_fragroundup(fs, size); + else + nsize = fs->fs_bsize; + error = ffs_alloc(ip, lbn, + ffs_blkpref_ufs1(ip, lbn, (int)lbn, + &ip->i_ffs1_db[0]), + nsize, &newb); + if (error) + return (error); + if (bpp != NULL) { + bp = getblk(ip->i_devvp, lbn, nsize, 0, 0); + bp->b_blkno = FFS_FSBTODB(fs, newb); + clrbuf(bp); + *bpp = bp; + } + } + ip->i_ffs1_db[lbn] = ufs_rw32((int32_t)newb, needswap); + return (0); + } + + /* + * Determine the number of levels of indirection. + */ + + pref = 0; + if ((error = ufs_getlbns(ip, lbn, indirs, &num)) != 0) + return (error); + + if (num < 1) { + warnx("ffs_balloc: ufs_getlbns returned indirect block"); + abort(); + } + + /* + * Fetch the first indirect block allocating if necessary. + */ + + --num; + nb = ufs_rw32(ip->i_ffs1_ib[indirs[0].in_off], needswap); + allocib = NULL; + allocblk = allociblk; + if (nb == 0) { + pref = ffs_blkpref_ufs1(ip, lbn, 0, (int32_t *)0); + error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb); + if (error) + return error; + nb = newb; + *allocblk++ = nb; + bp = getblk(ip->i_devvp, indirs[1].in_lbn, fs->fs_bsize, 0, 0); + bp->b_blkno = FFS_FSBTODB(fs, nb); + clrbuf(bp); + /* + * Write synchronously so that indirect blocks + * never point at garbage. + */ + if ((error = bwrite(bp)) != 0) + return error; + allocib = &ip->i_ffs1_ib[indirs[0].in_off]; + *allocib = ufs_rw32((int32_t)nb, needswap); + } + + /* + * Fetch through the indirect blocks, allocating as necessary. + */ + + for (i = 1;;) { + error = bread(ip->i_devvp, indirs[i].in_lbn, fs->fs_bsize, + NULL, 0, &bp); + if (error) { + brelse(bp, 0); + return error; + } + bap = (int32_t *)bp->b_data; + nb = ufs_rw32(bap[indirs[i].in_off], needswap); + if (i == num) + break; + i++; + if (nb != 0) { + brelse(bp, 0); + continue; + } + if (pref == 0) + pref = ffs_blkpref_ufs1(ip, lbn, 0, (int32_t *)0); + error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb); + if (error) { + brelse(bp, 0); + return error; + } + nb = newb; + *allocblk++ = nb; + nbp = getblk(ip->i_devvp, indirs[i].in_lbn, fs->fs_bsize, 0, 0); + nbp->b_blkno = FFS_FSBTODB(fs, nb); + clrbuf(nbp); + /* + * Write synchronously so that indirect blocks + * never point at garbage. + */ + + if ((error = bwrite(nbp)) != 0) { + brelse(bp, 0); + return error; + } + bap[indirs[i - 1].in_off] = ufs_rw32(nb, needswap); + + bwrite(bp); + } + + /* + * Get the data block, allocating if necessary. + */ + + if (nb == 0) { + pref = ffs_blkpref_ufs1(ip, lbn, indirs[num].in_off, &bap[0]); + error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb); + if (error) { + brelse(bp, 0); + return error; + } + nb = newb; + *allocblk++ = nb; + if (bpp != NULL) { + nbp = getblk(ip->i_devvp, lbn, fs->fs_bsize, 0, 0); + nbp->b_blkno = FFS_FSBTODB(fs, nb); + clrbuf(nbp); + *bpp = nbp; + } + bap[indirs[num].in_off] = ufs_rw32(nb, needswap); + + /* + * If required, write synchronously, otherwise use + * delayed write. + */ + bwrite(bp); + return (0); + } + brelse(bp, 0); + if (bpp != NULL) { + error = bread(ip->i_devvp, lbn, (int)fs->fs_bsize, NULL, 0, + &nbp); + if (error) { + brelse(nbp, 0); + return error; + } + *bpp = nbp; + } + return (0); +} + +static int +ffs_balloc_ufs2(struct inode *ip, off_t offset, int bufsize, struct buf **bpp) +{ + daddr_t lbn, lastlbn; + int size; + struct buf *bp, *nbp; + struct fs *fs = ip->i_fs; + struct indir indirs[UFS_NIADDR + 2]; + daddr_t newb, pref, nb; + int64_t *bap; + int osize, nsize, num, i, error; + int64_t *allocblk, allociblk[UFS_NIADDR + 1]; + int64_t *allocib; + const int needswap = UFS_FSNEEDSWAP(fs); + + lbn = ffs_lblkno(fs, offset); + size = ffs_blkoff(fs, offset) + bufsize; + if (bpp != NULL) { + *bpp = NULL; + } + + assert(size <= fs->fs_bsize); + if (lbn < 0) + return (EFBIG); + + /* + * If the next write will extend the file into a new block, + * and the file is currently composed of a fragment + * this fragment has to be extended to be a full block. + */ + + lastlbn = ffs_lblkno(fs, ip->i_ffs2_size); + if (lastlbn < UFS_NDADDR && lastlbn < lbn) { + nb = lastlbn; + osize = ffs_blksize(fs, ip, nb); + if (osize < fs->fs_bsize && osize > 0) { + warnx("need to ffs_realloccg; not supported!"); + abort(); + } + } + + /* + * The first UFS_NDADDR blocks are direct blocks + */ + + if (lbn < UFS_NDADDR) { + nb = ufs_rw64(ip->i_ffs2_db[lbn], needswap); + if (nb != 0 && ip->i_ffs2_size >= ffs_lblktosize(fs, lbn + 1)) { + + /* + * The block is an already-allocated direct block + * and the file already extends past this block, + * thus this must be a whole block. + * Just read the block (if requested). + */ + + if (bpp != NULL) { + error = bread(ip->i_devvp, lbn, fs->fs_bsize, + NULL, 0, bpp); + if (error) { + brelse(*bpp, 0); + return (error); + } + } + return (0); + } + if (nb != 0) { + + /* + * Consider need to reallocate a fragment. + */ + + osize = ffs_fragroundup(fs, ffs_blkoff(fs, ip->i_ffs2_size)); + nsize = ffs_fragroundup(fs, size); + if (nsize <= osize) { + + /* + * The existing block is already + * at least as big as we want. + * Just read the block (if requested). + */ + + if (bpp != NULL) { + error = bread(ip->i_devvp, lbn, osize, + NULL, 0, bpp); + if (error) { + brelse(*bpp, 0); + return (error); + } + } + return 0; + } else { + warnx("need to ffs_realloccg; not supported!"); + abort(); + } + } else { + + /* + * the block was not previously allocated, + * allocate a new block or fragment. + */ + + if (ip->i_ffs2_size < ffs_lblktosize(fs, lbn + 1)) + nsize = ffs_fragroundup(fs, size); + else + nsize = fs->fs_bsize; + error = ffs_alloc(ip, lbn, + ffs_blkpref_ufs2(ip, lbn, (int)lbn, + &ip->i_ffs2_db[0]), + nsize, &newb); + if (error) + return (error); + if (bpp != NULL) { + bp = getblk(ip->i_devvp, lbn, nsize, 0, 0); + bp->b_blkno = FFS_FSBTODB(fs, newb); + clrbuf(bp); + *bpp = bp; + } + } + ip->i_ffs2_db[lbn] = ufs_rw64(newb, needswap); + return (0); + } + + /* + * Determine the number of levels of indirection. + */ + + pref = 0; + if ((error = ufs_getlbns(ip, lbn, indirs, &num)) != 0) + return (error); + + if (num < 1) { + warnx("ffs_balloc: ufs_getlbns returned indirect block"); + abort(); + } + + /* + * Fetch the first indirect block allocating if necessary. + */ + + --num; + nb = ufs_rw64(ip->i_ffs2_ib[indirs[0].in_off], needswap); + allocib = NULL; + allocblk = allociblk; + if (nb == 0) { + pref = ffs_blkpref_ufs2(ip, lbn, 0, (int64_t *)0); + error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb); + if (error) + return error; + nb = newb; + *allocblk++ = nb; + bp = getblk(ip->i_devvp, indirs[1].in_lbn, fs->fs_bsize, 0, 0); + bp->b_blkno = FFS_FSBTODB(fs, nb); + clrbuf(bp); + /* + * Write synchronously so that indirect blocks + * never point at garbage. + */ + if ((error = bwrite(bp)) != 0) + return error; + allocib = &ip->i_ffs2_ib[indirs[0].in_off]; + *allocib = ufs_rw64(nb, needswap); + } + + /* + * Fetch through the indirect blocks, allocating as necessary. + */ + + for (i = 1;;) { + error = bread(ip->i_devvp, indirs[i].in_lbn, fs->fs_bsize, + NULL, 0, &bp); + if (error) { + brelse(bp, 0); + return error; + } + bap = (int64_t *)bp->b_data; + nb = ufs_rw64(bap[indirs[i].in_off], needswap); + if (i == num) + break; + i++; + if (nb != 0) { + brelse(bp, 0); + continue; + } + if (pref == 0) + pref = ffs_blkpref_ufs2(ip, lbn, 0, (int64_t *)0); + error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb); + if (error) { + brelse(bp, 0); + return error; + } + nb = newb; + *allocblk++ = nb; + nbp = getblk(ip->i_devvp, indirs[i].in_lbn, fs->fs_bsize, 0, 0); + nbp->b_blkno = FFS_FSBTODB(fs, nb); + clrbuf(nbp); + /* + * Write synchronously so that indirect blocks + * never point at garbage. + */ + + if ((error = bwrite(nbp)) != 0) { + brelse(bp, 0); + return error; + } + bap[indirs[i - 1].in_off] = ufs_rw64(nb, needswap); + + bwrite(bp); + } + + /* + * Get the data block, allocating if necessary. + */ + + if (nb == 0) { + pref = ffs_blkpref_ufs2(ip, lbn, indirs[num].in_off, &bap[0]); + error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb); + if (error) { + brelse(bp, 0); + return error; + } + nb = newb; + *allocblk++ = nb; + if (bpp != NULL) { + nbp = getblk(ip->i_devvp, lbn, fs->fs_bsize, 0, 0); + nbp->b_blkno = FFS_FSBTODB(fs, nb); + clrbuf(nbp); + *bpp = nbp; + } + bap[indirs[num].in_off] = ufs_rw64(nb, needswap); + + /* + * If required, write synchronously, otherwise use + * delayed write. + */ + bwrite(bp); + return (0); + } + brelse(bp, 0); + if (bpp != NULL) { + error = bread(ip->i_devvp, lbn, (int)fs->fs_bsize, NULL, 0, + &nbp); + if (error) { + brelse(nbp, 0); + return error; + } + *bpp = nbp; + } + return (0); +} diff --git a/usr.sbin/makefs/ffs/ffs_extern.h b/usr.sbin/makefs/ffs/ffs_extern.h new file mode 100644 index 000000000..53e9fe2ee --- /dev/null +++ b/usr.sbin/makefs/ffs/ffs_extern.h @@ -0,0 +1,76 @@ +/* $NetBSD: ffs_extern.h,v 1.6 2003/08/07 11:25:33 agc Exp $ */ +/* From: NetBSD: ffs_extern.h,v 1.19 2001/08/17 02:18:48 lukem Exp */ + +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ffs_extern.h 8.6 (Berkeley) 3/30/95 + */ + +#include "ffs/buf.h" + +/* + * Structure used to pass around logical block paths generated by + * ufs_getlbns and used by truncate and bmap code. + */ +struct indir { + daddr_t in_lbn; /* Logical block number. */ + int in_off; /* Offset in buffer. */ + int in_exists; /* Flag if the block exists. */ +}; + + /* ffs.c */ +void panic(const char *, ...) + __attribute__((__noreturn__,__format__(__printf__,1,2))); + + /* ffs_alloc.c */ +int ffs_alloc(struct inode *, daddr_t, daddr_t, int, daddr_t *); +daddr_t ffs_blkpref_ufs1(struct inode *, daddr_t, int, int32_t *); +daddr_t ffs_blkpref_ufs2(struct inode *, daddr_t, int, int64_t *); +void ffs_blkfree(struct inode *, daddr_t, long); +void ffs_clusteracct(struct fs *, struct cg *, int32_t, int); + + /* ffs_balloc.c */ +int ffs_balloc(struct inode *, off_t, int, struct buf **); + + /* ffs_bswap.c */ +void ffs_sb_swap(struct fs*, struct fs *); +void ffs_dinode1_swap(struct ufs1_dinode *, struct ufs1_dinode *); +void ffs_dinode2_swap(struct ufs2_dinode *, struct ufs2_dinode *); +void ffs_csum_swap(struct csum *, struct csum *, int); +void ffs_cg_swap(struct cg *, struct cg *, struct fs *); + + /* ffs_subr.c */ +void ffs_fragacct(struct fs *, int, int32_t[], int, int); +int ffs_isblock(struct fs *, u_char *, int32_t); +int ffs_isfreeblock(struct fs *, u_char *, int32_t); +void ffs_clrblock(struct fs *, u_char *, int32_t); +void ffs_setblock(struct fs *, u_char *, int32_t); + + /* ufs_bmap.c */ +int ufs_getlbns(struct inode *, daddr_t, struct indir *, int *); diff --git a/usr.sbin/makefs/ffs/mkfs.c b/usr.sbin/makefs/ffs/mkfs.c new file mode 100644 index 000000000..c4dbc9b46 --- /dev/null +++ b/usr.sbin/makefs/ffs/mkfs.c @@ -0,0 +1,845 @@ +/* $NetBSD: mkfs.c,v 1.32 2013/10/19 17:16:37 christos Exp $ */ + +/* + * Copyright (c) 2002 Networks Associates Technology, Inc. + * All rights reserved. + * + * This software was developed for the FreeBSD Project by Marshall + * Kirk McKusick and Network Associates Laboratories, the Security + * Research Division of Network Associates, Inc. under DARPA/SPAWAR + * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS + * research program + * + * Copyright (c) 1980, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)mkfs.c 8.11 (Berkeley) 5/3/95"; +#else +#ifdef __RCSID +__RCSID("$NetBSD: mkfs.c,v 1.32 2013/10/19 17:16:37 christos Exp $"); +#endif +#endif +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "makefs.h" +#include "ffs.h" + +#include +#include +#include + +#include "ffs/ufs_inode.h" +#include "ffs/ffs_extern.h" +#include "ffs/newfs_extern.h" + +static void initcg(int, time_t, const fsinfo_t *); +static int ilog2(int); + +static int count_digits(int); + +/* + * make file system for cylinder-group style file systems + */ +#define UMASK 0755 +#define POWEROF2(num) (((num) & ((num) - 1)) == 0) + +union { + struct fs fs; + char pad[SBLOCKSIZE]; +} fsun; +#define sblock fsun.fs +struct csum *fscs; + +union { + struct cg cg; + char pad[FFS_MAXBSIZE]; +} cgun; +#define acg cgun.cg + +char *iobuf; +int iobufsize; + +char writebuf[FFS_MAXBSIZE]; + +static int Oflag; /* format as an 4.3BSD file system */ +static int64_t fssize; /* file system size */ +static int sectorsize; /* bytes/sector */ +static int fsize; /* fragment size */ +static int bsize; /* block size */ +static int maxbsize; /* maximum clustering */ +static int maxblkspercg; +static int minfree; /* free space threshold */ +static int opt; /* optimization preference (space or time) */ +static int density; /* number of bytes per inode */ +static int maxcontig; /* max contiguous blocks to allocate */ +static int maxbpg; /* maximum blocks per file in a cyl group */ +static int bbsize; /* boot block size */ +static int sbsize; /* superblock size */ +static int avgfilesize; /* expected average file size */ +static int avgfpdir; /* expected number of files per directory */ + +struct fs * +ffs_mkfs(const char *fsys, const fsinfo_t *fsopts) +{ + int fragsperinode, optimalfpg, origdensity, minfpg, lastminfpg; + int32_t cylno, i, csfrags; + long long sizepb; + void *space; + int size; + int nprintcols, printcolwidth; + ffs_opt_t *ffs_opts = fsopts->fs_specific; + + Oflag = ffs_opts->version; + fssize = fsopts->size / fsopts->sectorsize; + sectorsize = fsopts->sectorsize; + fsize = ffs_opts->fsize; + bsize = ffs_opts->bsize; + maxbsize = ffs_opts->maxbsize; + maxblkspercg = ffs_opts->maxblkspercg; + minfree = ffs_opts->minfree; + opt = ffs_opts->optimization; + density = ffs_opts->density; + maxcontig = ffs_opts->maxcontig; + maxbpg = ffs_opts->maxbpg; + avgfilesize = ffs_opts->avgfilesize; + avgfpdir = ffs_opts->avgfpdir; + bbsize = BBSIZE; + sbsize = SBLOCKSIZE; + + strlcpy((char *)sblock.fs_volname, ffs_opts->label, + sizeof(sblock.fs_volname)); + + if (Oflag == 0) { + sblock.fs_old_inodefmt = FS_42INODEFMT; + sblock.fs_maxsymlinklen = 0; + sblock.fs_old_flags = 0; + } else { + sblock.fs_old_inodefmt = FS_44INODEFMT; + sblock.fs_maxsymlinklen = (Oflag == 1 ? UFS1_MAXSYMLINKLEN : + UFS2_MAXSYMLINKLEN); + sblock.fs_old_flags = FS_FLAGS_UPDATED; + sblock.fs_flags = 0; + } + /* + * Validate the given file system size. + * Verify that its last block can actually be accessed. + * Convert to file system fragment sized units. + */ + if (fssize <= 0) { + printf("preposterous size %lld\n", (long long)fssize); + exit(13); + } + ffs_wtfs(fssize - 1, sectorsize, (char *)&sblock, fsopts); + + /* + * collect and verify the filesystem density info + */ + sblock.fs_avgfilesize = avgfilesize; + sblock.fs_avgfpdir = avgfpdir; + if (sblock.fs_avgfilesize <= 0) + printf("illegal expected average file size %d\n", + sblock.fs_avgfilesize), exit(14); + if (sblock.fs_avgfpdir <= 0) + printf("illegal expected number of files per directory %d\n", + sblock.fs_avgfpdir), exit(15); + /* + * collect and verify the block and fragment sizes + */ + sblock.fs_bsize = bsize; + sblock.fs_fsize = fsize; + if (!POWEROF2(sblock.fs_bsize)) { + printf("block size must be a power of 2, not %d\n", + sblock.fs_bsize); + exit(16); + } + if (!POWEROF2(sblock.fs_fsize)) { + printf("fragment size must be a power of 2, not %d\n", + sblock.fs_fsize); + exit(17); + } + if (sblock.fs_fsize < sectorsize) { + printf("fragment size %d is too small, minimum is %d\n", + sblock.fs_fsize, sectorsize); + exit(18); + } + if (sblock.fs_bsize < MINBSIZE) { + printf("block size %d is too small, minimum is %d\n", + sblock.fs_bsize, MINBSIZE); + exit(19); + } + if (sblock.fs_bsize > FFS_MAXBSIZE) { + printf("block size %d is too large, maximum is %d\n", + sblock.fs_bsize, FFS_MAXBSIZE); + exit(19); + } + if (sblock.fs_bsize < sblock.fs_fsize) { + printf("block size (%d) cannot be smaller than fragment size (%d)\n", + sblock.fs_bsize, sblock.fs_fsize); + exit(20); + } + + if (maxbsize < bsize || !POWEROF2(maxbsize)) { + sblock.fs_maxbsize = sblock.fs_bsize; + printf("Extent size set to %d\n", sblock.fs_maxbsize); + } else if (sblock.fs_maxbsize > FS_MAXCONTIG * sblock.fs_bsize) { + sblock.fs_maxbsize = FS_MAXCONTIG * sblock.fs_bsize; + printf("Extent size reduced to %d\n", sblock.fs_maxbsize); + } else { + sblock.fs_maxbsize = maxbsize; + } + sblock.fs_maxcontig = maxcontig; + if (sblock.fs_maxcontig < sblock.fs_maxbsize / sblock.fs_bsize) { + sblock.fs_maxcontig = sblock.fs_maxbsize / sblock.fs_bsize; + printf("Maxcontig raised to %d\n", sblock.fs_maxbsize); + } + + if (sblock.fs_maxcontig > 1) + sblock.fs_contigsumsize = MIN(sblock.fs_maxcontig,FS_MAXCONTIG); + + sblock.fs_bmask = ~(sblock.fs_bsize - 1); + sblock.fs_fmask = ~(sblock.fs_fsize - 1); + sblock.fs_qbmask = ~sblock.fs_bmask; + sblock.fs_qfmask = ~sblock.fs_fmask; + for (sblock.fs_bshift = 0, i = sblock.fs_bsize; i > 1; i >>= 1) + sblock.fs_bshift++; + for (sblock.fs_fshift = 0, i = sblock.fs_fsize; i > 1; i >>= 1) + sblock.fs_fshift++; + sblock.fs_frag = ffs_numfrags(&sblock, sblock.fs_bsize); + for (sblock.fs_fragshift = 0, i = sblock.fs_frag; i > 1; i >>= 1) + sblock.fs_fragshift++; + if (sblock.fs_frag > MAXFRAG) { + printf("fragment size %d is too small, " + "minimum with block size %d is %d\n", + sblock.fs_fsize, sblock.fs_bsize, + sblock.fs_bsize / MAXFRAG); + exit(21); + } + sblock.fs_fsbtodb = ilog2(sblock.fs_fsize / sectorsize); + sblock.fs_size = fssize = FFS_DBTOFSB(&sblock, fssize); + + if (Oflag <= 1) { + sblock.fs_magic = FS_UFS1_MAGIC; + sblock.fs_sblockloc = SBLOCK_UFS1; + sblock.fs_nindir = sblock.fs_bsize / sizeof(int32_t); + sblock.fs_inopb = sblock.fs_bsize / sizeof(struct ufs1_dinode); + sblock.fs_maxsymlinklen = ((UFS_NDADDR + UFS_NIADDR) * + sizeof (int32_t)); + sblock.fs_old_inodefmt = FS_44INODEFMT; + sblock.fs_old_cgoffset = 0; + sblock.fs_old_cgmask = 0xffffffff; + sblock.fs_old_size = sblock.fs_size; + sblock.fs_old_rotdelay = 0; + sblock.fs_old_rps = 60; + sblock.fs_old_nspf = sblock.fs_fsize / sectorsize; + sblock.fs_old_cpg = 1; + sblock.fs_old_interleave = 1; + sblock.fs_old_trackskew = 0; + sblock.fs_old_cpc = 0; + sblock.fs_old_postblformat = 1; + sblock.fs_old_nrpos = 1; + } else { + sblock.fs_magic = FS_UFS2_MAGIC; +#if 0 /* XXX makefs is used for small filesystems. */ + sblock.fs_sblockloc = SBLOCK_UFS2; +#else + sblock.fs_sblockloc = SBLOCK_UFS1; +#endif + sblock.fs_nindir = sblock.fs_bsize / sizeof(int64_t); + sblock.fs_inopb = sblock.fs_bsize / sizeof(struct ufs2_dinode); + sblock.fs_maxsymlinklen = ((UFS_NDADDR + UFS_NIADDR) * + sizeof (int64_t)); + } + + sblock.fs_sblkno = + roundup(howmany(sblock.fs_sblockloc + SBLOCKSIZE, sblock.fs_fsize), + sblock.fs_frag); + sblock.fs_cblkno = (daddr_t)(sblock.fs_sblkno + + roundup(howmany(SBLOCKSIZE, sblock.fs_fsize), sblock.fs_frag)); + sblock.fs_iblkno = sblock.fs_cblkno + sblock.fs_frag; + sblock.fs_maxfilesize = sblock.fs_bsize * UFS_NDADDR - 1; + for (sizepb = sblock.fs_bsize, i = 0; i < UFS_NIADDR; i++) { + sizepb *= FFS_NINDIR(&sblock); + sblock.fs_maxfilesize += sizepb; + } + + /* + * Calculate the number of blocks to put into each cylinder group. + * + * This algorithm selects the number of blocks per cylinder + * group. The first goal is to have at least enough data blocks + * in each cylinder group to meet the density requirement. Once + * this goal is achieved we try to expand to have at least + * 1 cylinder group. Once this goal is achieved, we pack as + * many blocks into each cylinder group map as will fit. + * + * We start by calculating the smallest number of blocks that we + * can put into each cylinder group. If this is too big, we reduce + * the density until it fits. + */ + origdensity = density; + for (;;) { + fragsperinode = MAX(ffs_numfrags(&sblock, density), 1); + minfpg = fragsperinode * FFS_INOPB(&sblock); + if (minfpg > sblock.fs_size) + minfpg = sblock.fs_size; + sblock.fs_ipg = FFS_INOPB(&sblock); + sblock.fs_fpg = roundup(sblock.fs_iblkno + + sblock.fs_ipg / FFS_INOPF(&sblock), sblock.fs_frag); + if (sblock.fs_fpg < minfpg) + sblock.fs_fpg = minfpg; + sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode), + FFS_INOPB(&sblock)); + sblock.fs_fpg = roundup(sblock.fs_iblkno + + sblock.fs_ipg / FFS_INOPF(&sblock), sblock.fs_frag); + if (sblock.fs_fpg < minfpg) + sblock.fs_fpg = minfpg; + sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode), + FFS_INOPB(&sblock)); + if (CGSIZE(&sblock) < (unsigned long)sblock.fs_bsize) + break; + density -= sblock.fs_fsize; + } + if (density != origdensity) + printf("density reduced from %d to %d\n", origdensity, density); + + if (maxblkspercg <= 0 || maxblkspercg >= fssize) + maxblkspercg = fssize - 1; + /* + * Start packing more blocks into the cylinder group until + * it cannot grow any larger, the number of cylinder groups + * drops below 1, or we reach the size requested. + */ + for ( ; sblock.fs_fpg < maxblkspercg; sblock.fs_fpg += sblock.fs_frag) { + sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode), + FFS_INOPB(&sblock)); + if (sblock.fs_size / sblock.fs_fpg < 1) + break; + if (CGSIZE(&sblock) < (unsigned long)sblock.fs_bsize) + continue; + if (CGSIZE(&sblock) == (unsigned long)sblock.fs_bsize) + break; + sblock.fs_fpg -= sblock.fs_frag; + sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode), + FFS_INOPB(&sblock)); + break; + } + /* + * Check to be sure that the last cylinder group has enough blocks + * to be viable. If it is too small, reduce the number of blocks + * per cylinder group which will have the effect of moving more + * blocks into the last cylinder group. + */ + optimalfpg = sblock.fs_fpg; + for (;;) { + sblock.fs_ncg = howmany(sblock.fs_size, sblock.fs_fpg); + lastminfpg = roundup(sblock.fs_iblkno + + sblock.fs_ipg / FFS_INOPF(&sblock), sblock.fs_frag); + if (sblock.fs_size < lastminfpg) { + printf("Filesystem size %lld < minimum size of %d\n", + (long long)sblock.fs_size, lastminfpg); + exit(28); + } + if (sblock.fs_size % sblock.fs_fpg >= lastminfpg || + sblock.fs_size % sblock.fs_fpg == 0) + break; + sblock.fs_fpg -= sblock.fs_frag; + sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode), + FFS_INOPB(&sblock)); + } + if (optimalfpg != sblock.fs_fpg) + printf("Reduced frags per cylinder group from %d to %d %s\n", + optimalfpg, sblock.fs_fpg, "to enlarge last cyl group"); + sblock.fs_cgsize = ffs_fragroundup(&sblock, CGSIZE(&sblock)); + sblock.fs_dblkno = sblock.fs_iblkno + sblock.fs_ipg / FFS_INOPF(&sblock); + if (Oflag <= 1) { + sblock.fs_old_spc = sblock.fs_fpg * sblock.fs_old_nspf; + sblock.fs_old_nsect = sblock.fs_old_spc; + sblock.fs_old_npsect = sblock.fs_old_spc; + sblock.fs_old_ncyl = sblock.fs_ncg; + } + + /* + * fill in remaining fields of the super block + */ + sblock.fs_csaddr = cgdmin(&sblock, 0); + sblock.fs_cssize = + ffs_fragroundup(&sblock, sblock.fs_ncg * sizeof(struct csum)); + + /* + * Setup memory for temporary in-core cylgroup summaries. + * Cribbed from ffs_mountfs(). + */ + size = sblock.fs_cssize; + if (sblock.fs_contigsumsize > 0) + size += sblock.fs_ncg * sizeof(int32_t); + space = ecalloc(1, size); + sblock.fs_csp = space; + space = (char *)space + sblock.fs_cssize; + if (sblock.fs_contigsumsize > 0) { + int32_t *lp; + + sblock.fs_maxcluster = lp = space; + for (i = 0; i < sblock.fs_ncg; i++) + *lp++ = sblock.fs_contigsumsize; + } + + sblock.fs_sbsize = ffs_fragroundup(&sblock, sizeof(struct fs)); + if (sblock.fs_sbsize > SBLOCKSIZE) + sblock.fs_sbsize = SBLOCKSIZE; + sblock.fs_minfree = minfree; + sblock.fs_maxcontig = maxcontig; + sblock.fs_maxbpg = maxbpg; + sblock.fs_optim = opt; + sblock.fs_cgrotor = 0; + sblock.fs_pendingblocks = 0; + sblock.fs_pendinginodes = 0; + sblock.fs_cstotal.cs_ndir = 0; + sblock.fs_cstotal.cs_nbfree = 0; + sblock.fs_cstotal.cs_nifree = 0; + sblock.fs_cstotal.cs_nffree = 0; + sblock.fs_fmod = 0; + sblock.fs_ronly = 0; + sblock.fs_state = 0; + sblock.fs_clean = FS_ISCLEAN; + sblock.fs_ronly = 0; + sblock.fs_id[0] = start_time.tv_sec; + sblock.fs_id[1] = random(); + sblock.fs_fsmnt[0] = '\0'; + csfrags = howmany(sblock.fs_cssize, sblock.fs_fsize); + sblock.fs_dsize = sblock.fs_size - sblock.fs_sblkno - + sblock.fs_ncg * (sblock.fs_dblkno - sblock.fs_sblkno); + sblock.fs_cstotal.cs_nbfree = + ffs_fragstoblks(&sblock, sblock.fs_dsize) - + howmany(csfrags, sblock.fs_frag); + sblock.fs_cstotal.cs_nffree = + ffs_fragnum(&sblock, sblock.fs_size) + + (ffs_fragnum(&sblock, csfrags) > 0 ? + sblock.fs_frag - ffs_fragnum(&sblock, csfrags) : 0); + sblock.fs_cstotal.cs_nifree = sblock.fs_ncg * sblock.fs_ipg - UFS_ROOTINO; + sblock.fs_cstotal.cs_ndir = 0; + sblock.fs_dsize -= csfrags; + sblock.fs_time = start_time.tv_sec; + if (Oflag <= 1) { + sblock.fs_old_time = start_time.tv_sec; + sblock.fs_old_dsize = sblock.fs_dsize; + sblock.fs_old_csaddr = sblock.fs_csaddr; + sblock.fs_old_cstotal.cs_ndir = sblock.fs_cstotal.cs_ndir; + sblock.fs_old_cstotal.cs_nbfree = sblock.fs_cstotal.cs_nbfree; + sblock.fs_old_cstotal.cs_nifree = sblock.fs_cstotal.cs_nifree; + sblock.fs_old_cstotal.cs_nffree = sblock.fs_cstotal.cs_nffree; + } + /* + * Dump out summary information about file system. + */ +#define B2MBFACTOR (1 / (1024.0 * 1024.0)) + printf("%s: %.1fMB (%lld sectors) block size %d, " + "fragment size %d\n", + fsys, (float)sblock.fs_size * sblock.fs_fsize * B2MBFACTOR, + (long long)FFS_FSBTODB(&sblock, sblock.fs_size), + sblock.fs_bsize, sblock.fs_fsize); + printf("\tusing %d cylinder groups of %.2fMB, %d blks, " + "%d inodes.\n", + sblock.fs_ncg, + (float)sblock.fs_fpg * sblock.fs_fsize * B2MBFACTOR, + sblock.fs_fpg / sblock.fs_frag, sblock.fs_ipg); +#undef B2MBFACTOR + /* + * Now determine how wide each column will be, and calculate how + * many columns will fit in a 76 char line. 76 is the width of the + * subwindows in sysinst. + */ + printcolwidth = count_digits( + FFS_FSBTODB(&sblock, cgsblock(&sblock, sblock.fs_ncg -1))); + nprintcols = 76 / (printcolwidth + 2); + + /* + * allocate space for superblock, cylinder group map, and + * two sets of inode blocks. + */ + if (sblock.fs_bsize < SBLOCKSIZE) + iobufsize = SBLOCKSIZE + 3 * sblock.fs_bsize; + else + iobufsize = 4 * sblock.fs_bsize; + iobuf = ecalloc(1, iobufsize); + /* + * Make a copy of the superblock into the buffer that we will be + * writing out in each cylinder group. + */ + memcpy(writebuf, &sblock, sbsize); + if (fsopts->needswap) + ffs_sb_swap(&sblock, (struct fs*)writebuf); + memcpy(iobuf, writebuf, SBLOCKSIZE); + + printf("super-block backups (for fsck -b #) at:"); + for (cylno = 0; cylno < sblock.fs_ncg; cylno++) { + initcg(cylno, start_time.tv_sec, fsopts); + if (cylno % nprintcols == 0) + printf("\n"); + printf(" %*lld,", printcolwidth, + (long long)FFS_FSBTODB(&sblock, cgsblock(&sblock, cylno))); + fflush(stdout); + } + printf("\n"); + + /* + * Now construct the initial file system, + * then write out the super-block. + */ + sblock.fs_time = start_time.tv_sec; + if (Oflag <= 1) { + sblock.fs_old_cstotal.cs_ndir = sblock.fs_cstotal.cs_ndir; + sblock.fs_old_cstotal.cs_nbfree = sblock.fs_cstotal.cs_nbfree; + sblock.fs_old_cstotal.cs_nifree = sblock.fs_cstotal.cs_nifree; + sblock.fs_old_cstotal.cs_nffree = sblock.fs_cstotal.cs_nffree; + } + if (fsopts->needswap) + sblock.fs_flags |= FS_SWAPPED; + ffs_write_superblock(&sblock, fsopts); + return (&sblock); +} + +/* + * Write out the superblock and its duplicates, + * and the cylinder group summaries + */ +void +ffs_write_superblock(struct fs *fs, const fsinfo_t *fsopts) +{ + int cylno, size, blks, i, saveflag; + void *space; + char *wrbuf; + + saveflag = fs->fs_flags & FS_INTERNAL; + fs->fs_flags &= ~FS_INTERNAL; + + memcpy(writebuf, &sblock, sbsize); + if (fsopts->needswap) + ffs_sb_swap(fs, (struct fs*)writebuf); + ffs_wtfs(fs->fs_sblockloc / sectorsize, sbsize, writebuf, fsopts); + + /* Write out the duplicate super blocks */ + for (cylno = 0; cylno < fs->fs_ncg; cylno++) + ffs_wtfs(FFS_FSBTODB(fs, cgsblock(fs, cylno)), + sbsize, writebuf, fsopts); + + /* Write out the cylinder group summaries */ + size = fs->fs_cssize; + blks = howmany(size, fs->fs_fsize); + space = (void *)fs->fs_csp; + wrbuf = emalloc(size); + for (i = 0; i < blks; i+= fs->fs_frag) { + size = fs->fs_bsize; + if (i + fs->fs_frag > blks) + size = (blks - i) * fs->fs_fsize; + if (fsopts->needswap) + ffs_csum_swap((struct csum *)space, + (struct csum *)wrbuf, size); + else + memcpy(wrbuf, space, (u_int)size); + ffs_wtfs(FFS_FSBTODB(fs, fs->fs_csaddr + i), size, wrbuf, fsopts); + space = (char *)space + size; + } + free(wrbuf); + fs->fs_flags |= saveflag; +} + +/* + * Initialize a cylinder group. + */ +static void +initcg(int cylno, time_t utime, const fsinfo_t *fsopts) +{ + daddr_t cbase, dmax; + int i, j, d, dlower, dupper, blkno; + struct ufs1_dinode *dp1; + struct ufs2_dinode *dp2; + int start; + + /* + * Determine block bounds for cylinder group. + * Allow space for super block summary information in first + * cylinder group. + */ + cbase = cgbase(&sblock, cylno); + dmax = cbase + sblock.fs_fpg; + if (dmax > sblock.fs_size) + dmax = sblock.fs_size; + dlower = cgsblock(&sblock, cylno) - cbase; + dupper = cgdmin(&sblock, cylno) - cbase; + if (cylno == 0) + dupper += howmany(sblock.fs_cssize, sblock.fs_fsize); + memset(&acg, 0, sblock.fs_cgsize); + acg.cg_time = utime; + acg.cg_magic = CG_MAGIC; + acg.cg_cgx = cylno; + acg.cg_niblk = sblock.fs_ipg; + acg.cg_initediblk = sblock.fs_ipg < 2 * FFS_INOPB(&sblock) ? + sblock.fs_ipg : 2 * FFS_INOPB(&sblock); + acg.cg_ndblk = dmax - cbase; + if (sblock.fs_contigsumsize > 0) + acg.cg_nclusterblks = acg.cg_ndblk >> sblock.fs_fragshift; + start = &acg.cg_space[0] - (u_char *)(&acg.cg_firstfield); + if (Oflag == 2) { + acg.cg_iusedoff = start; + } else { + if (cylno == sblock.fs_ncg - 1) + acg.cg_old_ncyl = howmany(acg.cg_ndblk, + sblock.fs_fpg / sblock.fs_old_cpg); + else + acg.cg_old_ncyl = sblock.fs_old_cpg; + acg.cg_old_time = acg.cg_time; + acg.cg_time = 0; + acg.cg_old_niblk = acg.cg_niblk; + acg.cg_niblk = 0; + acg.cg_initediblk = 0; + acg.cg_old_btotoff = start; + acg.cg_old_boff = acg.cg_old_btotoff + + sblock.fs_old_cpg * sizeof(int32_t); + acg.cg_iusedoff = acg.cg_old_boff + + sblock.fs_old_cpg * sizeof(u_int16_t); + } + acg.cg_freeoff = acg.cg_iusedoff + howmany(sblock.fs_ipg, CHAR_BIT); + if (sblock.fs_contigsumsize <= 0) { + acg.cg_nextfreeoff = acg.cg_freeoff + + howmany(sblock.fs_fpg, CHAR_BIT); + } else { + acg.cg_clustersumoff = acg.cg_freeoff + + howmany(sblock.fs_fpg, CHAR_BIT) - sizeof(int32_t); + acg.cg_clustersumoff = + roundup(acg.cg_clustersumoff, sizeof(int32_t)); + acg.cg_clusteroff = acg.cg_clustersumoff + + (sblock.fs_contigsumsize + 1) * sizeof(int32_t); + acg.cg_nextfreeoff = acg.cg_clusteroff + + howmany(ffs_fragstoblks(&sblock, sblock.fs_fpg), CHAR_BIT); + } + if (acg.cg_nextfreeoff > sblock.fs_cgsize) { + printf("Panic: cylinder group too big\n"); + exit(37); + } + acg.cg_cs.cs_nifree += sblock.fs_ipg; + if (cylno == 0) { + size_t r; + + for (r = 0; r < UFS_ROOTINO; r++) { + setbit(cg_inosused(&acg, 0), r); + acg.cg_cs.cs_nifree--; + } + } + if (cylno > 0) { + /* + * In cylno 0, beginning space is reserved + * for boot and super blocks. + */ + for (d = 0, blkno = 0; d < dlower;) { + ffs_setblock(&sblock, cg_blksfree(&acg, 0), blkno); + if (sblock.fs_contigsumsize > 0) + setbit(cg_clustersfree(&acg, 0), blkno); + acg.cg_cs.cs_nbfree++; + d += sblock.fs_frag; + blkno++; + } + } + if ((i = (dupper & (sblock.fs_frag - 1))) != 0) { + acg.cg_frsum[sblock.fs_frag - i]++; + for (d = dupper + sblock.fs_frag - i; dupper < d; dupper++) { + setbit(cg_blksfree(&acg, 0), dupper); + acg.cg_cs.cs_nffree++; + } + } + for (d = dupper, blkno = dupper >> sblock.fs_fragshift; + d + sblock.fs_frag <= acg.cg_ndblk; ) { + ffs_setblock(&sblock, cg_blksfree(&acg, 0), blkno); + if (sblock.fs_contigsumsize > 0) + setbit(cg_clustersfree(&acg, 0), blkno); + acg.cg_cs.cs_nbfree++; + d += sblock.fs_frag; + blkno++; + } + if (d < acg.cg_ndblk) { + acg.cg_frsum[acg.cg_ndblk - d]++; + for (; d < acg.cg_ndblk; d++) { + setbit(cg_blksfree(&acg, 0), d); + acg.cg_cs.cs_nffree++; + } + } + if (sblock.fs_contigsumsize > 0) { + int32_t *sump = cg_clustersum(&acg, 0); + u_char *mapp = cg_clustersfree(&acg, 0); + int map = *mapp++; + int bit = 1; + int run = 0; + + for (i = 0; i < acg.cg_nclusterblks; i++) { + if ((map & bit) != 0) { + run++; + } else if (run != 0) { + if (run > sblock.fs_contigsumsize) + run = sblock.fs_contigsumsize; + sump[run]++; + run = 0; + } + if ((i & (CHAR_BIT - 1)) != (CHAR_BIT - 1)) { + bit <<= 1; + } else { + map = *mapp++; + bit = 1; + } + } + if (run != 0) { + if (run > sblock.fs_contigsumsize) + run = sblock.fs_contigsumsize; + sump[run]++; + } + } + sblock.fs_cs(&sblock, cylno) = acg.cg_cs; + /* + * Write out the duplicate super block, the cylinder group map + * and two blocks worth of inodes in a single write. + */ + start = sblock.fs_bsize > SBLOCKSIZE ? sblock.fs_bsize : SBLOCKSIZE; + memcpy(&iobuf[start], &acg, sblock.fs_cgsize); + if (fsopts->needswap) + ffs_cg_swap(&acg, (struct cg*)&iobuf[start], &sblock); + start += sblock.fs_bsize; + dp1 = (struct ufs1_dinode *)(&iobuf[start]); + dp2 = (struct ufs2_dinode *)(&iobuf[start]); + for (i = 0; i < acg.cg_initediblk; i++) { + if (sblock.fs_magic == FS_UFS1_MAGIC) { + /* No need to swap, it'll stay random */ + dp1->di_gen = random(); + dp1++; + } else { + dp2->di_gen = random(); + dp2++; + } + } + ffs_wtfs(FFS_FSBTODB(&sblock, cgsblock(&sblock, cylno)), iobufsize, iobuf, + fsopts); + /* + * For the old file system, we have to initialize all the inodes. + */ + if (Oflag <= 1) { + for (i = 2 * sblock.fs_frag; + i < sblock.fs_ipg / FFS_INOPF(&sblock); + i += sblock.fs_frag) { + dp1 = (struct ufs1_dinode *)(&iobuf[start]); + for (j = 0; j < FFS_INOPB(&sblock); j++) { + dp1->di_gen = random(); + dp1++; + } + ffs_wtfs(FFS_FSBTODB(&sblock, cgimin(&sblock, cylno) + i), + sblock.fs_bsize, &iobuf[start], fsopts); + } + } +} + +/* + * read a block from the file system + */ +void +ffs_rdfs(daddr_t bno, int size, void *bf, const fsinfo_t *fsopts) +{ + int n; + off_t offset; + + offset = bno * fsopts->sectorsize + fsopts->offset; + if (lseek(fsopts->fd, offset, SEEK_SET) < 0) + err(1, "%s: seek error for sector %lld", __func__, + (long long)bno); + n = read(fsopts->fd, bf, size); + if (n == -1) { + err(1, "%s: read error bno %lld size %d", __func__, + (long long)bno, size); + } + else if (n != size) + errx(1, "%s: short read error for sector %lld", __func__, + (long long)bno); +} + +/* + * write a block to the file system + */ +void +ffs_wtfs(daddr_t bno, int size, void *bf, const fsinfo_t *fsopts) +{ + int n; + off_t offset; + + offset = bno * fsopts->sectorsize + fsopts->offset; + if (lseek(fsopts->fd, offset, SEEK_SET) == -1) + err(1, "%s: seek error for sector %lld", __func__, + (long long)bno); + n = write(fsopts->fd, bf, size); + if (n == -1) + err(1, "%s: write error for sector %lld", __func__, + (long long)bno); + else if (n != size) + errx(1, "%s: short write error for sector %lld", __func__, + (long long)bno); +} + + +/* Determine how many digits are needed to print a given integer */ +static int +count_digits(int num) +{ + int ndig; + + for(ndig = 1; num > 9; num /=10, ndig++); + + return (ndig); +} + +static int +ilog2(int val) +{ + u_int n; + + for (n = 0; n < sizeof(n) * CHAR_BIT; n++) + if (1 << n == val) + return (n); + errx(1, "%s: %d is not a power of 2", __func__, val); +} diff --git a/usr.sbin/makefs/ffs/newfs_extern.h b/usr.sbin/makefs/ffs/newfs_extern.h new file mode 100644 index 000000000..d86b248dd --- /dev/null +++ b/usr.sbin/makefs/ffs/newfs_extern.h @@ -0,0 +1,34 @@ +/* $NetBSD: newfs_extern.h,v 1.3 2009/10/21 01:07:47 snj Exp $ */ +/* From: NetBSD: extern.h,v 1.3 2000/12/01 12:03:27 simonb Exp $ */ + +/* + * Copyright (c) 1997 Christos Zoulas. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* prototypes */ +struct fs *ffs_mkfs(const char *, const fsinfo_t *); +void ffs_write_superblock(struct fs *, const fsinfo_t *); +void ffs_rdfs(daddr_t, int, void *, const fsinfo_t *); +void ffs_wtfs(daddr_t, int, void *, const fsinfo_t *); + +#define FFS_MAXBSIZE 65536 diff --git a/usr.sbin/makefs/ffs/ufs_bmap.c b/usr.sbin/makefs/ffs/ufs_bmap.c new file mode 100644 index 000000000..1135b6505 --- /dev/null +++ b/usr.sbin/makefs/ffs/ufs_bmap.c @@ -0,0 +1,145 @@ +/* $NetBSD: ufs_bmap.c,v 1.18 2013/06/19 17:51:27 dholland Exp $ */ +/* From: NetBSD: ufs_bmap.c,v 1.14 2001/11/08 05:00:51 chs Exp */ + +/* + * Copyright (c) 1989, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ufs_bmap.c 8.8 (Berkeley) 8/11/95 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: ufs_bmap.c,v 1.18 2013/06/19 17:51:27 dholland Exp $"); +#endif /* !__lint */ + +#include +#include + +#include +#include +#include + +#include "makefs.h" + +#include +#include +#include + +#include "ffs/ufs_inode.h" +#include "ffs/ffs_extern.h" + +/* + * Create an array of logical block number/offset pairs which represent the + * path of indirect blocks required to access a data block. The first "pair" + * contains the logical block number of the appropriate single, double or + * triple indirect block and the offset into the inode indirect block array. + * Note, the logical block number of the inode single/double/triple indirect + * block appears twice in the array, once with the offset into the i_ffs_ib and + * once with the offset into the page itself. + */ +int +ufs_getlbns(struct inode *ip, daddr_t bn, struct indir *ap, int *nump) +{ + daddr_t metalbn, realbn; + int64_t blockcnt; + int lbc; + int i, numlevels, off; + u_long lognindir; + + lognindir = ffs(FFS_NINDIR(ip->i_fs)) - 1; + if (nump) + *nump = 0; + numlevels = 0; + realbn = bn; + if ((long)bn < 0) + bn = -(long)bn; + + assert (bn >= UFS_NDADDR); + + /* + * Determine the number of levels of indirection. After this loop + * is done, blockcnt indicates the number of data blocks possible + * at the given level of indirection, and UFS_NIADDR - i is the number + * of levels of indirection needed to locate the requested block. + */ + + bn -= UFS_NDADDR; + for (lbc = 0, i = UFS_NIADDR;; i--, bn -= blockcnt) { + if (i == 0) + return (EFBIG); + + lbc += lognindir; + blockcnt = (int64_t)1 << lbc; + + if (bn < blockcnt) + break; + } + + /* Calculate the address of the first meta-block. */ + metalbn = -((realbn >= 0 ? realbn : -realbn) - bn + UFS_NIADDR - i); + + /* + * At each iteration, off is the offset into the bap array which is + * an array of disk addresses at the current level of indirection. + * The logical block number and the offset in that block are stored + * into the argument array. + */ + ap->in_lbn = metalbn; + ap->in_off = off = UFS_NIADDR - i; + ap->in_exists = 0; + ap++; + for (++numlevels; i <= UFS_NIADDR; i++) { + /* If searching for a meta-data block, quit when found. */ + if (metalbn == realbn) + break; + + lbc -= lognindir; + blockcnt = (int64_t)1 << lbc; + off = (bn >> lbc) & (FFS_NINDIR(ip->i_fs) - 1); + + ++numlevels; + ap->in_lbn = metalbn; + ap->in_off = off; + ap->in_exists = 0; + ++ap; + + metalbn -= -1 + (off << lbc); + } + if (nump) + *nump = numlevels; + return (0); +} diff --git a/usr.sbin/makefs/ffs/ufs_inode.h b/usr.sbin/makefs/ffs/ufs_inode.h new file mode 100644 index 000000000..cc2d5893f --- /dev/null +++ b/usr.sbin/makefs/ffs/ufs_inode.h @@ -0,0 +1,112 @@ +/* $NetBSD: ufs_inode.h,v 1.5 2013/01/30 19:19:19 christos Exp $ */ +/* From: NetBSD: inode.h,v 1.27 2001/12/18 10:57:23 fvdl Exp $ */ + +/* + * Copyright (c) 1982, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)inode.h 8.9 (Berkeley) 5/14/95 + */ + +union dinode { + struct ufs1_dinode ffs1_din; + struct ufs2_dinode ffs2_din; +}; + +struct inode { + ino_t i_number; /* The identity of the inode. */ + struct vnode *i_devvp; /* device vnode for block I/O */ + struct fs *i_fs; /* File system */ + union dinode i_din; + uint64_t i_size; +}; + +#define i_ffs1_atime i_din.ffs1_din.di_atime +#define i_ffs1_atimensec i_din.ffs1_din.di_atimensec +#define i_ffs1_blocks i_din.ffs1_din.di_blocks +#define i_ffs1_ctime i_din.ffs1_din.di_ctime +#define i_ffs1_ctimensec i_din.ffs1_din.di_ctimensec +#define i_ffs1_db i_din.ffs1_din.di_db +#define i_ffs1_flags i_din.ffs1_din.di_flags +#define i_ffs1_gen i_din.ffs1_din.di_gen +#define i_ffs11_gid i_din.ffs1_din.di_gid +#define i_ffs1_ib i_din.ffs1_din.di_ib +#define i_ffs1_mode i_din.ffs1_din.di_mode +#define i_ffs1_mtime i_din.ffs1_din.di_mtime +#define i_ffs1_mtimensec i_din.ffs1_din.di_mtimensec +#define i_ffs1_nlink i_din.ffs1_din.di_nlink +#define i_ffs1_rdev i_din.ffs1_din.di_rdev +#define i_ffs1_shortlink i_din.ffs1_din.db +#define i_ffs1_size i_din.ffs1_din.di_size +#define i_ffs1_uid i_din.ffs1_din.di_uid + +#define i_ffs2_atime i_din.ffs2_din.di_atime +#define i_ffs2_atimensec i_din.ffs2_din.di_atimensec +#define i_ffs2_blocks i_din.ffs2_din.di_blocks +#define i_ffs2_ctime i_din.ffs2_din.di_ctime +#define i_ffs2_ctimensec i_din.ffs2_din.di_ctimensec +#define i_ffs2_birthtime i_din.ffs2_din.di_birthtime +#define i_ffs2_birthnsec i_din.ffs2_din.di_birthnsec +#define i_ffs2_db i_din.ffs2_din.di_db +#define i_ffs2_flags i_din.ffs2_din.di_flags +#define i_ffs2_gen i_din.ffs2_din.di_gen +#define i_ffs21_gid i_din.ffs2_din.di_gid +#define i_ffs2_ib i_din.ffs2_din.di_ib +#define i_ffs2_mode i_din.ffs2_din.di_mode +#define i_ffs2_mtime i_din.ffs2_din.di_mtime +#define i_ffs2_mtimensec i_din.ffs2_din.di_mtimensec +#define i_ffs2_nlink i_din.ffs2_din.di_nlink +#define i_ffs2_rdev i_din.ffs2_din.di_rdev +#define i_ffs2_shortlink i_din.ffs2_din.db +#define i_ffs2_size i_din.ffs2_din.di_size +#define i_ffs2_uid i_din.ffs2_din.di_uid + +#undef DIP +#define DIP(ip, field) \ + (((ip)->i_fs->fs_magic == FS_UFS1_MAGIC) ? \ + (ip)->i_ffs1_##field : (ip)->i_ffs2_##field) + +#define DIP_ASSIGN(ip, field, value) \ + do { \ + if ((ip)->i_fs->fs_magic == FS_UFS1_MAGIC) \ + (ip)->i_ffs1_##field = (value); \ + else \ + (ip)->i_ffs2_##field = (value); \ + } while(0) + +#define DIP_ADD(ip, field, value) \ + do { \ + if ((ip)->i_fs->fs_magic == FS_UFS1_MAGIC) \ + (ip)->i_ffs1_##field += (value); \ + else \ + (ip)->i_ffs2_##field += (value); \ + } while(0) diff --git a/usr.sbin/makefs/makefs.8 b/usr.sbin/makefs/makefs.8 new file mode 100644 index 000000000..92f8fb40d --- /dev/null +++ b/usr.sbin/makefs/makefs.8 @@ -0,0 +1,460 @@ +.\" $NetBSD: makefs.8,v 1.53 2013/08/06 20:16:54 wiz Exp $ +.\" +.\" Copyright (c) 2001-2003 Wasabi Systems, Inc. +.\" All rights reserved. +.\" +.\" Written by Luke Mewburn for Wasabi Systems, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed for the NetBSD Project by +.\" Wasabi Systems, Inc. +.\" 4. The name of Wasabi Systems, Inc. may not be used to endorse +.\" or promote products derived from this software without specific prior +.\" written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd August 6, 2013 +.Dt MAKEFS 8 +.Os +.Sh NAME +.Nm makefs +.Nd create a file system image from a directory tree +.Sh SYNOPSIS +.Nm +.Op Fl rxZ +.Op Fl B Ar endian +.Op Fl b Ar free-blocks +.Op Fl d Ar debug-mask +.Op Fl F Ar mtree-specfile +.Op Fl f Ar free-files +.Op Fl M Ar minimum-size +.Op Fl m Ar maximum-size +.Op Fl N Ar userdb-dir +.Op Fl O Ar offset +.Op Fl o Ar fs-options +.Op Fl S Ar sector-size +.Op Fl s Ar image-size +.Op Fl t Ar fs-type +.Ar image-file +.Ar directory +.Op Ar extra-directory ... +.Sh DESCRIPTION +The utility +.Nm +creates a file system image into +.Ar image-file +from the directory tree +.Ar directory . +If any optional directory trees are passed in the +.Ar extra-directory +arguments, then the directory tree of each argument will be merged +into the +.Ar directory +first before creating +.Ar image-file . +No special devices or privileges are required to perform this task. +.Pp +The options are as follows: +.Bl -tag -width flag +.It Fl B Ar endian +Set the byte order of the image to +.Ar endian . +Valid byte orders are +.Ql 4321 , +.Ql big , +or +.Ql be +for big endian, and +.Ql 1234 , +.Ql little , +or +.Ql le +for little endian. +Some file systems may have a fixed byte order; in those cases this +argument will be ignored. +.It Fl b Ar free-blocks +Ensure that a minimum of +.Ar free-blocks +free blocks exist in the image. +An optional +.Ql % +suffix may be provided to indicate that +.Ar free-blocks +indicates a percentage of the calculated image size. +.It Fl d Ar debug-mask +Enable various levels of debugging, depending upon which bits are +set in +.Ar debug-mask . +XXX: document these +.It Fl F Ar mtree-specfile +Use +.Ar mtree-specfile +as an +.Xr mtree 8 +.Sq specfile +specification. +.Pp +If a specfile entry exists in the underlying file system, its +permissions and modification time will be used unless specifically +overridden by the specfile. +An error will be raised if the type of entry in the specfile +conflicts with that of an existing entry. +.Pp +In the opposite case (where a specfile entry does not have an entry +in the underlying file system) the following occurs: +If the specfile entry is marked +.Sy optional , +the specfile entry is ignored. +Otherwise, the entry will be created in the image, and it is +necessary to specify at least the following parameters in the +specfile: +.Sy type , +.Sy mode , +.Sy gname , +or +.Sy gid , +and +.Sy uname +or +.Sy uid , +.Sy device +(in the case of block or character devices), and +.Sy link +(in the case of symbolic links). +If +.Sy time +isn't provided, the current time will be used. +If +.Sy flags +isn't provided, the current file flags will be used. +Missing regular file entries will be created as zero-length files. +.It Fl f Ar free-files +Ensure that a minimum of +.Ar free-files +free files (inodes) exist in the image. +An optional +.Ql % +suffix may be provided to indicate that +.Ar free-files +indicates a percentage of the calculated image size. +.It Fl M Ar minimum-size +Set the minimum size of the file system image to +.Ar minimum-size . +.It Fl m Ar maximum-size +Set the maximum size of the file system image to +.Ar maximum-size . +An error will be raised if the target file system needs to be larger +than this to accommodate the provided directory tree. +.It Fl N Ar userdb-dir +Use the user database text file +.Pa master.passwd +and group database text file +.Pa group +from +.Ar userdb-dir , +rather than using the results from the system's +.Xr getpwnam 3 +and +.Xr getgrnam 3 +(and related) library calls. +.It Fl O Ar offset +Instead of creating the filesystem at the beginning of the file, start +at offset. +Valid only for +.Sy ffs +and +.Sy msdos . +.It Fl o Ar fs-options +Set file system specific options. +.Ar fs-options +is a comma separated list of options. +Valid file system specific options are detailed below. +.It Fl r +When merging multiple directories replace duplicate files with the last found. +.It Fl S Ar sector-size +Set the file system sector size to +.Ar sector-size . +.\" XXX: next line also true for cd9660? +Defaults to 512. +.It Fl s Ar image-size +Set the size of the file system image to +.Ar image-size . +.It Fl t Ar fs-type +Create an +.Ar fs-type +file system image. +The following file system types are supported: +.Bl -tag -width cd9660 -offset indent +.It Sy ffs +BSD fast file system (default). +.It Sy cd9660 +ISO 9660 file system. +.It Sy chfs +Chip flash file system. +.It Sy msdos +FAT12, FAT16, or FAT32 file system. +.It Sy v7fs +7th Edition(V7) file system. +.It Sy udf +ISO/Ecma UDF file system. +.El +.It Fl x +Exclude file system nodes not explicitly listed in the specfile. +.It Fl Z +Create a sparse file for +.Sy ffs . +This is useful for virtual machine images. +.El +.Pp +Where sizes are specified, a decimal number of bytes is expected. +Two or more numbers may be separated by an +.Dq x +to indicate a product. +Each number may have one of the following optional suffixes: +.Bl -tag -width 3n -offset indent -compact +.It b +Block; multiply by 512 +.It k +Kibi; multiply by 1024 (1 KiB) +.It m +Mebi; multiply by 1048576 (1 MiB) +.It g +Gibi; multiply by 1073741824 (1 GiB) +.It t +Tebi; multiply by 1099511627776 (1 TiB) +.It w +Word; multiply by the number of bytes in an integer +.El +.\" +.\" +.Ss FFS-specific options +.Sy ffs +images have ffs-specific optional parameters that may be provided. +Each of the options consists of a keyword, an equal sign +.Pq Ql = , +and a value. +The following keywords are supported: +.Pp +.Bl -tag -width optimization -offset indent -compact +.It Sy avgfilesize +Expected average file size. +.It Sy avgfpdir +Expected number of files per directory. +.It Sy bsize +Block size. +.It Sy density +Bytes per inode. +.It Sy fsize +Fragment size. +.It Sy label +Label name of the image. +.It Sy maxbpg +Maximum blocks per file in a cylinder group. +.It Sy minfree +Minimum % free. +.It Sy optimization +Optimization preference; one of +.Ql space +or +.Ql time . +.It Sy extent +Maximum extent size. +.It Sy maxbpcg +Maximum total number of blocks in a cylinder group. +.It Sy version +UFS version. +1 for FFS (default), 2 for UFS2. +.El +.Ss CD9660-specific options +.Sy cd9660 +images have ISO9660-specific optional parameters that may be +provided. +The arguments consist of a keyword and, optionally, an equal sign +.Pq Ql = , +and a value. +The following keywords are supported: +.Pp +.Bl -tag -width omit-trailing-period -offset indent -compact +.It Sy allow-deep-trees +Allow the directory structure to exceed the maximum specified in +the spec. +.\" .It Sy allow-illegal-chars +.\" Unknown +.\" .It Sy allow-lowercase +.\" Unknown +.It Sy allow-max-name +Allow 37 instead of 33 characters for filenames by omitting the +version id. +.It Sy allow-multidot +Allow multiple dots in a filename. +.It Sy applicationid +Application ID of the image. +.It Sy archimedes +Use the +.Ql ARCHIMEDES +extension to encode +.Tn RISC OS +metadata. +.It Sy chrp-boot +Write an MBR partition table to the image to allow older CHRP hardware to +boot. +.It Sy boot-load-segment +Set load segment for the boot image. +.It Sy bootimage +Filename of a boot image in the format +.Dq sysid;filename , +where +.Dq sysid +is one of +.Ql i386 , +.Ql mac68k , +.Ql macppc , +or +.Ql powerpc . +.It Sy generic-bootimage +Load a generic boot image into the first 32K of the cd9660 image. +.It Sy hard-disk-boot +Boot image is a hard disk image. +.It Sy keep-bad-images +Don't throw away images whose write was aborted due to an error. +For debugging purposes. +.It Sy label +Label name of the image. +.It Sy no-boot +Boot image is not bootable. +.It Sy no-emul-boot +Boot image is a +.Dq no emulation +ElTorito image. +.It Sy no-trailing-padding +Do not pad the image (apparently Linux needs the padding). +.\" .It Sy omit-trailing-period +.\" Unknown +.It Sy preparer +Preparer ID of the image. +.It Sy publisher +Publisher ID of the image. +.It Sy rockridge +Use RockRidge extensions (for longer filenames, etc.). +.It Sy volumeid +Volume set identifier of the image. +.El +.Ss CHFS-specific options +.Sy chfs +images have chfs-specific optional parameters that may be provided. +Each of the options consists of a keyword, an equal sign +.Pq Ql = , +and a value. +The following keywords are supported: +.Pp +.Bl -tag -width optimization -offset indent -compact +.It Sy pagesize +Pagesize. +.It Sy erasesize +Erase block size of the media. +.It Sy mediatype +Type of the media. +NOR: 0 or NAND: 1. +.El +.Ss msdos-specific options +See +.Xr newfs_msdos 8 +for fs specific options. +.Ss V7FS-specific options +The following keywords are supported: +.Pp +.Bl -tag -width optimization -offset indent -compact +.It Sy pdp +PDP endian. +.It Sy progress +Display a progress meter for the file system construction and file +population. +.El +.Ss UDF-specific options +.Sy udf +images have udf-specific optional parameters that may be provided. +Each of the options consists of a keyword, an equal sign +.Pq Ql = , +and a value. +The following keywords are supported: +.Pp +.Bl -tag -width optimization -compact +.It Sy disctype +This can have the following values: +.Bl -tag -width cdromXdvdromXbdromXXX -compact +.It Sy cdrom , Sy dvdrom , Sy bdrom +create a read-only fs +.It Sy dvdram , Sy bdre , Sy disk +create a rewritable fs without sparing for defective sectors +.It Sy cdr , Sy dvdr , Sy bdr +create a rewritable fs on once recordable media using a VAT +.It Sy cdrw , Sy dvdrw +create a rewritable fs with sparing for defective sectors +.El +When an optical media is selected here, the sectorsize and the default disc +size is assumed unless given explicitly. +For rom images the disc size is the minimum needed. +.It Sy loglabel +Set the logical volume label of the disc to the specified argument. +.It Sy discid +Set the physical volume label of the disc to the specified argument. +Prepend the physical volume label with a volumeset label separated +with a ':' if wanted. +For strict conformance and interchange, don't set the volumeset label +manually unless it has an unique hex number in the first 8 character +positions. +.It Sy minver +Set the minimum UDF version to be used. +Choose UDF version numbers from 0x102, 0x150, 0x200, and 0x201. +Versions 0x250 and 0x260 are currently not supported +in +.Nm . +.El +.Sh SEE ALSO +.Xr strsuftoll 3 , +.Xr installboot 8 , +.Xr mtree 8 , +.Xr newfs 8 +.Sh HISTORY +The +.Nm +utility appeared in +.Nx 1.6 . +.Sh AUTHORS +.An Luke Mewburn +.Aq lukem@NetBSD.org +(original program), +.An Daniel Watt , +.An Walter Deignan , +.An Ryan Gabrys , +.An Alan Perez-Rathke , +.An Ram Vedam +(cd9660 support), +.An UCHIYAMA Yasushi +(v7fs support), +.An Tamas Toth +(chfs support). +.An Christos Zoulas +(msdos support). +.An Reinoud Zandijk +(udf support). diff --git a/usr.sbin/makefs/makefs.c b/usr.sbin/makefs/makefs.c new file mode 100644 index 000000000..116eceb45 --- /dev/null +++ b/usr.sbin/makefs/makefs.c @@ -0,0 +1,437 @@ +/* $NetBSD: makefs.c,v 1.50 2013/08/05 14:41:57 reinoud Exp $ */ + +/* + * Copyright (c) 2001-2003 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Luke Mewburn for Wasabi Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: makefs.c,v 1.50 2013/08/05 14:41:57 reinoud Exp $"); +#endif /* !__lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "makefs.h" +#include "mtree.h" +#include "cd9660.h" + +/* + * list of supported file systems and dispatch functions + */ +typedef struct { + const char *type; + void (*prepare_options)(fsinfo_t *); + int (*parse_options)(const char *, fsinfo_t *); + void (*cleanup_options)(fsinfo_t *); + void (*make_fs)(const char *, const char *, fsnode *, + fsinfo_t *); +} fstype_t; + +static fstype_t fstypes[] = { +#define ENTRY(name) { \ + # name, name ## _prep_opts, name ## _parse_opts, \ + name ## _cleanup_opts, name ## _makefs \ +} + ENTRY(ffs), + ENTRY(cd9660), + ENTRY(chfs), + ENTRY(v7fs), + ENTRY(msdos), + ENTRY(udf), + { .type = NULL }, +}; + +u_int debug; +struct timespec start_time; + +static fstype_t *get_fstype(const char *); +static void usage(fstype_t *, fsinfo_t *) __dead; + +int +main(int argc, char *argv[]) +{ + struct timeval start; + fstype_t *fstype; + fsinfo_t fsoptions; + fsnode *root; + int ch, i, len; + char *specfile; + + setprogname(argv[0]); + + debug = 0; + if ((fstype = get_fstype(DEFAULT_FSTYPE)) == NULL) + errx(1, "Unknown default fs type `%s'.", DEFAULT_FSTYPE); + + /* set default fsoptions */ + (void)memset(&fsoptions, 0, sizeof(fsoptions)); + fsoptions.fd = -1; + fsoptions.sectorsize = -1; + + if (fstype->prepare_options) + fstype->prepare_options(&fsoptions); + + specfile = NULL; + if (gettimeofday(&start, NULL) == -1) + err(1, "Unable to get system time"); + + start_time.tv_sec = start.tv_sec; + start_time.tv_nsec = start.tv_usec * 1000; + + while ((ch = getopt(argc, argv, "B:b:d:f:F:M:m:N:O:o:rs:S:t:xZ")) != -1) { + switch (ch) { + + case 'B': + if (strcmp(optarg, "be") == 0 || + strcmp(optarg, "4321") == 0 || + strcmp(optarg, "big") == 0) { +#if BYTE_ORDER == LITTLE_ENDIAN + fsoptions.needswap = 1; +#endif + } else if (strcmp(optarg, "le") == 0 || + strcmp(optarg, "1234") == 0 || + strcmp(optarg, "little") == 0) { +#if BYTE_ORDER == BIG_ENDIAN + fsoptions.needswap = 1; +#endif + } else { + warnx("Invalid endian `%s'.", optarg); + usage(fstype, &fsoptions); + } + break; + + case 'b': + len = strlen(optarg) - 1; + if (optarg[len] == '%') { + optarg[len] = '\0'; + fsoptions.freeblockpc = + strsuftoll("free block percentage", + optarg, 0, 99); + } else { + fsoptions.freeblocks = + strsuftoll("free blocks", + optarg, 0, LLONG_MAX); + } + break; + + case 'd': + debug = strtoll(optarg, NULL, 0); + break; + + case 'f': + len = strlen(optarg) - 1; + if (optarg[len] == '%') { + optarg[len] = '\0'; + fsoptions.freefilepc = + strsuftoll("free file percentage", + optarg, 0, 99); + } else { + fsoptions.freefiles = + strsuftoll("free files", + optarg, 0, LLONG_MAX); + } + break; + + case 'F': + specfile = optarg; + break; + + case 'M': + fsoptions.minsize = + strsuftoll("minimum size", optarg, 1LL, LLONG_MAX); + break; + + case 'N': + if (! setup_getid(optarg)) + errx(1, + "Unable to use user and group databases in `%s'", + optarg); + break; + + case 'm': + fsoptions.maxsize = + strsuftoll("maximum size", optarg, 1LL, LLONG_MAX); + break; + + case 'O': + fsoptions.offset = + strsuftoll("offset", optarg, 0LL, LLONG_MAX); + break; + + case 'o': + { + char *p; + + while ((p = strsep(&optarg, ",")) != NULL) { + if (*p == '\0') + errx(1, "Empty option"); + if (! fstype->parse_options(p, &fsoptions)) + usage(fstype, &fsoptions); + } + break; + } + + case 'r': + fsoptions.replace = 1; + break; + + case 's': + fsoptions.minsize = fsoptions.maxsize = + strsuftoll("size", optarg, 1LL, LLONG_MAX); + break; + + case 'S': + fsoptions.sectorsize = + (int)strsuftoll("sector size", optarg, + 1LL, INT_MAX); + break; + + case 't': + /* Check current one and cleanup if necessary. */ + if (fstype->cleanup_options) + fstype->cleanup_options(&fsoptions); + fsoptions.fs_specific = NULL; + if ((fstype = get_fstype(optarg)) == NULL) + errx(1, "Unknown fs type `%s'.", optarg); + fstype->prepare_options(&fsoptions); + break; + + case 'x': + fsoptions.onlyspec = 1; + break; + + case 'Z': + fsoptions.sparse = 1; + break; + + case '?': + default: + usage(fstype, &fsoptions); + /* NOTREACHED */ + + } + } + if (debug) { + printf("debug mask: 0x%08x\n", debug); + printf("start time: %ld.%ld, %s", + (long)start_time.tv_sec, (long)start_time.tv_nsec, + ctime(&start_time.tv_sec)); + } + argc -= optind; + argv += optind; + + if (argc < 2) + usage(fstype, &fsoptions); + + /* -x must be accompanied by -F */ + if (fsoptions.onlyspec != 0 && specfile == NULL) + errx(1, "-x requires -F mtree-specfile."); + + /* walk the tree */ + TIMER_START(start); + root = walk_dir(argv[1], ".", NULL, NULL, fsoptions.replace); + TIMER_RESULTS(start, "walk_dir"); + + /* append extra directory */ + for (i = 2; i < argc; i++) { + struct stat sb; + if (stat(argv[i], &sb) == -1) + err(1, "Can't stat `%s'", argv[i]); + if (!S_ISDIR(sb.st_mode)) + errx(1, "%s: not a directory", argv[i]); + TIMER_START(start); + root = walk_dir(argv[i], ".", NULL, root, fsoptions.replace); + TIMER_RESULTS(start, "walk_dir2"); + } + + if (specfile) { /* apply a specfile */ + TIMER_START(start); + apply_specfile(specfile, argv[1], root, fsoptions.onlyspec); + TIMER_RESULTS(start, "apply_specfile"); + } + + if (debug & DEBUG_DUMP_FSNODES) { + printf("\nparent: %s\n", argv[1]); + dump_fsnodes(root); + putchar('\n'); + } + + /* build the file system */ + TIMER_START(start); + fstype->make_fs(argv[0], argv[1], root, &fsoptions); + TIMER_RESULTS(start, "make_fs"); + + free_fsnodes(root); + + exit(0); + /* NOTREACHED */ +} + +int +set_option(const option_t *options, const char *option, char *buf, size_t len) +{ + char *var, *val; + int retval; + + assert(option != NULL); + + var = estrdup(option); + for (val = var; *val; val++) + if (*val == '=') { + *val++ = '\0'; + break; + } + retval = set_option_var(options, var, val, buf, len); + free(var); + return retval; +} + +int +set_option_var(const option_t *options, const char *var, const char *val, + char *buf, size_t len) +{ + char *s; + size_t i; + +#define NUM(type) \ + if (!*val) { \ + *(type *)options[i].value = 1; \ + break; \ + } \ + *(type *)options[i].value = (type)strsuftoll(options[i].desc, val, \ + options[i].minimum, options[i].maximum); break + + for (i = 0; options[i].name != NULL; i++) { + if (var[1] == '\0') { + if (options[i].letter != var[0]) + continue; + } else if (strcmp(options[i].name, var) != 0) + continue; + switch (options[i].type) { + case OPT_BOOL: + *(bool *)options[i].value = 1; + break; + case OPT_STRARRAY: + strlcpy((void *)options[i].value, val, (size_t) + options[i].maximum); + break; + case OPT_STRPTR: + s = estrdup(val); + *(char **)options[i].value = s; + break; + case OPT_STRBUF: + if (buf == NULL) + abort(); + strlcpy(buf, val, len); + break; + case OPT_INT64: + NUM(uint64_t); + case OPT_INT32: + NUM(uint32_t); + case OPT_INT16: + NUM(uint16_t); + case OPT_INT8: + NUM(uint8_t); + default: + warnx("Unknown type %d in option %s", options[i].type, + val); + return 0; + } + return i; + } + warnx("Unknown option `%s'", var); + return -1; +} + + +static fstype_t * +get_fstype(const char *type) +{ + int i; + + for (i = 0; fstypes[i].type != NULL; i++) + if (strcmp(fstypes[i].type, type) == 0) + return (&fstypes[i]); + return (NULL); +} + +option_t * +copy_opts(const option_t *o) +{ + size_t i; + for (i = 0; o[i].name; i++) + continue; + i++; + return memcpy(ecalloc(i, sizeof(*o)), o, i * sizeof(*o)); +} + +static void +usage(fstype_t *fstype, fsinfo_t *fsoptions) +{ + const char *prog; + + prog = getprogname(); + fprintf(stderr, +"Usage: %s [-rxZ] [-B endian] [-b free-blocks] [-d debug-mask]\n" +"\t[-F mtree-specfile] [-f free-files] [-M minimum-size] [-m maximum-size]\n" +"\t[-N userdb-dir] [-O offset] [-o fs-options] [-S sector-size]\n" +"\t[-s image-size] [-t fs-type] image-file directory [extra-directory ...]\n", + prog); + + if (fstype) { + size_t i; + option_t *o = fsoptions->fs_options; + + fprintf(stderr, "\n%s specific options:\n", fstype->type); + for (i = 0; o[i].name != NULL; i++) + fprintf(stderr, "\t%c%c%20.20s\t%s\n", + o[i].letter ? o[i].letter : ' ', + o[i].letter ? ',' : ' ', + o[i].name, o[i].desc); + } + exit(1); +} diff --git a/usr.sbin/makefs/makefs.h b/usr.sbin/makefs/makefs.h new file mode 100644 index 000000000..b856d5232 --- /dev/null +++ b/usr.sbin/makefs/makefs.h @@ -0,0 +1,267 @@ +/* $NetBSD: makefs.h,v 1.35 2013/08/05 14:41:57 reinoud Exp $ */ + +/* + * Copyright (c) 2001 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Luke Mewburn for Wasabi Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _MAKEFS_H +#define _MAKEFS_H + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#else +#define HAVE_STRUCT_STAT_ST_FLAGS 1 +#define HAVE_STRUCT_STAT_ST_GEN 1 +#define HAVE_STRUCT_STAT_ST_MTIMENSEC 1 +#define HAVE_STRUCT_STATVFS_F_IOSIZE 1 +#define HAVE_STRUCT_STAT_BIRTHTIME 1 +#define HAVE_FSTATVFS 1 +#endif + +#include +#include + +/* + * fsnode - + * a component of the tree; contains a filename, a pointer to + * fsinode, optional symlink name, and tree pointers + * + * fsinode - + * equivalent to an inode, containing target file system inode number, + * refcount (nlink), and stat buffer + * + * A tree of fsnodes looks like this: + * + * name "." "bin" "netbsd" + * type S_IFDIR S_IFDIR S_IFREG + * next > > NULL + * parent NULL NULL NULL + * child NULL v + * + * name "." "ls" + * type S_IFDIR S_IFREG + * next > NULL + * parent ^ ^ (to "bin") + * child NULL NULL + * + * Notes: + * - first always points to first entry, at current level, which + * must be "." when the tree has been built; during build it may + * not be if "." hasn't yet been found by readdir(2). + */ + +enum fi_flags { + FI_SIZED = 1<<0, /* inode sized */ + FI_ALLOCATED = 1<<1, /* fsinode->ino allocated */ + FI_WRITTEN = 1<<2, /* inode written */ +}; + +typedef struct { + uint32_t ino; /* inode number used on target fs */ + uint32_t nlink; /* number of links to this entry */ + enum fi_flags flags; /* flags used by fs specific code */ + struct stat st; /* stat entry */ + void *fsuse; /* for storing FS dependent info */ +} fsinode; + +typedef struct _fsnode { + struct _fsnode *parent; /* parent (NULL if root) */ + struct _fsnode *child; /* child (if type == S_IFDIR) */ + struct _fsnode *next; /* next */ + struct _fsnode *first; /* first node of current level (".") */ + uint32_t type; /* type of entry */ + fsinode *inode; /* actual inode data */ + char *symlink; /* symlink target */ + const char *root; /* root path */ + char *path; /* directory name */ + char *name; /* file name */ + int flags; /* misc flags */ +} fsnode; + +#define FSNODE_F_HASSPEC 0x01 /* fsnode has a spec entry */ + +/* + * option_t - contains option name, description, pointer to location to store + * result, and range checks for the result. Used to simplify fs specific + * option setting + */ +typedef enum { + OPT_STRARRAY, + OPT_STRPTR, + OPT_STRBUF, + OPT_BOOL, + OPT_INT8, + OPT_INT16, + OPT_INT32, + OPT_INT64 +} opttype_t; + +typedef struct { + char letter; /* option letter NUL for none */ + const char *name; /* option name */ + void *value; /* where to stuff the value */ + opttype_t type; /* type of entry */ + long long minimum; /* minimum for value */ + long long maximum; /* maximum for value */ + const char *desc; /* option description */ +} option_t; + +/* + * fsinfo_t - contains various settings and parameters pertaining to + * the image, including current settings, global options, and fs + * specific options + */ +typedef struct makefs_fsinfo { + /* current settings */ + off_t size; /* total size */ + off_t inodes; /* number of inodes */ + uint32_t curinode; /* current inode */ + + /* image settings */ + int fd; /* file descriptor of image */ + void *superblock; /* superblock */ + int onlyspec; /* only add entries in specfile */ + + + /* global options */ + off_t minsize; /* minimum size image should be */ + off_t maxsize; /* maximum size image can be */ + off_t freefiles; /* free file entries to leave */ + off_t freeblocks; /* free blocks to leave */ + off_t offset; /* offset from start of file */ + int freefilepc; /* free file % */ + int freeblockpc; /* free block % */ + int needswap; /* non-zero if byte swapping needed */ + int sectorsize; /* sector size */ + int sparse; /* sparse image, don't fill it with zeros */ + int replace; /* replace files when merging */ + + void *fs_specific; /* File system specific additions. */ + option_t *fs_options; /* File system specific options */ +} fsinfo_t; + + + + +void apply_specfile(const char *, const char *, fsnode *, int); +void dump_fsnodes(fsnode *); +const char * inode_type(mode_t); +int set_option(const option_t *, const char *, char *, size_t); +int set_option_var(const option_t *, const char *, const char *, + char *, size_t); +fsnode * walk_dir(const char *, const char *, fsnode *, fsnode *, int); +void free_fsnodes(fsnode *); +option_t * copy_opts(const option_t *); + +#define DECLARE_FUN(fs) \ +void fs ## _prep_opts(fsinfo_t *); \ +int fs ## _parse_opts(const char *, fsinfo_t *); \ +void fs ## _cleanup_opts(fsinfo_t *); \ +void fs ## _makefs(const char *, const char *, fsnode *, fsinfo_t *) + +DECLARE_FUN(ffs); +DECLARE_FUN(cd9660); +DECLARE_FUN(chfs); +DECLARE_FUN(v7fs); +DECLARE_FUN(msdos); +DECLARE_FUN(udf); + +extern u_int debug; +extern struct timespec start_time; + +/* + * If -x is specified, we want to exclude nodes which do not appear + * in the spec file. + */ +#define FSNODE_EXCLUDE_P(opts, fsnode) \ + ((opts)->onlyspec != 0 && ((fsnode)->flags & FSNODE_F_HASSPEC) == 0) + +#define DEBUG_TIME 0x00000001 + /* debug bits 1..3 unused at this time */ +#define DEBUG_WALK_DIR 0x00000010 +#define DEBUG_WALK_DIR_NODE 0x00000020 +#define DEBUG_WALK_DIR_LINKCHECK 0x00000040 +#define DEBUG_DUMP_FSNODES 0x00000080 +#define DEBUG_DUMP_FSNODES_VERBOSE 0x00000100 +#define DEBUG_FS_PARSE_OPTS 0x00000200 +#define DEBUG_FS_MAKEFS 0x00000400 +#define DEBUG_FS_VALIDATE 0x00000800 +#define DEBUG_FS_CREATE_IMAGE 0x00001000 +#define DEBUG_FS_SIZE_DIR 0x00002000 +#define DEBUG_FS_SIZE_DIR_NODE 0x00004000 +#define DEBUG_FS_SIZE_DIR_ADD_DIRENT 0x00008000 +#define DEBUG_FS_POPULATE 0x00010000 +#define DEBUG_FS_POPULATE_DIRBUF 0x00020000 +#define DEBUG_FS_POPULATE_NODE 0x00040000 +#define DEBUG_FS_WRITE_FILE 0x00080000 +#define DEBUG_FS_WRITE_FILE_BLOCK 0x00100000 +#define DEBUG_FS_MAKE_DIRBUF 0x00200000 +#define DEBUG_FS_WRITE_INODE 0x00400000 +#define DEBUG_BUF_BREAD 0x00800000 +#define DEBUG_BUF_BWRITE 0x01000000 +#define DEBUG_BUF_GETBLK 0x02000000 +#define DEBUG_APPLY_SPECFILE 0x04000000 +#define DEBUG_APPLY_SPECENTRY 0x08000000 +#define DEBUG_APPLY_SPECONLY 0x10000000 + + +#define TIMER_START(x) \ + if (debug & DEBUG_TIME) \ + gettimeofday(&(x), NULL) + +#define TIMER_RESULTS(x,d) \ + if (debug & DEBUG_TIME) { \ + struct timeval end, td; \ + gettimeofday(&end, NULL); \ + timersub(&end, &(x), &td); \ + printf("%s took %lld.%06ld seconds\n", \ + (d), (long long)td.tv_sec, \ + (long)td.tv_usec); \ + } + + +#ifndef DEFAULT_FSTYPE +#define DEFAULT_FSTYPE "ffs" +#endif + + +/* + * ffs specific settings + * --------------------- + */ + +#define FFS_EI /* for opposite endian support in ffs headers */ + + +#endif /* _MAKEFS_H */ diff --git a/usr.sbin/makefs/msdos.c b/usr.sbin/makefs/msdos.c new file mode 100644 index 000000000..5a7b10310 --- /dev/null +++ b/usr.sbin/makefs/msdos.c @@ -0,0 +1,257 @@ +/* $NetBSD: msdos.c,v 1.14 2013/02/03 03:21:21 christos Exp $ */ + +/*- + * Copyright (c) 2013 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: msdos.c,v 1.14 2013/02/03 03:21:21 christos Exp $"); +#endif /* !__lint */ + +#include + +#if !HAVE_NBTOOL_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "makefs.h" +#include "msdos.h" +#include "mkfs_msdos.h" + +static int msdos_populate_dir(const char *, struct denode *, fsnode *, + fsnode *, fsinfo_t *); + +void +msdos_prep_opts(fsinfo_t *fsopts) +{ + struct msdos_options *msdos_opt = ecalloc(1, sizeof(*msdos_opt)); + const option_t msdos_options[] = { +#define AOPT(_opt, _type, _name, _min, _desc) { \ + .letter = _opt, \ + .name = # _name, \ + .type = _min == -1 ? OPT_STRPTR : \ + (_min == -2 ? OPT_BOOL : \ + (sizeof(_type) == 1 ? OPT_INT8 : \ + (sizeof(_type) == 2 ? OPT_INT16 : \ + (sizeof(_type) == 4 ? OPT_INT32 : OPT_INT64)))), \ + .value = &msdos_opt->_name, \ + .minimum = _min, \ + .maximum = sizeof(_type) == 1 ? 0xff : \ + (sizeof(_type) == 2 ? 0xffff : \ + (sizeof(_type) == 4 ? 0xffffffff : 0xffffffffffffffffLL)), \ + .desc = _desc, \ +}, +ALLOPTS +#undef AOPT + { .name = NULL } + }; + + fsopts->fs_specific = msdos_opt; + fsopts->fs_options = copy_opts(msdos_options); +} + +void +msdos_cleanup_opts(fsinfo_t *fsopts) +{ + free(fsopts->fs_specific); + free(fsopts->fs_options); +} + +int +msdos_parse_opts(const char *option, fsinfo_t *fsopts) +{ + struct msdos_options *msdos_opt = fsopts->fs_specific; + option_t *msdos_options = fsopts->fs_options; + + int rv; + + assert(option != NULL); + assert(fsopts != NULL); + assert(msdos_opt != NULL); + + if (debug & DEBUG_FS_PARSE_OPTS) + printf("msdos_parse_opts: got `%s'\n", option); + + rv = set_option(msdos_options, option, NULL, 0); + if (rv == -1) + return rv; + + if (strcmp(msdos_options[rv].name, "volume_id") == 0) + msdos_opt->volume_id_set = 1; + else if (strcmp(msdos_options[rv].name, "media_descriptor") == 0) + msdos_opt->media_descriptor_set = 1; + else if (strcmp(msdos_options[rv].name, "hidden_sectors") == 0) + msdos_opt->hidden_sectors_set = 1; + return 1; +} + + +void +msdos_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts) +{ + struct msdos_options *msdos_opt = fsopts->fs_specific; + struct vnode vp, rootvp; + struct timeval start; + struct msdosfsmount *pmp; + + assert(image != NULL); + assert(dir != NULL); + assert(root != NULL); + assert(fsopts != NULL); + + /* + * XXX: pick up other options from the msdos specific ones? + * Is minsize right here? + */ + msdos_opt->create_size = MAX(msdos_opt->create_size, fsopts->minsize); + msdos_opt->offset = fsopts->offset; + if (msdos_opt->bytes_per_sector == 0) { + if (fsopts->sectorsize == -1) + fsopts->sectorsize = 512; + msdos_opt->bytes_per_sector = fsopts->sectorsize; + } else if (fsopts->sectorsize == -1) { + fsopts->sectorsize = msdos_opt->bytes_per_sector; + } else if (fsopts->sectorsize != msdos_opt->bytes_per_sector) { + err(1, "inconsistent sectorsize -S %u" + "!= -o bytes_per_sector %u", + fsopts->sectorsize, msdos_opt->bytes_per_sector); + } + + /* create image */ + printf("Creating `%s'\n", image); + TIMER_START(start); + if (mkfs_msdos(image, NULL, msdos_opt) == -1) + return; + TIMER_RESULTS(start, "mkfs_msdos"); + + fsopts->fd = open(image, O_RDWR); + vp.fs = fsopts; + + if ((pmp = msdosfs_mount(&vp, 0)) == NULL) + err(1, "msdosfs_mount"); + + if (msdosfs_root(pmp, &rootvp) != 0) + err(1, "msdosfs_root"); + + if (debug & DEBUG_FS_MAKEFS) + printf("msdos_makefs: image %s directory %s root %p\n", + image, dir, root); + + /* populate image */ + printf("Populating `%s'\n", image); + TIMER_START(start); + if (msdos_populate_dir(dir, VTODE(&rootvp), root, root, fsopts) == -1) + errx(1, "Image file `%s' not created.", image); + TIMER_RESULTS(start, "msdos_populate_dir"); + + if (debug & DEBUG_FS_MAKEFS) + putchar('\n'); + + /* ensure no outstanding buffers remain */ + if (debug & DEBUG_FS_MAKEFS) + bcleanup(); + + printf("Image `%s' complete\n", image); +} + +static int +msdos_populate_dir(const char *path, struct denode *dir, fsnode *root, + fsnode *parent, fsinfo_t *fsopts) +{ + fsnode *cur; + char pbuf[MAXPATHLEN]; + + assert(dir != NULL); + assert(root != NULL); + assert(fsopts != NULL); + + for (cur = root->next; cur != NULL; cur = cur->next) { + if ((size_t)snprintf(pbuf, sizeof(pbuf), "%s/%s", path, + cur->name) >= sizeof(pbuf)) { + warnx("path %s too long", pbuf); + return -1; + } + + if ((cur->inode->flags & FI_ALLOCATED) == 0) { + cur->inode->flags |= FI_ALLOCATED; + if (cur != root) { + fsopts->curinode++; + cur->inode->ino = fsopts->curinode; + cur->parent = parent; + } + } + + if (cur->inode->flags & FI_WRITTEN) { + continue; // hard link + } + cur->inode->flags |= FI_WRITTEN; + + if (cur->child) { + struct denode *de; + if ((de = msdosfs_mkdire(pbuf, dir, cur)) == NULL) { + warn("msdosfs_mkdire %s", pbuf); + return -1; + } + if (msdos_populate_dir(pbuf, de, cur->child, cur, + fsopts) == -1) { + warn("msdos_populate_dir %s", pbuf); + return -1; + } + continue; + } else if (!S_ISREG(cur->type)) { + warnx("skipping non-regular file %s/%s", cur->path, + cur->name); + continue; + } + if (msdosfs_mkfile(pbuf, dir, cur) == NULL) { + warn("msdosfs_mkfile %s", pbuf); + return -1; + } + } + return 0; +} diff --git a/usr.sbin/makefs/msdos.h b/usr.sbin/makefs/msdos.h new file mode 100644 index 000000000..3571bef38 --- /dev/null +++ b/usr.sbin/makefs/msdos.h @@ -0,0 +1,42 @@ +/* $NetBSD: msdos.h,v 1.2 2013/01/26 00:31:49 christos Exp $ */ + +/*- + * Copyright (c) 2013 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +struct vnode; +struct denode; + +struct msdosfsmount *msdosfs_mount(struct vnode *, int); +int msdosfs_root(struct msdosfsmount *, struct vnode *); + +struct denode *msdosfs_mkfile(const char *, struct denode *, fsnode *); +struct denode *msdosfs_mkdire(const char *, struct denode *, fsnode *); diff --git a/usr.sbin/makefs/msdos/Makefile.inc b/usr.sbin/makefs/msdos/Makefile.inc new file mode 100644 index 000000000..fbcce2add --- /dev/null +++ b/usr.sbin/makefs/msdos/Makefile.inc @@ -0,0 +1,15 @@ +# $NetBSD: Makefile.inc,v 1.5 2013/01/26 16:50:46 christos Exp $ +# + +MSDOS= ${NETBSDSRCDIR}/sys/fs/msdosfs +MSDOS_NEWFS= ${NETBSDSRCDIR}/sbin/newfs_msdos + +.PATH: ${.CURDIR}/msdos ${MSDOS} ${MSDOS_NEWFS} + +CPPFLAGS+= -DMSDOS_EI -I${MSDOS} -I${MSDOS_NEWFS} +.if !defined(HOSTPROGNAME) +CPPFLAGS+= -I${NETBSDSRCDIR}/sys +.endif + +SRCS+= mkfs_msdos.c msdosfs_fat.c msdosfs_conv.c msdosfs_vfsops.c +SRCS+= msdosfs_lookup.c msdosfs_denode.c msdosfs_vnops.c diff --git a/usr.sbin/makefs/msdos/msdosfs_denode.c b/usr.sbin/makefs/msdos/msdosfs_denode.c new file mode 100644 index 000000000..47ebee012 --- /dev/null +++ b/usr.sbin/makefs/msdos/msdosfs_denode.c @@ -0,0 +1,363 @@ +/* $NetBSD: msdosfs_denode.c,v 1.6 2013/10/19 17:16:37 christos Exp $ */ + +/*- + * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. + * Copyright (C) 1994, 1995, 1997 TooLs GmbH. + * All rights reserved. + * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by TooLs GmbH. + * 4. The name of TooLs GmbH may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Written by Paul Popelka (paulp@uts.amdahl.com) + * + * You can do anything you want with this software, just don't say you wrote + * it, and don't remove this notice. + * + * This software is provided "as is". + * + * The author supplies this software to be publicly redistributed on the + * understanding that the author is not responsible for the correct + * functioning of this software in any circumstances and is not liable for + * any damages caused by this software. + * + * October 1992 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: msdosfs_denode.c,v 1.6 2013/10/19 17:16:37 christos Exp $"); + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +/* + * If deget() succeeds it returns with the gotten denode locked(). + * + * pmp - address of msdosfsmount structure of the filesystem containing + * the denode of interest. The pm_dev field and the address of + * the msdosfsmount structure are used. + * dirclust - which cluster bp contains, if dirclust is 0 (root directory) + * diroffset is relative to the beginning of the root directory, + * otherwise it is cluster relative. + * diroffset - offset past begin of cluster of denode we want + * depp - returns the address of the gotten denode. + */ +int +deget(struct msdosfsmount *pmp, u_long dirclust, u_long diroffset, + struct denode **depp) + /* pmp: so we know the maj/min number */ + /* dirclust: cluster this dir entry came from */ + /* diroffset: index of entry within the cluster */ + /* depp: returns the addr of the gotten denode */ +{ + int error; + struct direntry *direntptr; + struct denode *ldep; + struct buf *bp; + +#ifdef MSDOSFS_DEBUG + printf("deget(pmp %p, dirclust %lu, diroffset %lx, depp %p)\n", + pmp, dirclust, diroffset, depp); +#endif + + /* + * On FAT32 filesystems, root is a (more or less) normal + * directory + */ + if (FAT32(pmp) && dirclust == MSDOSFSROOT) + dirclust = pmp->pm_rootdirblk; + + ldep = ecalloc(1, sizeof(*ldep)); + ldep->de_vnode = NULL; + ldep->de_flag = 0; + ldep->de_devvp = 0; + ldep->de_lockf = 0; + ldep->de_dev = pmp->pm_dev; + ldep->de_dirclust = dirclust; + ldep->de_diroffset = diroffset; + ldep->de_pmp = pmp; + ldep->de_devvp = pmp->pm_devvp; + ldep->de_refcnt = 1; + fc_purge(ldep, 0); + /* + * Copy the directory entry into the denode area of the vnode. + */ + if ((dirclust == MSDOSFSROOT + || (FAT32(pmp) && dirclust == pmp->pm_rootdirblk)) + && diroffset == MSDOSFSROOT_OFS) { + /* + * Directory entry for the root directory. There isn't one, + * so we manufacture one. We should probably rummage + * through the root directory and find a label entry (if it + * exists), and then use the time and date from that entry + * as the time and date for the root denode. + */ + ldep->de_vnode = (struct vnode *)-1; + + ldep->de_Attributes = ATTR_DIRECTORY; + if (FAT32(pmp)) + ldep->de_StartCluster = pmp->pm_rootdirblk; + /* de_FileSize will be filled in further down */ + else { + ldep->de_StartCluster = MSDOSFSROOT; + ldep->de_FileSize = pmp->pm_rootdirsize * pmp->pm_BytesPerSec; + } + /* + * fill in time and date so that dos2unixtime() doesn't + * spit up when called from msdosfs_getattr() with root + * denode + */ + ldep->de_CHun = 0; + ldep->de_CTime = 0x0000; /* 00:00:00 */ + ldep->de_CDate = (0 << DD_YEAR_SHIFT) | (1 << DD_MONTH_SHIFT) + | (1 << DD_DAY_SHIFT); + /* Jan 1, 1980 */ + ldep->de_ADate = ldep->de_CDate; + ldep->de_MTime = ldep->de_CTime; + ldep->de_MDate = ldep->de_CDate; + /* leave the other fields as garbage */ + } else { + error = readep(pmp, dirclust, diroffset, &bp, &direntptr); + if (error) { + ldep->de_devvp = NULL; + ldep->de_Name[0] = SLOT_DELETED; + return (error); + } + DE_INTERNALIZE(ldep, direntptr); + brelse(bp, 0); + } + + /* + * Fill in a few fields of the vnode and finish filling in the + * denode. Then return the address of the found denode. + */ + if (ldep->de_Attributes & ATTR_DIRECTORY) { + /* + * Since DOS directory entries that describe directories + * have 0 in the filesize field, we take this opportunity + * to find out the length of the directory and plug it into + * the denode structure. + */ + u_long size; + + if (ldep->de_StartCluster != MSDOSFSROOT) { + error = pcbmap(ldep, CLUST_END, 0, &size, 0); + if (error == E2BIG) { + ldep->de_FileSize = de_cn2off(pmp, size); + error = 0; + } else + printf("deget(): pcbmap returned %d\n", error); + } + } + *depp = ldep; + return (0); +} + +/* + * Truncate the file described by dep to the length specified by length. + */ +int +detrunc(struct denode *dep, u_long length, int flags, struct kauth_cred *cred) +{ + int error; + int allerror = 0; + u_long eofentry; + u_long chaintofree = 0; + daddr_t bn, lastblock; + int boff; + int isadir = dep->de_Attributes & ATTR_DIRECTORY; + struct buf *bp; + struct msdosfsmount *pmp = dep->de_pmp; + +#ifdef MSDOSFS_DEBUG + printf("detrunc(): file %s, length %lu, flags %x\n", dep->de_Name, length, flags); +#endif + + /* + * Disallow attempts to truncate the root directory since it is of + * fixed size. That's just the way dos filesystems are. We use + * the VROOT bit in the vnode because checking for the directory + * bit and a startcluster of 0 in the denode is not adequate to + * recognize the root directory at this point in a file or + * directory's life. + */ + if (dep->de_vnode != NULL && !FAT32(pmp)) { + printf("detrunc(): can't truncate root directory, clust %ld, offset %ld\n", + dep->de_dirclust, dep->de_diroffset); + return (EINVAL); + } + + if (dep->de_FileSize < length) + return (deextend(dep, length, cred)); + lastblock = de_clcount(pmp, length) - 1; + + /* + * If the desired length is 0 then remember the starting cluster of + * the file and set the StartCluster field in the directory entry + * to 0. If the desired length is not zero, then get the number of + * the last cluster in the shortened file. Then get the number of + * the first cluster in the part of the file that is to be freed. + * Then set the next cluster pointer in the last cluster of the + * file to CLUST_EOFE. + */ + if (length == 0) { + chaintofree = dep->de_StartCluster; + dep->de_StartCluster = 0; + eofentry = ~0; + } else { + error = pcbmap(dep, lastblock, 0, &eofentry, 0); + if (error) { +#ifdef MSDOSFS_DEBUG + printf("detrunc(): pcbmap fails %d\n", error); +#endif + return (error); + } + } + + /* + * If the new length is not a multiple of the cluster size then we + * must zero the tail end of the new last cluster in case it + * becomes part of the file again because of a seek. + */ + if ((boff = length & pmp->pm_crbomask) != 0) { + if (isadir) { + bn = cntobn(pmp, eofentry); + error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), + pmp->pm_bpcluster, NOCRED, B_MODIFY, &bp); + if (error) { +#ifdef MSDOSFS_DEBUG + printf("detrunc(): bread fails %d\n", error); +#endif + return (error); + } + memset((char *)bp->b_data + boff, 0, + pmp->pm_bpcluster - boff); + if (flags & IO_SYNC) + bwrite(bp); + else + bdwrite(bp); + } + } + + /* + * Write out the updated directory entry. Even if the update fails + * we free the trailing clusters. + */ + dep->de_FileSize = length; + if (!isadir) + dep->de_flag |= DE_UPDATE|DE_MODIFIED; +#ifdef MSDOSFS_DEBUG + printf("detrunc(): allerror %d, eofentry %lu\n", + allerror, eofentry); +#endif + + /* + * If we need to break the cluster chain for the file then do it + * now. + */ + if (eofentry != (u_long)~0) { + error = fatentry(FAT_GET_AND_SET, pmp, eofentry, + &chaintofree, CLUST_EOFE); + if (error) { +#ifdef MSDOSFS_DEBUG + printf("detrunc(): fatentry errors %d\n", error); +#endif + return (error); + } + } + + /* + * Now free the clusters removed from the file because of the + * truncation. + */ + if (chaintofree != 0 && !MSDOSFSEOF(chaintofree, pmp->pm_fatmask)) + freeclusterchain(pmp, chaintofree); + + return (allerror); +} + +/* + * Extend the file described by dep to length specified by length. + */ +int +deextend(struct denode *dep, u_long length, struct kauth_cred *cred) +{ + struct msdosfsmount *pmp = dep->de_pmp; + u_long count; + int error; + + /* + * The root of a DOS filesystem cannot be extended. + */ + if (dep->de_vnode != NULL && !FAT32(pmp)) + return EINVAL; + + /* + * Directories cannot be extended. + */ + if (dep->de_Attributes & ATTR_DIRECTORY) + return EISDIR; + + if (length <= dep->de_FileSize) + return E2BIG; + + /* + * Compute the number of clusters to allocate. + */ + count = de_clcount(pmp, length) - de_clcount(pmp, dep->de_FileSize); + if (count > 0) { + if (count > pmp->pm_freeclustercount) + return (ENOSPC); + error = extendfile(dep, count, NULL, NULL, DE_CLEAR); + if (error) { + /* truncate the added clusters away again */ + (void) detrunc(dep, dep->de_FileSize, 0, cred); + return (error); + } + } + + /* + * Zero extend file range; ubc_zerorange() uses ubc_alloc() and a + * memset(); we set the write size so ubc won't read in file data that + * is zero'd later. + */ + dep->de_FileSize = length; + dep->de_flag |= DE_UPDATE|DE_MODIFIED; + return 0; +} diff --git a/usr.sbin/makefs/msdos/msdosfs_vfsops.c b/usr.sbin/makefs/msdos/msdosfs_vfsops.c new file mode 100644 index 000000000..29461c760 --- /dev/null +++ b/usr.sbin/makefs/msdos/msdosfs_vfsops.c @@ -0,0 +1,425 @@ +/*- + * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. + * Copyright (C) 1994, 1995, 1997 TooLs GmbH. + * All rights reserved. + * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by TooLs GmbH. + * 4. The name of TooLs GmbH may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Written by Paul Popelka (paulp@uts.amdahl.com) + * + * You can do anything you want with this software, just don't say you wrote + * it, and don't remove this notice. + * + * This software is provided "as is". + * + * The author supplies this software to be publicly redistributed on the + * understanding that the author is not responsible for the correct + * functioning of this software in any circumstances and is not liable for + * any damages caused by this software. + * + * October 1992 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: msdosfs_vfsops.c,v 1.7 2013/01/30 19:19:19 christos Exp $"); + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "makefs.h" +#include "msdos.h" +#include "mkfs_msdos.h" + +#ifdef MSDOSFS_DEBUG +#define DPRINTF(a) printf a +#else +#define DPRINTF(a) +#endif + +struct msdosfsmount * +msdosfs_mount(struct vnode *devvp, int flags) +{ + struct msdosfsmount *pmp = NULL; + struct buf *bp; + union bootsector *bsp; + struct byte_bpb33 *b33; + struct byte_bpb50 *b50; + struct byte_bpb710 *b710; + uint8_t SecPerClust; + int ronly = 0, error, tmp; + int bsize; + struct msdos_options *m = devvp->fs->fs_specific; + uint64_t psize = m->create_size; + unsigned secsize = 512; + + DPRINTF(("%s(bread 0)\n", __func__)); + if ((error = bread(devvp, 0, secsize, NULL, 0, &bp)) != 0) + goto error_exit; + + bsp = (union bootsector *)bp->b_data; + b33 = (struct byte_bpb33 *)bsp->bs33.bsBPB; + b50 = (struct byte_bpb50 *)bsp->bs50.bsBPB; + b710 = (struct byte_bpb710 *)bsp->bs710.bsBPB; + + if (!(flags & MSDOSFSMNT_GEMDOSFS)) { + if (bsp->bs50.bsBootSectSig0 != BOOTSIG0 + || bsp->bs50.bsBootSectSig1 != BOOTSIG1) { + DPRINTF(("bootsig0 %d bootsig1 %d\n", + bsp->bs50.bsBootSectSig0, + bsp->bs50.bsBootSectSig1)); + error = EINVAL; + goto error_exit; + } + bsize = 0; + } else + bsize = 512; + + pmp = ecalloc(1, sizeof *pmp); + /* + * Compute several useful quantities from the bpb in the + * bootsector. Copy in the dos 5 variant of the bpb then fix up + * the fields that are different between dos 5 and dos 3.3. + */ + SecPerClust = b50->bpbSecPerClust; + pmp->pm_BytesPerSec = getushort(b50->bpbBytesPerSec); + pmp->pm_ResSectors = getushort(b50->bpbResSectors); + pmp->pm_FATs = b50->bpbFATs; + pmp->pm_RootDirEnts = getushort(b50->bpbRootDirEnts); + pmp->pm_Sectors = getushort(b50->bpbSectors); + pmp->pm_FATsecs = getushort(b50->bpbFATsecs); + pmp->pm_SecPerTrack = getushort(b50->bpbSecPerTrack); + pmp->pm_Heads = getushort(b50->bpbHeads); + pmp->pm_Media = b50->bpbMedia; + + DPRINTF(("%s(BytesPerSec=%u, ResSectors=%u, FATs=%d, RootDirEnts=%u, " + "Sectors=%u, FATsecs=%lu, SecPerTrack=%u, Heads=%u, Media=%u)\n", + __func__, pmp->pm_BytesPerSec, pmp->pm_ResSectors, pmp->pm_FATs, + pmp->pm_RootDirEnts, pmp->pm_Sectors, pmp->pm_FATsecs, + pmp->pm_SecPerTrack, pmp->pm_Heads, pmp->pm_Media)); + if (!(flags & MSDOSFSMNT_GEMDOSFS)) { + /* XXX - We should probably check more values here */ + if (!pmp->pm_BytesPerSec || !SecPerClust + || pmp->pm_SecPerTrack > 63) { + DPRINTF(("bytespersec %d secperclust %d " + "secpertrack %d\n", + pmp->pm_BytesPerSec, SecPerClust, + pmp->pm_SecPerTrack)); + error = EINVAL; + goto error_exit; + } + } + + if (pmp->pm_Sectors == 0) { + pmp->pm_HiddenSects = getulong(b50->bpbHiddenSecs); + pmp->pm_HugeSectors = getulong(b50->bpbHugeSectors); + } else { + pmp->pm_HiddenSects = getushort(b33->bpbHiddenSecs); + pmp->pm_HugeSectors = pmp->pm_Sectors; + } + + if (pmp->pm_RootDirEnts == 0) { + unsigned short vers = getushort(b710->bpbFSVers); + /* + * Some say that bsBootSectSig[23] must be zero, but + * Windows does not require this and some digital cameras + * do not set these to zero. Therefore, do not insist. + */ + if (pmp->pm_Sectors || pmp->pm_FATsecs || vers) { + DPRINTF(("sectors %d fatsecs %lu vers %d\n", + pmp->pm_Sectors, pmp->pm_FATsecs, vers)); + error = EINVAL; + goto error_exit; + } + pmp->pm_fatmask = FAT32_MASK; + pmp->pm_fatmult = 4; + pmp->pm_fatdiv = 1; + pmp->pm_FATsecs = getulong(b710->bpbBigFATsecs); + + /* mirrorring is enabled if the FATMIRROR bit is not set */ + if ((getushort(b710->bpbExtFlags) & FATMIRROR) == 0) + pmp->pm_flags |= MSDOSFS_FATMIRROR; + else + pmp->pm_curfat = getushort(b710->bpbExtFlags) & FATNUM; + } else + pmp->pm_flags |= MSDOSFS_FATMIRROR; + + if (flags & MSDOSFSMNT_GEMDOSFS) { + if (FAT32(pmp)) { + DPRINTF(("FAT32 for GEMDOS\n")); + /* + * GEMDOS doesn't know FAT32. + */ + error = EINVAL; + goto error_exit; + } + + /* + * Check a few values (could do some more): + * - logical sector size: power of 2, >= block size + * - sectors per cluster: power of 2, >= 1 + * - number of sectors: >= 1, <= size of partition + */ + if ( (SecPerClust == 0) + || (SecPerClust & (SecPerClust - 1)) + || (pmp->pm_BytesPerSec < bsize) + || (pmp->pm_BytesPerSec & (pmp->pm_BytesPerSec - 1)) + || (pmp->pm_HugeSectors == 0) + || (pmp->pm_HugeSectors * (pmp->pm_BytesPerSec / bsize) + > psize)) { + DPRINTF(("consistency checks for GEMDOS\n")); + error = EINVAL; + goto error_exit; + } + /* + * XXX - Many parts of the msdosfs driver seem to assume that + * the number of bytes per logical sector (BytesPerSec) will + * always be the same as the number of bytes per disk block + * Let's pretend it is. + */ + tmp = pmp->pm_BytesPerSec / bsize; + pmp->pm_BytesPerSec = bsize; + pmp->pm_HugeSectors *= tmp; + pmp->pm_HiddenSects *= tmp; + pmp->pm_ResSectors *= tmp; + pmp->pm_Sectors *= tmp; + pmp->pm_FATsecs *= tmp; + SecPerClust *= tmp; + } + + /* Check that fs has nonzero FAT size */ + if (pmp->pm_FATsecs == 0) { + DPRINTF(("FATsecs is 0\n")); + error = EINVAL; + goto error_exit; + } + + pmp->pm_fatblk = pmp->pm_ResSectors; + if (FAT32(pmp)) { + pmp->pm_rootdirblk = getulong(b710->bpbRootClust); + pmp->pm_firstcluster = pmp->pm_fatblk + + (pmp->pm_FATs * pmp->pm_FATsecs); + pmp->pm_fsinfo = getushort(b710->bpbFSInfo); + } else { + pmp->pm_rootdirblk = pmp->pm_fatblk + + (pmp->pm_FATs * pmp->pm_FATsecs); + pmp->pm_rootdirsize = (pmp->pm_RootDirEnts * sizeof(struct direntry) + + pmp->pm_BytesPerSec - 1) + / pmp->pm_BytesPerSec;/* in sectors */ + pmp->pm_firstcluster = pmp->pm_rootdirblk + pmp->pm_rootdirsize; + } + + pmp->pm_nmbrofclusters = (pmp->pm_HugeSectors - pmp->pm_firstcluster) / + SecPerClust; + pmp->pm_maxcluster = pmp->pm_nmbrofclusters + 1; + pmp->pm_fatsize = pmp->pm_FATsecs * pmp->pm_BytesPerSec; + + if (flags & MSDOSFSMNT_GEMDOSFS) { + if (pmp->pm_nmbrofclusters <= (0xff0 - 2)) { + pmp->pm_fatmask = FAT12_MASK; + pmp->pm_fatmult = 3; + pmp->pm_fatdiv = 2; + } else { + pmp->pm_fatmask = FAT16_MASK; + pmp->pm_fatmult = 2; + pmp->pm_fatdiv = 1; + } + } else if (pmp->pm_fatmask == 0) { + if (pmp->pm_maxcluster + <= ((CLUST_RSRVD - CLUST_FIRST) & FAT12_MASK)) { + /* + * This will usually be a floppy disk. This size makes + * sure that one FAT entry will not be split across + * multiple blocks. + */ + pmp->pm_fatmask = FAT12_MASK; + pmp->pm_fatmult = 3; + pmp->pm_fatdiv = 2; + } else { + pmp->pm_fatmask = FAT16_MASK; + pmp->pm_fatmult = 2; + pmp->pm_fatdiv = 1; + } + } + if (FAT12(pmp)) + pmp->pm_fatblocksize = 3 * pmp->pm_BytesPerSec; + else + pmp->pm_fatblocksize = MAXBSIZE; + + pmp->pm_fatblocksec = pmp->pm_fatblocksize / pmp->pm_BytesPerSec; + pmp->pm_bnshift = ffs(pmp->pm_BytesPerSec) - 1; + + /* + * Compute mask and shift value for isolating cluster relative byte + * offsets and cluster numbers from a file offset. + */ + pmp->pm_bpcluster = SecPerClust * pmp->pm_BytesPerSec; + pmp->pm_crbomask = pmp->pm_bpcluster - 1; + pmp->pm_cnshift = ffs(pmp->pm_bpcluster) - 1; + + DPRINTF(("%s(fatmask=%lu, fatmult=%u, fatdiv=%u, fatblocksize=%lu, " + "fatblocksec=%lu, bnshift=%lu, pbcluster=%lu, crbomask=%lu, " + "cnshift=%lu)\n", + __func__, pmp->pm_fatmask, pmp->pm_fatmult, pmp->pm_fatdiv, + pmp->pm_fatblocksize, pmp->pm_fatblocksec, pmp->pm_bnshift, + pmp->pm_bpcluster, pmp->pm_crbomask, pmp->pm_cnshift)); + /* + * Check for valid cluster size + * must be a power of 2 + */ + if (pmp->pm_bpcluster ^ (1 << pmp->pm_cnshift)) { + DPRINTF(("bpcluster %lu cnshift %lu\n", + pmp->pm_bpcluster, pmp->pm_cnshift)); + error = EINVAL; + goto error_exit; + } + + /* + * Release the bootsector buffer. + */ + brelse(bp, BC_AGE); + bp = NULL; + + /* + * Check FSInfo. + */ + if (pmp->pm_fsinfo) { + struct fsinfo *fp; + + /* + * XXX If the fsinfo block is stored on media with + * 2KB or larger sectors, is the fsinfo structure + * padded at the end or in the middle? + */ + DPRINTF(("%s(bread %lu)\n", __func__, + (unsigned long)de_bn2kb(pmp, pmp->pm_fsinfo))); + if ((error = bread(devvp, de_bn2kb(pmp, pmp->pm_fsinfo), + pmp->pm_BytesPerSec, NULL, 0, &bp)) != 0) + goto error_exit; + fp = (struct fsinfo *)bp->b_data; + if (!memcmp(fp->fsisig1, "RRaA", 4) + && !memcmp(fp->fsisig2, "rrAa", 4) + && !memcmp(fp->fsisig3, "\0\0\125\252", 4) + && !memcmp(fp->fsisig4, "\0\0\125\252", 4)) + pmp->pm_nxtfree = getulong(fp->fsinxtfree); + else + pmp->pm_fsinfo = 0; + brelse(bp, 0); + bp = NULL; + } + + /* + * Check and validate (or perhaps invalidate?) the fsinfo structure? + * XXX + */ + if (pmp->pm_fsinfo) { + if ((pmp->pm_nxtfree == 0xffffffffUL) || + (pmp->pm_nxtfree > pmp->pm_maxcluster)) + pmp->pm_fsinfo = 0; + } + + /* + * Allocate memory for the bitmap of allocated clusters, and then + * fill it in. + */ + pmp->pm_inusemap = ecalloc(sizeof(*pmp->pm_inusemap), + ((pmp->pm_maxcluster + N_INUSEBITS) / N_INUSEBITS)); + /* + * fillinusemap() needs pm_devvp. + */ + pmp->pm_dev = 0; + pmp->pm_devvp = devvp; + + /* + * Have the inuse map filled in. + */ + if ((error = fillinusemap(pmp)) != 0) { + DPRINTF(("fillinusemap %d\n", error)); + goto error_exit; + } + + /* + * Finish up. + */ + if (ronly) + pmp->pm_flags |= MSDOSFSMNT_RONLY; + else + pmp->pm_fmod = 1; + + /* + * If we ever do quotas for DOS filesystems this would be a place + * to fill in the info in the msdosfsmount structure. You dolt, + * quotas on dos filesystems make no sense because files have no + * owners on dos filesystems. of course there is some empty space + * in the directory entry where we could put uid's and gid's. + */ + + return pmp; + +error_exit: + if (bp) + brelse(bp, BC_AGE); + if (pmp) { + if (pmp->pm_inusemap) + free(pmp->pm_inusemap); + free(pmp); + } + errno = error; + return pmp; +} + +int +msdosfs_root(struct msdosfsmount *pmp, struct vnode *vp) { + struct denode *ndep; + int error; + + *vp = *pmp->pm_devvp; + if ((error = deget(pmp, MSDOSFSROOT, MSDOSFSROOT_OFS, &ndep)) != 0) { + errno = error; + return -1; + } + vp->v_data = ndep; + return 0; +} diff --git a/usr.sbin/makefs/msdos/msdosfs_vnops.c b/usr.sbin/makefs/msdos/msdosfs_vnops.c new file mode 100644 index 000000000..53c438c24 --- /dev/null +++ b/usr.sbin/makefs/msdos/msdosfs_vnops.c @@ -0,0 +1,639 @@ +/* $NetBSD: msdosfs_vnops.c,v 1.15 2013/10/19 17:16:37 christos Exp $ */ + +/*- + * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. + * Copyright (C) 1994, 1995, 1997 TooLs GmbH. + * All rights reserved. + * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by TooLs GmbH. + * 4. The name of TooLs GmbH may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Written by Paul Popelka (paulp@uts.amdahl.com) + * + * You can do anything you want with this software, just don't say you wrote + * it, and don't remove this notice. + * + * This software is provided "as is". + * + * The author supplies this software to be publicly redistributed on the + * understanding that the author is not responsible for the correct + * functioning of this software in any circumstances and is not liable for + * any damages caused by this software. + * + * October 1992 + */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__KERNEL_RCSID(0, "$NetBSD: msdosfs_vnops.c,v 1.15 2013/10/19 17:16:37 christos Exp $"); + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "makefs.h" +#include "msdos.h" + +#ifdef MSDOSFS_DEBUG +#define DPRINTF(a) printf a +#else +#define DPRINTF(a) +#endif +/* + * Some general notes: + * + * In the ufs filesystem the inodes, superblocks, and indirect blocks are + * read/written using the vnode for the filesystem. Blocks that represent + * the contents of a file are read/written using the vnode for the file + * (including directories when they are read/written as files). This + * presents problems for the dos filesystem because data that should be in + * an inode (if dos had them) resides in the directory itself. Since we + * must update directory entries without the benefit of having the vnode + * for the directory we must use the vnode for the filesystem. This means + * that when a directory is actually read/written (via read, write, or + * readdir, or seek) we must use the vnode for the filesystem instead of + * the vnode for the directory as would happen in ufs. This is to insure we + * retrieve the correct block from the buffer cache since the hash value is + * based upon the vnode address and the desired block number. + */ + +static int msdosfs_wfile(const char *, struct denode *, fsnode *); + +static void +msdosfs_times(struct msdosfsmount *pmp, struct denode *dep, + const struct stat *st) +{ +#ifndef HAVE_NBTOOL_CONFIG_H + struct timespec at = st->st_atimespec; + struct timespec mt = st->st_mtimespec; +#else + struct timespec at = { st->st_atime, 0 }; + struct timespec mt = { st->st_mtime, 0 }; +#endif + unix2dostime(&at, pmp->pm_gmtoff, &dep->de_ADate, NULL, NULL); + unix2dostime(&mt, pmp->pm_gmtoff, &dep->de_MDate, &dep->de_MTime, NULL); +} + +/* + * When we search a directory the blocks containing directory entries are + * read and examined. The directory entries contain information that would + * normally be in the inode of a unix filesystem. This means that some of + * a directory's contents may also be in memory resident denodes (sort of + * an inode). This can cause problems if we are searching while some other + * process is modifying a directory. To prevent one process from accessing + * incompletely modified directory information we depend upon being the + * sole owner of a directory block. bread/brelse provide this service. + * This being the case, when a process modifies a directory it must first + * acquire the disk block that contains the directory entry to be modified. + * Then update the disk block and the denode, and then write the disk block + * out to disk. This way disk blocks containing directory entries and in + * memory denode's will be in synch. + */ +static int +msdosfs_findslot(struct denode *dp, struct componentname *cnp) +{ + daddr_t bn; + int error; + int slotcount; + int slotoffset = 0; + int frcn; + u_long cluster; + int blkoff; + u_int diroff; + int blsize; + struct msdosfsmount *pmp; + struct buf *bp = 0; + struct direntry *dep; + u_char dosfilename[12]; + int wincnt = 1; + int chksum = -1, chksum_ok; + int olddos = 1; + + pmp = dp->de_pmp; + + switch (unix2dosfn((const u_char *)cnp->cn_nameptr, dosfilename, + cnp->cn_namelen, 0)) { + case 0: + return (EINVAL); + case 1: + break; + case 2: + wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr, + cnp->cn_namelen) + 1; + break; + case 3: + olddos = 0; + wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr, + cnp->cn_namelen) + 1; + break; + } + + if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) + wincnt = 1; + + /* + * Suppress search for slots unless creating + * file and at end of pathname, in which case + * we watch for a place to put the new file in + * case it doesn't already exist. + */ + slotcount = 0; + DPRINTF(("%s(): dos filename: %s\n", __func__, dosfilename)); + /* + * Search the directory pointed at by vdp for the name pointed at + * by cnp->cn_nameptr. + */ + /* + * The outer loop ranges over the clusters that make up the + * directory. Note that the root directory is different from all + * other directories. It has a fixed number of blocks that are not + * part of the pool of allocatable clusters. So, we treat it a + * little differently. The root directory starts at "cluster" 0. + */ + diroff = 0; + for (frcn = 0; diroff < dp->de_FileSize; frcn++) { + if ((error = pcbmap(dp, frcn, &bn, &cluster, &blsize)) != 0) { + if (error == E2BIG) + break; + return (error); + } + error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize, NOCRED, + 0, &bp); + if (error) { + return (error); + } + for (blkoff = 0; blkoff < blsize; + blkoff += sizeof(struct direntry), + diroff += sizeof(struct direntry)) { + dep = (struct direntry *)((char *)bp->b_data + blkoff); + /* + * If the slot is empty and we are still looking + * for an empty then remember this one. If the + * slot is not empty then check to see if it + * matches what we are looking for. If the slot + * has never been filled with anything, then the + * remainder of the directory has never been used, + * so there is no point in searching it. + */ + if (dep->deName[0] == SLOT_EMPTY || + dep->deName[0] == SLOT_DELETED) { + /* + * Drop memory of previous long matches + */ + chksum = -1; + + if (slotcount < wincnt) { + slotcount++; + slotoffset = diroff; + } + if (dep->deName[0] == SLOT_EMPTY) { + brelse(bp, 0); + goto notfound; + } + } else { + /* + * If there wasn't enough space for our + * winentries, forget about the empty space + */ + if (slotcount < wincnt) + slotcount = 0; + + /* + * Check for Win95 long filename entry + */ + if (dep->deAttributes == ATTR_WIN95) { + if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) + continue; + + chksum = winChkName((const u_char *)cnp->cn_nameptr, + cnp->cn_namelen, + (struct winentry *)dep, + chksum); + continue; + } + + /* + * Ignore volume labels (anywhere, not just + * the root directory). + */ + if (dep->deAttributes & ATTR_VOLUME) { + chksum = -1; + continue; + } + + /* + * Check for a checksum or name match + */ + chksum_ok = (chksum == winChksum(dep->deName)); + if (!chksum_ok + && (!olddos || memcmp(dosfilename, dep->deName, 11))) { + chksum = -1; + continue; + } + DPRINTF(("%s(): match blkoff %d, diroff %d\n", + __func__, blkoff, diroff)); + /* + * Remember where this directory + * entry came from for whoever did + * this lookup. + */ + dp->de_fndoffset = diroff; + dp->de_fndcnt = 0; + + return EEXIST; + } + } /* for (blkoff = 0; .... */ + /* + * Release the buffer holding the directory cluster just + * searched. + */ + brelse(bp, 0); + } /* for (frcn = 0; ; frcn++) */ + +notfound: + /* + * We hold no disk buffers at this point. + */ + + /* + * If we get here we didn't find the entry we were looking for. But + * that's ok if we are creating or renaming and are at the end of + * the pathname and the directory hasn't been removed. + */ + DPRINTF(("%s(): refcnt %ld, slotcount %d, slotoffset %d\n", + __func__, dp->de_refcnt, slotcount, slotoffset)); + /* + * Fixup the slot description to point to the place where + * we might put the new DOS direntry (putting the Win95 + * long name entries before that) + */ + if (!slotcount) { + slotcount = 1; + slotoffset = diroff; + } + if (wincnt > slotcount) { + slotoffset += sizeof(struct direntry) * (wincnt - slotcount); + } + + /* + * Return an indication of where the new directory + * entry should be put. + */ + dp->de_fndoffset = slotoffset; + dp->de_fndcnt = wincnt - 1; + + /* + * We return with the directory locked, so that + * the parameters we set up above will still be + * valid if we actually decide to do a direnter(). + * We return ni_vp == NULL to indicate that the entry + * does not currently exist; we leave a pointer to + * the (locked) directory inode in ndp->ni_dvp. + * + * NB - if the directory is unlocked, then this + * information cannot be used. + */ + return 0; +} + +/* + * Create a regular file. On entry the directory to contain the file being + * created is locked. We must release before we return. + */ +struct denode * +msdosfs_mkfile(const char *path, struct denode *pdep, fsnode *node) +{ + struct componentname cn; + struct denode ndirent; + struct denode *dep; + int error; + struct stat *st = &node->inode->st; + struct msdosfsmount *pmp = pdep->de_pmp; + + cn.cn_nameptr = node->name; + cn.cn_namelen = strlen(node->name); + + DPRINTF(("%s(name %s, mode 0%o size %zu)\n", __func__, node->name, + st->st_mode, (size_t)st->st_size)); + + /* + * If this is the root directory and there is no space left we + * can't do anything. This is because the root directory can not + * change size. + */ + if (pdep->de_StartCluster == MSDOSFSROOT + && pdep->de_fndoffset >= pdep->de_FileSize) { + error = ENOSPC; + goto bad; + } + + /* + * Create a directory entry for the file, then call createde() to + * have it installed. NOTE: DOS files are always executable. We + * use the absence of the owner write bit to make the file + * readonly. + */ + memset(&ndirent, 0, sizeof(ndirent)); + if ((error = uniqdosname(pdep, &cn, ndirent.de_Name)) != 0) + goto bad; + + ndirent.de_Attributes = (st->st_mode & S_IWUSR) ? + ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY; + ndirent.de_StartCluster = 0; + ndirent.de_FileSize = 0; + ndirent.de_dev = pdep->de_dev; + ndirent.de_devvp = pdep->de_devvp; + ndirent.de_pmp = pdep->de_pmp; + ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE; + msdosfs_times(pmp, &ndirent, st); + if ((error = msdosfs_findslot(pdep, &cn)) != 0) + goto bad; + if ((error = createde(&ndirent, pdep, &dep, &cn)) != 0) + goto bad; + if ((error = msdosfs_wfile(path, dep, node)) != 0) + goto bad; + return dep; + +bad: + errno = error; + return NULL; +} +static int +msdosfs_updatede(struct denode *dep) +{ + struct buf *bp; + struct direntry *dirp; + int error; + + dep->de_flag &= ~DE_MODIFIED; + error = readde(dep, &bp, &dirp); + if (error) + return error; + DE_EXTERNALIZE(dirp, dep); + error = bwrite(bp); + return error; +} + +/* + * Write data to a file or directory. + */ +static int +msdosfs_wfile(const char *path, struct denode *dep, fsnode *node) +{ + int error, fd; + size_t osize = dep->de_FileSize; + struct stat *st = &node->inode->st; + size_t nsize, offs; + struct msdosfsmount *pmp = dep->de_pmp; + struct buf *bp; + char *dat; + u_long cn = 0; + + error = 0; /* XXX: gcc/vax */ + DPRINTF(("%s(diroff %lu, dirclust %lu, startcluster %lu)\n", __func__, + dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster)); + if (st->st_size == 0) + return 0; + + /* Don't bother to try to write files larger than the fs limit */ + if (st->st_size > MSDOSFS_FILESIZE_MAX) { + errno = EFBIG; + return -1; + } + + nsize = st->st_size; + DPRINTF(("%s(nsize=%zu, osize=%zu)\n", __func__, nsize, osize)); + if (nsize > osize) { + if ((error = deextend(dep, nsize, NULL)) != 0) { + errno = error; + return -1; + } + if ((error = msdosfs_updatede(dep)) != 0) { + errno = error; + return -1; + } + } + + if ((fd = open(path, O_RDONLY)) == -1) + err(1, "open %s", path); + + if ((dat = mmap(0, nsize, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0)) + == MAP_FAILED) { + DPRINTF(("%s: mmap %s %s", __func__, node->name, + strerror(errno))); + close(fd); + goto out; + } + close(fd); + + for (offs = 0; offs < nsize;) { + int blsize, cpsize; + daddr_t bn; + u_long on = offs & pmp->pm_crbomask; +#ifdef HACK + cn = dep->de_StartCluster; + if (cn == MSDOSFSROOT) { + DPRINTF(("%s: bad lbn %lu", __func__, cn)); + goto out; + } + bn = cntobn(pmp, cn); + blsize = pmp->pm_bpcluster; +#else + if ((error = pcbmap(dep, cn++, &bn, NULL, &blsize)) != 0) { + DPRINTF(("%s: pcbmap %lu", __func__, bn)); + goto out; + } +#endif + DPRINTF(("%s(cn=%lu, bn=%llu/%llu, blsize=%d)\n", __func__, + cn, (unsigned long long)bn, + (unsigned long long)de_bn2kb(pmp, bn), blsize)); + if ((error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize, + NULL, 0, &bp)) != 0) { + DPRINTF(("bread %d\n", error)); + goto out; + } + cpsize = MIN((nsize - offs), blsize - on); + memcpy((char *)bp->b_data + on, dat + offs, cpsize); + bwrite(bp); + offs += cpsize; + } + + munmap(dat, nsize); + return 0; +out: + munmap(dat, nsize); + return error; +} + + +static const struct { + struct direntry dot; + struct direntry dotdot; +} dosdirtemplate = { + { ". ", " ", /* the . entry */ + ATTR_DIRECTORY, /* file attribute */ + 0, /* reserved */ + 0, { 0, 0 }, { 0, 0 }, /* create time & date */ + { 0, 0 }, /* access date */ + { 0, 0 }, /* high bits of start cluster */ + { 210, 4 }, { 210, 4 }, /* modify time & date */ + { 0, 0 }, /* startcluster */ + { 0, 0, 0, 0 } /* filesize */ + }, + { ".. ", " ", /* the .. entry */ + ATTR_DIRECTORY, /* file attribute */ + 0, /* reserved */ + 0, { 0, 0 }, { 0, 0 }, /* create time & date */ + { 0, 0 }, /* access date */ + { 0, 0 }, /* high bits of start cluster */ + { 210, 4 }, { 210, 4 }, /* modify time & date */ + { 0, 0 }, /* startcluster */ + { 0, 0, 0, 0 } /* filesize */ + } +}; + +struct denode * +msdosfs_mkdire(const char *path, struct denode *pdep, fsnode *node) { + struct denode ndirent; + struct denode *dep; + struct componentname cn; + struct stat *st = &node->inode->st; + struct msdosfsmount *pmp = pdep->de_pmp; + int error; + u_long newcluster, pcl, bn; + daddr_t lbn; + struct direntry *denp; + struct buf *bp; + + cn.cn_nameptr = node->name; + cn.cn_namelen = strlen(node->name); + /* + * If this is the root directory and there is no space left we + * can't do anything. This is because the root directory can not + * change size. + */ + if (pdep->de_StartCluster == MSDOSFSROOT + && pdep->de_fndoffset >= pdep->de_FileSize) { + error = ENOSPC; + goto bad2; + } + + /* + * Allocate a cluster to hold the about to be created directory. + */ + error = clusteralloc(pmp, 0, 1, &newcluster, NULL); + if (error) + goto bad2; + + memset(&ndirent, 0, sizeof(ndirent)); + ndirent.de_pmp = pmp; + ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE; + msdosfs_times(pmp, &ndirent, st); + + /* + * Now fill the cluster with the "." and ".." entries. And write + * the cluster to disk. This way it is there for the parent + * directory to be pointing at if there were a crash. + */ + bn = cntobn(pmp, newcluster); + lbn = de_bn2kb(pmp, bn); + DPRINTF(("%s(newcluster %lu, bn=%lu, lbn=%lu)\n", __func__, newcluster, + bn, lbn)); + /* always succeeds */ + bp = getblk(pmp->pm_devvp, lbn, pmp->pm_bpcluster, 0, 0); + memset(bp->b_data, 0, pmp->pm_bpcluster); + memcpy(bp->b_data, &dosdirtemplate, sizeof dosdirtemplate); + denp = (struct direntry *)bp->b_data; + putushort(denp[0].deStartCluster, newcluster); + putushort(denp[0].deCDate, ndirent.de_CDate); + putushort(denp[0].deCTime, ndirent.de_CTime); + denp[0].deCHundredth = ndirent.de_CHun; + putushort(denp[0].deADate, ndirent.de_ADate); + putushort(denp[0].deMDate, ndirent.de_MDate); + putushort(denp[0].deMTime, ndirent.de_MTime); + pcl = pdep->de_StartCluster; + DPRINTF(("%s(pcl %lu, rootdirblk=%lu)\n", __func__, pcl, + pmp->pm_rootdirblk)); + if (FAT32(pmp) && pcl == pmp->pm_rootdirblk) + pcl = 0; + putushort(denp[1].deStartCluster, pcl); + putushort(denp[1].deCDate, ndirent.de_CDate); + putushort(denp[1].deCTime, ndirent.de_CTime); + denp[1].deCHundredth = ndirent.de_CHun; + putushort(denp[1].deADate, ndirent.de_ADate); + putushort(denp[1].deMDate, ndirent.de_MDate); + putushort(denp[1].deMTime, ndirent.de_MTime); + if (FAT32(pmp)) { + putushort(denp[0].deHighClust, newcluster >> 16); + putushort(denp[1].deHighClust, pdep->de_StartCluster >> 16); + } else { + putushort(denp[0].deHighClust, 0); + putushort(denp[1].deHighClust, 0); + } + + if ((error = bwrite(bp)) != 0) + goto bad; + + /* + * Now build up a directory entry pointing to the newly allocated + * cluster. This will be written to an empty slot in the parent + * directory. + */ + if ((error = uniqdosname(pdep, &cn, ndirent.de_Name)) != 0) + goto bad; + + ndirent.de_Attributes = ATTR_DIRECTORY; + ndirent.de_StartCluster = newcluster; + ndirent.de_FileSize = 0; + ndirent.de_dev = pdep->de_dev; + ndirent.de_devvp = pdep->de_devvp; + ndirent.de_pmp = pdep->de_pmp; + if ((error = msdosfs_findslot(pdep, &cn)) != 0) + goto bad; + if ((error = createde(&ndirent, pdep, &dep, &cn)) != 0) + goto bad; + if ((error = msdosfs_updatede(dep)) != 0) + goto bad; + return dep; + +bad: + clusterfree(pmp, newcluster, NULL); +bad2: + errno = error; + return NULL; +} diff --git a/usr.sbin/makefs/udf.c b/usr.sbin/makefs/udf.c new file mode 100644 index 000000000..ad0136cdc --- /dev/null +++ b/usr.sbin/makefs/udf.c @@ -0,0 +1,1377 @@ +/* $NetBSD: udf.c,v 1.14 2013/10/19 17:16:37 christos Exp $ */ + +/* + * Copyright (c) 2006, 2008, 2013 Reinoud Zandijk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +__RCSID("$NetBSD: udf.c,v 1.14 2013/10/19 17:16:37 christos Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !HAVE_NBTOOL_CONFIG_H +#define _EXPOSE_MMC +#include +#else +#include "udf/cdio_mmc_structs.h" +#endif + +#if !HAVE_NBTOOL_CONFIG_H +#define HAVE_STRUCT_TM_TM_GMTOFF +#endif + +#include "makefs.h" +#include "udf_create.h" +#include "udf_write.h" +#include "newfs_udf.h" + +#undef APP_NAME +#define APP_NAME "*NetBSD makefs" + +/* + * Note: due to the setup of the newfs code, the current state of the program + * and its options are helt in a few global variables. The FS specific parts + * are in a global `context' structure. + */ + +/* global variables describing disc and format requests */ +int fd; /* device: file descriptor */ +char *dev; /* device: name */ +struct mmc_discinfo mmc_discinfo; /* device: disc info */ + +char *format_str; /* format: string representation */ +int format_flags; /* format: attribute flags */ +int media_accesstype; /* derived from current mmc cap */ +int check_surface; /* for rewritables */ +int imagefile_secsize; /* for files */ + +int display_progressbar; + +int wrtrack_skew; +float meta_fract = (float) UDF_META_PERC / 100.0; + +int mmc_profile; /* emulated profile */ +int req_enable, req_disable; + + +/* --------------------------------------------------------------------- */ + +int +udf_write_sector(void *sector, uint64_t location) +{ + uint64_t wpos; + ssize_t ret; + + wpos = (uint64_t) location * context.sector_size; + ret = pwrite(fd, sector, context.sector_size, wpos); + if (ret == -1) + return errno; + if (ret < (int) context.sector_size) + return EIO; + return 0; +} + + +/* not implemented for files */ +int +udf_surface_check(void) +{ + return 0; +} + + +/* + * mmc_discinfo and mmc_trackinfo readers modified from origional in udf main + * code in sys/fs/udf/ + */ + +#ifdef DEBUG +static void +udf_dump_discinfo(struct mmc_discinfo *di) +{ + char bits[128]; + + printf("Device/media info :\n"); + printf("\tMMC profile 0x%02x\n", di->mmc_profile); + printf("\tderived class %d\n", di->mmc_class); + printf("\tsector size %d\n", di->sector_size); + printf("\tdisc state %d\n", di->disc_state); + printf("\tlast ses state %d\n", di->last_session_state); + printf("\tbg format state %d\n", di->bg_format_state); + printf("\tfrst track %d\n", di->first_track); + printf("\tfst on last ses %d\n", di->first_track_last_session); + printf("\tlst on last ses %d\n", di->last_track_last_session); + printf("\tlink block penalty %d\n", di->link_block_penalty); + snprintb(bits, sizeof(bits), MMC_DFLAGS_FLAGBITS, + (uint64_t) di->disc_flags); + printf("\tdisc flags %s\n", bits); + printf("\tdisc id %x\n", di->disc_id); + printf("\tdisc barcode %"PRIx64"\n", di->disc_barcode); + + printf("\tnum sessions %d\n", di->num_sessions); + printf("\tnum tracks %d\n", di->num_tracks); + + snprintb(bits, sizeof(bits), MMC_CAP_FLAGBITS, di->mmc_cur); + printf("\tcapabilities cur %s\n", bits); + snprintb(bits, sizeof(bits), MMC_CAP_FLAGBITS, di->mmc_cap); + printf("\tcapabilities cap %s\n", bits); + printf("\n"); + printf("\tlast_possible_lba %d\n", di->last_possible_lba); + printf("\n"); +} +#else +#define udf_dump_discinfo(a); +#endif + +/* --------------------------------------------------------------------- */ + +static int +udf_emulate_discinfo(fsinfo_t *fsopts, struct mmc_discinfo *di, + int mmc_emuprofile) +{ + off_t sectors; + + memset(di, 0, sizeof(*di)); + + /* file support */ + if ((mmc_emuprofile != 0x01) && (fsopts->sectorsize != 2048)) + warnx("cd/dvd/bd sectorsize is not set to default 2048"); + + sectors = fsopts->size / fsopts->sectorsize; + + /* commons */ + di->mmc_profile = mmc_emuprofile; + di->disc_state = MMC_STATE_CLOSED; + di->last_session_state = MMC_STATE_CLOSED; + di->bg_format_state = MMC_BGFSTATE_COMPLETED; + di->link_block_penalty = 0; + + di->disc_flags = MMC_DFLAGS_UNRESTRICTED; + + di->last_possible_lba = sectors - 1; + di->sector_size = fsopts->sectorsize; + + di->num_sessions = 1; + di->num_tracks = 1; + + di->first_track = 1; + di->first_track_last_session = di->last_track_last_session = 1; + + di->mmc_cur = MMC_CAP_RECORDABLE | MMC_CAP_ZEROLINKBLK; + switch (mmc_emuprofile) { + case 0x00: /* unknown, treat as CDROM */ + case 0x08: /* CDROM */ + case 0x10: /* DVDROM */ + case 0x40: /* BDROM */ + req_enable |= FORMAT_READONLY; + /* FALLTROUGH */ + case 0x01: /* disc */ + /* set up a disc info profile for partitions/files */ + di->mmc_class = MMC_CLASS_DISC; + di->mmc_cur |= MMC_CAP_REWRITABLE | MMC_CAP_HW_DEFECTFREE; + break; + case 0x09: /* CD-R */ + di->mmc_class = MMC_CLASS_CD; + di->mmc_cur |= MMC_CAP_SEQUENTIAL; + di->disc_state = MMC_STATE_EMPTY; + break; + case 0x0a: /* CD-RW + CD-MRW (regretably) */ + di->mmc_class = MMC_CLASS_CD; + di->mmc_cur |= MMC_CAP_REWRITABLE; + break; + case 0x13: /* DVD-RW */ + di->mmc_class = MMC_CLASS_DVD; + di->mmc_cur |= MMC_CAP_REWRITABLE; + break; + case 0x11: /* DVD-R */ + case 0x14: /* DVD-RW sequential */ + case 0x1b: /* DVD+R */ + case 0x2b: /* DVD+R DL */ + case 0x51: /* HD DVD-R */ + di->mmc_class = MMC_CLASS_DVD; + di->mmc_cur |= MMC_CAP_SEQUENTIAL; + di->disc_state = MMC_STATE_EMPTY; + break; + case 0x41: /* BD-R */ + di->mmc_class = MMC_CLASS_BD; + di->mmc_cur |= MMC_CAP_SEQUENTIAL | MMC_CAP_HW_DEFECTFREE; + di->disc_state = MMC_STATE_EMPTY; + break; + case 0x43: /* BD-RE */ + di->mmc_class = MMC_CLASS_BD; + di->mmc_cur |= MMC_CAP_REWRITABLE | MMC_CAP_HW_DEFECTFREE; + break; + default: + err(EINVAL, "makefs_udf: unknown or unimplemented device type"); + return EINVAL; + } + di->mmc_cap = di->mmc_cur; + + udf_dump_discinfo(di); + return 0; +} + + +int +udf_update_trackinfo(struct mmc_discinfo *di, struct mmc_trackinfo *ti) +{ + /* discs partition support */ + if (ti->tracknr != 1) + return EIO; + + /* create fake ti */ + ti->sessionnr = 1; + + ti->track_mode = 0; /* XXX */ + ti->data_mode = 0; /* XXX */ + ti->flags = MMC_TRACKINFO_LRA_VALID | MMC_TRACKINFO_NWA_VALID; + + ti->track_start = 0; + ti->packet_size = 32; /* take sensible default */ + + ti->track_size = di->last_possible_lba; + ti->next_writable = di->last_possible_lba; + ti->last_recorded = ti->next_writable; + ti->free_blocks = 0; + + return 0; +} + +#define OPT_STR(letter, name, desc) \ + { letter, name, NULL, OPT_STRBUF, 0, 0, desc } + +#define OPT_NUM(letter, name, field, min, max, desc) \ + { letter, name, &context.field, \ + sizeof(context.field) == 8 ? OPT_INT64 : \ + (sizeof(context.field) == 4 ? OPT_INT32 : \ + (sizeof(context.field) == 2 ? OPT_INT16 : OPT_INT8)), \ + min, max, desc } + +#define OPT_BOOL(letter, name, field, desc) \ + OPT_NUM(letter, name, field, 0, 1, desc) + +void +udf_prep_opts(fsinfo_t *fsopts) +{ + struct tm *tm; + time_t now; + + const option_t udf_options[] = { + OPT_STR('T', "disctype", "disc type (cdrom,dvdrom,bdrom," + "dvdram,bdre,disk,cdr,dvdr,bdr,cdrw,dvdrw)"), + OPT_STR('L', "loglabel", "\"logical volume name\""), + OPT_STR('P', "discid", "\"[volset name ':']" + "physical volume name\""), + OPT_NUM('t', "tz", gmtoff, -24, 24, "timezone"), + OPT_STR('v', "minver", "minimum UDF version in either " + "``0x201'' or ``2.01'' format"), +#if notyet + OPT_STR('V', "maxver", "maximum UDF version in either " + "``0x201'' or ``2.01'' format"), +#endif + { .name = NULL } + }; + + /* initialise */ + format_str = strdup(""); + req_enable = req_disable = 0; + format_flags = FORMAT_INVALID; + fsopts->sectorsize = 512; /* minimum allowed sector size */ + + srandom((unsigned long) time(NULL)); + + mmc_profile = 0x01; /* 'disc'/file */ + + udf_init_create_context(); + context.app_name = "*NetBSD makefs"; + context.app_version_main = APP_VERSION_MAIN; + context.app_version_sub = APP_VERSION_SUB; + context.impl_name = IMPL_NAME; + + /* minimum and maximum UDF versions we advise */ + context.min_udf = 0x102; + context.max_udf = 0x201; /* 0x250 and 0x260 are not ready */ + + /* use user's time zone as default */ + (void)time(&now); + tm = localtime(&now); +#ifdef HAVE_STRUCT_TM_TM_GMTOFF + context.gmtoff = tm->tm_gmtoff; +#else + context.gmtoff = 0; +#endif + + /* return info */ + fsopts->fs_specific = NULL; + fsopts->fs_options = copy_opts(udf_options); +} + + +void +udf_cleanup_opts(fsinfo_t *fsopts) +{ + free(fsopts->fs_options); +} + + +/* ----- included from newfs_udf.c ------ */ +/* ----- */ + + +#define CDRSIZE ((uint64_t) 700*1024*1024) /* small approx */ +#define CDRWSIZE ((uint64_t) 576*1024*1024) /* small approx */ +#define DVDRSIZE ((uint64_t) 4488*1024*1024) /* small approx */ +#define DVDRAMSIZE ((uint64_t) 4330*1024*1024) /* small approx with spare */ +#define DVDRWSIZE ((uint64_t) 4482*1024*1024) /* small approx */ +#define BDRSIZE ((uint64_t) 23866*1024*1024) /* small approx */ +#define BDRESIZE ((uint64_t) 23098*1024*1024) /* small approx */ +int +udf_parse_opts(const char *option, fsinfo_t *fsopts) +{ + option_t *udf_options = fsopts->fs_options; + uint64_t stdsize; + uint32_t set_sectorsize; + char buffer[1024], *buf, *colon; + int i; + + assert(option != NULL); + + if (debug & DEBUG_FS_PARSE_OPTS) + printf("udf_parse_opts: got `%s'\n", option); + + i = set_option(udf_options, option, buffer, sizeof(buffer)); + if (i == -1) + return 0; + + if (udf_options[i].name == NULL) + abort(); + + set_sectorsize = 0; + stdsize = 0; + + buf = buffer; + switch (udf_options[i].letter) { + case 'T': + if (strcmp(buf, "cdrom") == 0) { + mmc_profile = 0x00; + } else if (strcmp(buf, "dvdrom") == 0) { + mmc_profile = 0x10; + } else if (strcmp(buf, "bdrom") == 0) { + mmc_profile = 0x40; + } else if (strcmp(buf, "dvdram") == 0) { + mmc_profile = 0x12; + stdsize = DVDRAMSIZE; + } else if (strcmp(buf, "bdre") == 0) { + mmc_profile = 0x43; + stdsize = BDRESIZE; + } else if (strcmp(buf, "disk") == 0) { + mmc_profile = 0x01; + } else if (strcmp(buf, "cdr") == 0) { + mmc_profile = 0x09; + stdsize = CDRSIZE; + } else if (strcmp(buf, "dvdr") == 0) { + mmc_profile = 0x1b; + stdsize = DVDRSIZE; + } else if (strcmp(buf, "bdr") == 0) { + mmc_profile = 0x41; + stdsize = BDRSIZE; + } else if (strcmp(buf, "cdrw") == 0) { + mmc_profile = 0x0a; + stdsize = CDRWSIZE; + } else if (strcmp(buf, "dvdrw") == 0) { + mmc_profile = 0x13; + stdsize = DVDRWSIZE; + } else { + errx(EINVAL, "Unknown or unimplemented disc format"); + return 0; + } + if (mmc_profile != 0x01) + set_sectorsize = 2048; + break; + case 'L': + if (context.logvol_name) free(context.logvol_name); + context.logvol_name = strdup(buf); + break; + case 'P': + if ((colon = strstr(buf, ":"))) { + if (context.volset_name) + free(context.volset_name); + *colon = 0; + context.volset_name = strdup(buf); + buf = colon+1; + } + if (context.primary_name) + free(context.primary_name); + if ((strstr(buf, ":"))) { + errx(EINVAL, "primary name can't have ':' in its name"); + return 0; + } + context.primary_name = strdup(buf); + break; + case 'v': + context.min_udf = a_udf_version(buf, "min_udf"); + if (context.min_udf > 0x201) { + errx(EINVAL, "maximum supported version is UDF 2.01"); + return 0; + } + if (context.min_udf > context.max_udf) + context.max_udf = context.min_udf; + break; + } + if (set_sectorsize) + fsopts->sectorsize = set_sectorsize; + if (stdsize) + fsopts->size = stdsize; + return 1; +} + +/* --------------------------------------------------------------------- */ + +struct udf_stats { + uint32_t nfiles; + uint32_t ndirs; + uint32_t ndescr; + uint32_t nmetadatablocks; + uint32_t ndatablocks; +}; + + +/* node reference administration */ +static void +udf_inc_link(union dscrptr *dscr) +{ + struct file_entry *fe; + struct extfile_entry *efe; + + if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) { + fe = &dscr->fe; + fe->link_cnt = udf_rw16(udf_rw16(fe->link_cnt) + 1); + } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) { + efe = &dscr->efe; + efe->link_cnt = udf_rw16(udf_rw16(efe->link_cnt) + 1); + } else { + errx(1, "Bad tag passed to udf_inc_link"); + } +} + + +static void +udf_set_link_cnt(union dscrptr *dscr, int num) +{ + struct file_entry *fe; + struct extfile_entry *efe; + + if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) { + fe = &dscr->fe; + fe->link_cnt = udf_rw16(num); + } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) { + efe = &dscr->efe; + efe->link_cnt = udf_rw16(num); + } else { + errx(1, "Bad tag passed to udf_set_link_cnt"); + } +} + + +static uint32_t +udf_datablocks(off_t sz) +{ + /* predictor if it can be written inside the node */ + if (sz < context.sector_size - UDF_EXTFENTRY_SIZE - 16) + return 0; + + return UDF_ROUNDUP(sz, context.sector_size) / context.sector_size; +} + + +static void +udf_prepare_fids(struct long_ad *dir_icb, struct long_ad *dirdata_icb, + uint8_t *dirdata, uint32_t dirdata_size) +{ + struct fileid_desc *fid; + struct long_ad *icb; + uint32_t fidsize, offset; + uint32_t location; + + if (udf_datablocks(dirdata_size) == 0) { + /* going internal */ + icb = dir_icb; + } else { + /* external blocks to write to */ + icb = dirdata_icb; + } + + for (offset = 0; offset < dirdata_size; offset += fidsize) { + /* for each FID: */ + fid = (struct fileid_desc *) (dirdata + offset); + assert(udf_rw16(fid->tag.id) == TAGID_FID); + + location = udf_rw32(icb->loc.lb_num); + location += offset / context.sector_size; + + fid->tag.tag_loc = udf_rw32(location); + udf_validate_tag_and_crc_sums((union dscrptr *) fid); + + fidsize = udf_fidsize(fid); + } +} + +static int +udf_file_inject_blob(union dscrptr *dscr, uint8_t *blob, off_t size) +{ + struct icb_tag *icb; + struct file_entry *fe; + struct extfile_entry *efe; + uint64_t inf_len, obj_size; + uint32_t l_ea, l_ad; + uint32_t free_space, desc_size; + uint16_t crclen; + uint8_t *data, *pos; + + fe = NULL; + efe = NULL; + if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) { + fe = &dscr->fe; + data = fe->data; + l_ea = udf_rw32(fe->l_ea); + l_ad = udf_rw32(fe->l_ad); + icb = &fe->icbtag; + inf_len = udf_rw64(fe->inf_len); + obj_size = 0; + desc_size = sizeof(struct file_entry); + } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) { + efe = &dscr->efe; + data = efe->data; + l_ea = udf_rw32(efe->l_ea); + l_ad = udf_rw32(efe->l_ad); + icb = &efe->icbtag; + inf_len = udf_rw64(efe->inf_len); + obj_size = udf_rw64(efe->obj_size); + desc_size = sizeof(struct extfile_entry); + } else { + errx(1, "Bad tag passed to udf_file_inject_blob"); + } + crclen = udf_rw16(dscr->tag.desc_crc_len); + + /* calculate free space */ + free_space = context.sector_size - (l_ea + l_ad) - desc_size; + if (udf_datablocks(size)) { + assert(free_space < size); + return 1; + } + + /* going internal */ + assert(l_ad == 0); + assert((udf_rw16(icb->flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK) == + UDF_ICB_INTERN_ALLOC); + + // assert(free_space >= size); + pos = data + l_ea + l_ad; + memcpy(pos, blob, size); + l_ad += size; + crclen += size; + + inf_len += size; + obj_size += size; + + if (fe) { + fe->l_ad = udf_rw32(l_ad); + fe->inf_len = udf_rw64(inf_len); + } else if (efe) { + efe->l_ad = udf_rw32(l_ad); + efe->inf_len = udf_rw64(inf_len); + efe->obj_size = udf_rw64(inf_len); + } + + /* make sure the header sums stays correct */ + dscr->tag.desc_crc_len = udf_rw16(crclen); + udf_validate_tag_and_crc_sums(dscr); + + return 0; +} + + +/* XXX no sparse file support */ +static void +udf_append_file_mapping(union dscrptr *dscr, struct long_ad *piece) +{ + struct icb_tag *icb; + struct file_entry *fe; + struct extfile_entry *efe; + struct long_ad *last_long, last_piece; + struct short_ad *last_short, new_short; + uint64_t inf_len, obj_size, logblks_rec; + uint32_t l_ea, l_ad, size; + uint32_t last_lb_num, piece_lb_num; + uint64_t last_len, piece_len, last_flags; + uint64_t rest_len, merge_len, last_end; + uint16_t last_part_num, piece_part_num; + uint16_t crclen, cur_alloc; + uint8_t *data, *pos; + const int short_len = sizeof(struct short_ad); + const int long_len = sizeof(struct long_ad); + const int sector_size = context.sector_size; + const int use_shorts = (context.data_part == context.metadata_part); + uint64_t max_len = UDF_ROUNDDOWN(UDF_EXT_MAXLEN, sector_size); + + fe = NULL; + efe = NULL; + if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) { + fe = &dscr->fe; + data = fe->data; + l_ea = fe->l_ea; + l_ad = udf_rw32(fe->l_ad); + icb = &fe->icbtag; + inf_len = udf_rw64(fe->inf_len); + logblks_rec = udf_rw64(fe->logblks_rec); + obj_size = 0; + } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) { + efe = &dscr->efe; + data = efe->data; + l_ea = efe->l_ea; + l_ad = udf_rw32(efe->l_ad); + icb = &efe->icbtag; + inf_len = udf_rw64(efe->inf_len); + obj_size = udf_rw64(efe->obj_size); + logblks_rec = udf_rw64(efe->logblks_rec); + } else { + errx(1, "Bad tag passed to udf_file_append_blob"); + } + crclen = udf_rw16(dscr->tag.desc_crc_len); + + pos = data + l_ea; + cur_alloc = udf_rw16(icb->flags); + size = UDF_EXT_LEN(udf_rw32(piece->len)); + + /* extract last entry as a long_ad */ + memset(&last_piece, 0, sizeof(last_piece)); + last_len = 0; + last_lb_num = 0; + last_part_num = 0; + last_flags = 0; + last_short = NULL; + last_long = NULL; + if (l_ad != 0) { + if (use_shorts) { + assert(cur_alloc == UDF_ICB_SHORT_ALLOC); + pos += l_ad - short_len; + last_short = (struct short_ad *) pos; + last_lb_num = udf_rw32(last_short->lb_num); + last_part_num = udf_rw16(piece->loc.part_num); + last_len = UDF_EXT_LEN(udf_rw32(last_short->len)); + last_flags = UDF_EXT_FLAGS(udf_rw32(last_short->len)); + } else { + assert(cur_alloc == UDF_ICB_LONG_ALLOC); + pos += l_ad - long_len; + last_long = (struct long_ad *) pos; + last_lb_num = udf_rw32(last_long->loc.lb_num); + last_part_num = udf_rw16(last_long->loc.part_num); + last_len = UDF_EXT_LEN(udf_rw32(last_long->len)); + last_flags = UDF_EXT_FLAGS(udf_rw32(last_long->len)); + } + } + + piece_len = UDF_EXT_LEN(udf_rw32(piece->len)); + piece_lb_num = udf_rw32(piece->loc.lb_num); + piece_part_num = udf_rw16(piece->loc.part_num); + + /* try merging */ + rest_len = max_len - last_len; + + merge_len = MIN(piece_len, rest_len); + last_end = last_lb_num + (last_len / sector_size); + + if ((piece_lb_num == last_end) && (last_part_num == piece_part_num)) { + /* we can merge */ + last_len += merge_len; + piece_len -= merge_len; + + /* write back merge result */ + if (use_shorts) { + last_short->len = udf_rw32(last_len | last_flags); + } else { + last_long->len = udf_rw32(last_len | last_flags); + } + piece_lb_num += merge_len / sector_size; + } + + if (piece_len) { + /* append new entry */ + pos = data + l_ea + l_ad; + if (use_shorts) { + icb->flags = udf_rw16(UDF_ICB_SHORT_ALLOC); + memset(&new_short, 0, short_len); + new_short.len = udf_rw32(piece_len); + new_short.lb_num = udf_rw32(piece_lb_num); + memcpy(pos, &new_short, short_len); + l_ad += short_len; + crclen += short_len; + } else { + icb->flags = udf_rw16(UDF_ICB_LONG_ALLOC); + piece->len = udf_rw32(piece_len); + piece->loc.lb_num = udf_rw32(piece_lb_num); + memcpy(pos, piece, long_len); + l_ad += long_len; + crclen += long_len; + } + } + piece->len = udf_rw32(0); + + inf_len += size; + obj_size += size; + logblks_rec += UDF_ROUNDUP(size, sector_size) / sector_size; + + dscr->tag.desc_crc_len = udf_rw16(crclen); + if (udf_rw16(dscr->tag.id) == TAGID_FENTRY) { + fe->l_ad = udf_rw32(l_ad); + fe->inf_len = udf_rw64(inf_len); + fe->logblks_rec = udf_rw64(logblks_rec); + } else if (udf_rw16(dscr->tag.id) == TAGID_EXTFENTRY) { + efe->l_ad = udf_rw32(l_ad); + efe->inf_len = udf_rw64(inf_len); + efe->obj_size = udf_rw64(inf_len); + efe->logblks_rec = udf_rw64(logblks_rec); + } +} + + +static int +udf_append_file_contents(union dscrptr *dscr, struct long_ad *data_icb, + uint8_t *fdata, off_t flen) +{ + struct long_ad icb; + uint32_t location; + uint64_t phys; + uint16_t vpart; + uint8_t *bpos; + int cnt, sects; + int error; + + if (udf_file_inject_blob(dscr, fdata, flen) == 0) + return 0; + + /* has to be appended in mappings */ + icb = *data_icb; + icb.len = udf_rw32(flen); + while (udf_rw32(icb.len) > 0) + udf_append_file_mapping(dscr, &icb); + udf_validate_tag_and_crc_sums(dscr); + + /* write out data piece */ + vpart = udf_rw16(data_icb->loc.part_num); + location = udf_rw32(data_icb->loc.lb_num); + sects = udf_datablocks(flen); + for (cnt = 0; cnt < sects; cnt++) { + bpos = fdata + cnt*context.sector_size; + phys = context.vtop_offset[vpart] + location + cnt; + error = udf_write_sector(bpos, phys); + if (error) + return error; + } + return 0; +} + + +static int +udf_create_new_file(struct stat *st, union dscrptr **dscr, + int filetype, struct long_ad *icb) +{ + struct file_entry *fe; + struct extfile_entry *efe; + int error; + + fe = NULL; + efe = NULL; + if (context.dscrver == 2) { + error = udf_create_new_fe(&fe, filetype, st); + if (error) + errx(error, "can't create fe"); + *dscr = (union dscrptr *) fe; + icb->longad_uniqueid = fe->unique_id; + } else { + error = udf_create_new_efe(&efe, filetype, st); + if (error) + errx(error, "can't create fe"); + *dscr = (union dscrptr *) efe; + icb->longad_uniqueid = efe->unique_id; + } + + return 0; +} + + +static void +udf_estimate_walk(fsinfo_t *fsopts, + fsnode *root, char *dir, struct udf_stats *stats) +{ + struct fileid_desc *fid; + struct long_ad dummy_ref; + fsnode *cur; + fsinode *fnode; + size_t pathlen = strlen(dir); + char *mydir = dir + pathlen; + off_t sz; + uint32_t nblk, ddoff; + uint32_t softlink_len; + uint8_t *softlink_buf; + int nentries; + int error; + + stats->ndirs++; + + /* + * Count number of directory entries and count directory size; needed + * for the reservation of enough space for the directory. Pity we + * don't keep the FIDs we created. If it turns out to be a issue we + * can cache it later. + */ + fid = (struct fileid_desc *) malloc(context.sector_size); + assert(fid); + + ddoff = 40; /* '..' entry */ + for (cur = root, nentries = 0; cur != NULL; cur = cur->next) { + switch (cur->type & S_IFMT) { + default: + /* what kind of nodes? */ + break; + case S_IFCHR: + case S_IFBLK: + /* not supported yet */ + continue; + case S_IFDIR: + if (strcmp(cur->name, ".") == 0) + continue; + case S_IFLNK: + case S_IFREG: + /* create dummy FID to see how long name will become */ + udf_create_fid(ddoff, fid, cur->name, 0, &dummy_ref); + + nentries++; + ddoff += udf_fidsize(fid); + } + } + sz = ddoff; + + root->inode->st.st_size = sz; /* max now */ + root->inode->flags |= FI_SIZED; + + nblk = udf_datablocks(sz); + stats->nmetadatablocks += nblk; + + /* for each entry in the directory, there needs to be a (E)FE */ + stats->nmetadatablocks += nentries + 1; + + /* recurse */ + for (cur = root; cur != NULL; cur = cur->next) { + switch (cur->type & S_IFMT) { + default: + /* what kind of nodes? */ + break; + case S_IFCHR: + case S_IFBLK: + /* not supported yet */ + // stats->nfiles++; + break; + case S_IFDIR: + if (strcmp(cur->name, ".") == 0) + continue; + /* empty dir? */ + if (!cur->child) + break; + mydir[0] = '/'; + strncpy(&mydir[1], cur->name, MAXPATHLEN - pathlen); + udf_estimate_walk(fsopts, cur->child, dir, stats); + mydir[0] = '\0'; + break; + case S_IFREG: + fnode = cur->inode; + /* don't double-count hard-links */ + if (!(fnode->flags & FI_SIZED)) { + sz = fnode->st.st_size; + nblk = udf_datablocks(sz); + stats->ndatablocks += nblk; + /* ... */ + fnode->flags |= FI_SIZED; + } + stats->nfiles++; + break; + case S_IFLNK: + /* softlink */ + fnode = cur->inode; + /* don't double-count hard-links */ + if (!(fnode->flags & FI_SIZED)) { + error = udf_encode_symlink(&softlink_buf, + &softlink_len, cur->symlink); + if (error) { + printf("SOFTLINK error %d\n", error); + break; + } + nblk = udf_datablocks(softlink_len); + stats->ndatablocks += nblk; + fnode->flags |= FI_SIZED; + + free(softlink_buf); + } + stats->nfiles++; + break; + } + } +} + + +#define UDF_MAX_CHUNK_SIZE (4*1024*1024) +static int +udf_copy_file(struct stat *st, char *path, fsnode *cur, struct fileid_desc *fid, + struct long_ad *icb) +{ + union dscrptr *dscr; + struct long_ad data_icb; + fsinode *fnode; + off_t sz, chunk, rd; + uint8_t *data; + int nblk; + int i, f; + int error; + + fnode = cur->inode; + + f = open(path, O_RDONLY); + if (f < 0) { + warn("Can't open file %s for reading", cur->name); + return errno; + } + + /* claim disc space for the (e)fe descriptor for this file */ + udf_metadata_alloc(1, icb); + udf_create_new_file(st, &dscr, UDF_ICB_FILETYPE_RANDOMACCESS, icb); + + sz = fnode->st.st_size; + + chunk = MIN(sz, UDF_MAX_CHUNK_SIZE); + data = malloc(MAX(chunk, context.sector_size)); + assert(data); + + printf(" "); + i = 0; + error = 0; + while (chunk) { + rd = read(f, data, chunk); + if (rd != chunk) { + warn("Short read of file %s\n", cur->name); + error = errno; + break; + } + printf("\b%c", "\\|/-"[i++ % 4]); fflush(stdout);fflush(stderr); + + nblk = udf_datablocks(chunk); + if (nblk > 0) + udf_data_alloc(nblk, &data_icb); + udf_append_file_contents(dscr, &data_icb, data, chunk); + + sz -= chunk; + chunk = MIN(sz, UDF_MAX_CHUNK_SIZE); + } + printf("\b \n"); + close(f); + free(data); + + /* write out dscr (e)fe */ + udf_set_link_cnt(dscr, fnode->nlink); + udf_write_dscr_virt(dscr, udf_rw32(icb->loc.lb_num), + udf_rw16(icb->loc.part_num), 1); + free(dscr); + + /* remember our location for hardlinks */ + cur->inode->fsuse = malloc(sizeof(struct long_ad)); + memcpy(cur->inode->fsuse, icb, sizeof(struct long_ad)); + + return error; +} + + +static int +udf_populate_walk(fsinfo_t *fsopts, fsnode *root, char *dir, + struct long_ad *parent_icb, struct long_ad *dir_icb) +{ + union dscrptr *dir_dscr, *dscr; + struct fileid_desc *fid; + struct long_ad icb, data_icb, dirdata_icb; + fsnode *cur; + fsinode *fnode; + size_t pathlen = strlen(dir); + size_t dirlen; + char *mydir = dir + pathlen; + uint32_t nblk, ddoff; + uint32_t softlink_len; + uint8_t *softlink_buf; + uint8_t *dirdata; + int error, ret, retval; + + /* claim disc space for the (e)fe descriptor for this dir */ + udf_metadata_alloc(1, dir_icb); + + /* create new e(fe) */ + udf_create_new_file(&root->inode->st, &dir_dscr, + UDF_ICB_FILETYPE_DIRECTORY, dir_icb); + + /* claim space for the directory contents */ + dirlen = root->inode->st.st_size; + nblk = udf_datablocks(dirlen); + if (nblk > 0) { + /* claim disc space for the dir contents */ + udf_data_alloc(nblk, &dirdata_icb); + } + + /* allocate memory for the directory contents */ + nblk++; + dirdata = malloc(nblk * context.sector_size); + assert(dirdata); + memset(dirdata, 0, nblk * context.sector_size); + + /* create and append '..' */ + fid = (struct fileid_desc *) dirdata; + ddoff = udf_create_parentfid(fid, parent_icb); + + /* for '..' */ + udf_inc_link(dir_dscr); + + /* recurse */ + retval = 0; + for (cur = root; cur != NULL; cur = cur->next) { + mydir[0] = '/'; + strncpy(&mydir[1], cur->name, MAXPATHLEN - pathlen); + + fid = (struct fileid_desc *) (dirdata + ddoff); + switch (cur->type & S_IFMT) { + default: + /* what kind of nodes? */ + retval = 2; + break; + case S_IFCHR: + case S_IFBLK: + /* not supported */ + retval = 2; + warnx("device node %s not supported", dir); + break; + case S_IFDIR: + /* not an empty dir? */ + if (strcmp(cur->name, ".") == 0) + break; + assert(cur->child); + if (cur->child) { + ret = udf_populate_walk(fsopts, cur->child, + dir, dir_icb, &icb); + if (ret) + retval = 2; + } + udf_create_fid(ddoff, fid, cur->name, + UDF_FILE_CHAR_DIR, &icb); + udf_inc_link(dir_dscr); + ddoff += udf_fidsize(fid); + break; + case S_IFREG: + fnode = cur->inode; + /* don't re-copy hard-links */ + if (!(fnode->flags & FI_WRITTEN)) { + printf("%s", dir); + error = udf_copy_file(&fnode->st, dir, cur, + fid, &icb); + if (!error) { + fnode->flags |= FI_WRITTEN; + udf_create_fid(ddoff, fid, cur->name, + 0, &icb); + ddoff += udf_fidsize(fid); + } else { + retval = 2; + } + } else { + /* hardlink! */ + printf("%s (hardlink)\n", dir); + udf_create_fid(ddoff, fid, cur->name, + 0, (struct long_ad *) (fnode->fsuse)); + ddoff += udf_fidsize(fid); + } + fnode->nlink--; + if (fnode->nlink == 0) + free(fnode->fsuse); + break; + case S_IFLNK: + /* softlink */ + fnode = cur->inode; + printf("%s -> %s\n", dir, cur->symlink); + error = udf_encode_symlink(&softlink_buf, + &softlink_len, cur->symlink); + if (error) { + printf("SOFTLINK error %d\n", error); + retval = 2; + break; + } + + udf_metadata_alloc(1, &icb); + udf_create_new_file(&fnode->st, &dscr, + UDF_ICB_FILETYPE_SYMLINK, &icb); + + nblk = udf_datablocks(softlink_len); + if (nblk > 0) + udf_data_alloc(nblk, &data_icb); + udf_append_file_contents(dscr, &data_icb, + softlink_buf, softlink_len); + + /* write out dscr (e)fe */ + udf_inc_link(dscr); + udf_write_dscr_virt(dscr, udf_rw32(icb.loc.lb_num), + udf_rw16(icb.loc.part_num), 1); + + free(dscr); + free(softlink_buf); + + udf_create_fid(ddoff, fid, cur->name, 0, &icb); + ddoff += udf_fidsize(fid); + break; + } + mydir[0] = '\0'; + } + + /* writeout directory contents */ + dirlen = ddoff; /* XXX might bite back */ + + udf_prepare_fids(dir_icb, &dirdata_icb, dirdata, dirlen); + udf_append_file_contents(dir_dscr, &dirdata_icb, dirdata, dirlen); + + /* write out dir_dscr (e)fe */ + udf_write_dscr_virt(dir_dscr, udf_rw32(dir_icb->loc.lb_num), + udf_rw16(dir_icb->loc.part_num), 1); + + free(dirdata); + free(dir_dscr); + return retval; +} + + +static int +udf_populate(const char *dir, fsnode *root, fsinfo_t *fsopts, + struct udf_stats *stats) +{ + struct long_ad rooticb; + static char path[MAXPATHLEN+1]; + int error; + + /* make sure the root gets the rootdir entry */ + context.metadata_alloc_pos = layout.rootdir; + context.data_alloc_pos = layout.rootdir; + + strncpy(path, dir, sizeof(path)); + error = udf_populate_walk(fsopts, root, path, &rooticb, &rooticb); + + return error; +} + + +static void +udf_enumerate_and_estimate(const char *dir, fsnode *root, fsinfo_t *fsopts, + struct udf_stats *stats) +{ + char path[MAXPATHLEN + 1]; + off_t proposed_size; + uint32_t n, nblk; + + strncpy(path, dir, sizeof(path)); + + /* calculate strict minimal size */ + udf_estimate_walk(fsopts, root, path, stats); + printf("ndirs %d\n", stats->ndirs); + printf("nfiles %d\n", stats->nfiles); + printf("ndata_blocks %d\n", stats->ndatablocks); + printf("nmetadata_blocks %d\n", stats->nmetadatablocks); + printf("\n"); + + /* adjust for options : free file nodes */ + if (fsopts->freefiles) { + /* be mercifull and reserve more for the FID */ + stats->nmetadatablocks += fsopts->freefiles * 1.5; + } else if ((n = fsopts->freefilepc)) { + stats->nmetadatablocks += (stats->nmetadatablocks*n) / (100-n); + } + + /* adjust for options : free data blocks */ + if (fsopts->freeblocks) { + stats->ndatablocks += fsopts->freeblocks; + } else if ((n = fsopts->freeblockpc)) { + stats->ndatablocks += (stats->ndatablocks * n) / (100-n); + } + + /* rough predictor of minimum disc size */ + nblk = stats->ndatablocks + stats->nmetadatablocks; + nblk = (double) nblk * (1.0 + 1.0/8.0); /* free space map */ + nblk += 256; /* pre-volume space */ + nblk += 256; /* post-volume space */ + nblk += 64; /* safeguard */ + + /* try to honour minimum size */ + n = fsopts->minsize / fsopts->sectorsize; + if (nblk < n) { + stats->ndatablocks += (n - nblk); + nblk += n - nblk; + } + proposed_size = (off_t) nblk * fsopts->sectorsize; + /* sanity size */ + if (proposed_size < 512*1024) + proposed_size = 512*1024; + + if (fsopts->size) { + if (fsopts->size < proposed_size) + err(EINVAL, "makefs_udf: won't fit on disc!"); + } else { + fsopts->size = proposed_size; + } + + fsopts->inodes = stats->nfiles + stats->ndirs; +} + + +void +udf_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts) +{ + struct udf_stats stats; + uint64_t truncate_len; + char scrap[255]; + int error; + + /* determine format */ + udf_emulate_discinfo(fsopts, &mmc_discinfo, mmc_profile); + printf("req_enable %d, req_disable %d\n", req_enable, req_disable); + + context.sector_size = fsopts->sectorsize; + error = udf_derive_format(req_enable, req_disable, false); + if (error) + err(EINVAL, "makefs_udf: can't determine format"); + + /* names */ + error = udf_proces_names(); + if (error) + err(EINVAL, "makefs_udf: bad names given"); + + /* set return value to 1 indicating error */ + error = 1; + + /* estimate the amount of space needed */ + memset(&stats, 0, sizeof(stats)); + udf_enumerate_and_estimate(dir, root, fsopts, &stats); + + printf("Calculated size of `%s': %lld bytes, %ld inodes\n", + image, (long long)fsopts->size, (long)fsopts->inodes); + + /* create file image */ + if ((fd = open(image, O_RDWR | O_CREAT | O_TRUNC, 0666)) == -1) { + err(EXIT_FAILURE, "%s", image); + } + if (lseek(fd, fsopts->size - 1, SEEK_SET) == -1) { + goto err_exit; + } + if (write(fd, &fd, 1) != 1) { + goto err_exit; + } + if (lseek(fd, 0, SEEK_SET) == -1) { + goto err_exit; + } + fsopts->fd = fd; + + /* calculate metadata percentage */ + meta_fract = fsopts->size / (stats.nmetadatablocks*fsopts->sectorsize); + meta_fract = ((int) ((meta_fract + 0.005)*100.0)) / 100; + + /* update mmc info but now with correct size */ + udf_emulate_discinfo(fsopts, &mmc_discinfo, mmc_profile); + + printf("Building disc compatible with UDF version %x to %x\n\n", + context.min_udf, context.max_udf); + (void)snprintb(scrap, sizeof(scrap), FORMAT_FLAGBITS, + (uint64_t) format_flags); + printf("UDF properties %s\n", scrap); + printf("Volume set `%s'\n", context.volset_name); + printf("Primary volume `%s`\n", context.primary_name); + printf("Logical volume `%s`\n", context.logvol_name); + if (format_flags & FORMAT_META) + printf("Metadata percentage %d %%\n", + (int) (100.0*stats.ndatablocks/stats.nmetadatablocks)); + printf("\n"); + udf_do_newfs_prefix(); + + /* update context */ + context.unique_id = 0; + + /* XXX are the next two needed? or should be re-count them? */ + context.num_files = stats.nfiles; + context.num_directories = stats.ndirs; + + error = udf_populate(dir, root, fsopts, &stats); + + udf_do_newfs_postfix(); + + if (format_flags & FORMAT_VAT) { + truncate_len = context.vtop_offset[context.data_part] + + context.data_alloc_pos; + truncate_len *= context.sector_size; + + printf("\nTruncing the disc-image to allow for VAT\n"); + printf("Free space left on this volume approx. " + "%"PRIu64" KiB, %"PRIu64" MiB\n", + (fsopts->size - truncate_len)/1024, + (fsopts->size - truncate_len)/1024/1024); + ftruncate(fd, truncate_len); + } + + if (error) { + error = 2; /* some files couldn't be added */ + goto err_exit; + } + + close(fd); + return; + +err_exit: + close(fd); + if (error == 2) { + errx(error, "Not all files could be added"); + } else { + errx(error, "creation of %s failed", image); + } +} + diff --git a/usr.sbin/makefs/udf/Makefile.inc b/usr.sbin/makefs/udf/Makefile.inc new file mode 100644 index 000000000..8d47e9ba7 --- /dev/null +++ b/usr.sbin/makefs/udf/Makefile.inc @@ -0,0 +1,13 @@ +# $NetBSD: Makefile.inc,v 1.2 2013/08/05 18:45:00 reinoud Exp $ +# + +UDF= ${NETBSDSRCDIR}/sys/fs/udf +UDF_NEWFS= ${NETBSDSRCDIR}/sbin/newfs_udf +FSCK= ${NETBSDSRCDIR}/sbin/fsck # use progress meter. + +.PATH: ${.CURDIR}/udf ${UDF} ${UDF_NEWFS} ${FSCK} + +CPPFLAGS+= -I${UDF} -I${UDF_NEWFS} -I${FSCK} + +SRCS += udf_create.c udf_write.c udf_osta.c + diff --git a/usr.sbin/makefs/udf/cdio_mmc_structs.h b/usr.sbin/makefs/udf/cdio_mmc_structs.h new file mode 100644 index 000000000..8e5992de7 --- /dev/null +++ b/usr.sbin/makefs/udf/cdio_mmc_structs.h @@ -0,0 +1,163 @@ +/* $NetBSD: cdio_mmc_structs.h,v 1.1 2013/08/05 18:44:16 reinoud Exp $ */ + +/* + * Copyright (c) 2006, 2008, 2013 Reinoud Zandijk + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _CDIO_MMC_EMU_H_ +#define _CDIO_MMC_EMU_H_ + +#include + +/* + * MMC device abstraction interface. + * + * It gathers information from GET_CONFIGURATION, READ_DISCINFO, + * READ_TRACKINFO, READ_TOC2, READ_CD_CAPACITY and GET_CONFIGURATION + * SCSI/ATAPI calls regardless if its a legacy CD-ROM/DVD-ROM device or a MMC + * standard recordable device. + */ +struct mmc_discinfo { + uint16_t mmc_profile; + uint16_t mmc_class; + + uint8_t disc_state; + uint8_t last_session_state; + uint8_t bg_format_state; + uint8_t link_block_penalty; /* in sectors */ + + uint64_t mmc_cur; /* current MMC_CAPs */ + uint64_t mmc_cap; /* possible MMC_CAPs */ + + uint32_t disc_flags; /* misc flags */ + + uint32_t disc_id; + uint64_t disc_barcode; + uint8_t application_code; /* 8 bit really */ + + uint8_t unused1[3]; /* padding */ + + uint32_t last_possible_lba; /* last leadout start adr. */ + uint32_t sector_size; + + uint16_t num_sessions; + uint16_t num_tracks; /* derived */ + + uint16_t first_track; + uint16_t first_track_last_session; + uint16_t last_track_last_session; + + uint16_t unused2; /* padding/misc info resv. */ + + uint16_t reserved1[4]; /* MMC-5 track resources */ + uint32_t reserved2[3]; /* MMC-5 POW resources */ + + uint32_t reserved3[8]; /* MMC-5+ */ +}; +#define MMCGETDISCINFO _IOR('c', 28, struct mmc_discinfo) + +#define MMC_CLASS_UNKN 0 +#define MMC_CLASS_DISC 1 +#define MMC_CLASS_CD 2 +#define MMC_CLASS_DVD 3 +#define MMC_CLASS_MO 4 +#define MMC_CLASS_BD 5 +#define MMC_CLASS_FILE 0xffff /* emulation mode */ + +#define MMC_DFLAGS_BARCODEVALID (1 << 0) /* barcode is present and valid */ +#define MMC_DFLAGS_DISCIDVALID (1 << 1) /* discid is present and valid */ +#define MMC_DFLAGS_APPCODEVALID (1 << 2) /* application code valid */ +#define MMC_DFLAGS_UNRESTRICTED (1 << 3) /* restricted, then set app. code */ + +#define MMC_DFLAGS_FLAGBITS \ + "\10\1BARCODEVALID\2DISCIDVALID\3APPCODEVALID\4UNRESTRICTED" + +#define MMC_CAP_SEQUENTIAL (1 << 0) /* sequential writable only */ +#define MMC_CAP_RECORDABLE (1 << 1) /* record-able; i.e. not static */ +#define MMC_CAP_ERASABLE (1 << 2) /* drive can erase sectors */ +#define MMC_CAP_BLANKABLE (1 << 3) /* media can be blanked */ +#define MMC_CAP_FORMATTABLE (1 << 4) /* media can be formatted */ +#define MMC_CAP_REWRITABLE (1 << 5) /* media can be rewritten */ +#define MMC_CAP_MRW (1 << 6) /* Mount Rainier formatted */ +#define MMC_CAP_PACKET (1 << 7) /* using packet recording */ +#define MMC_CAP_STRICTOVERWRITE (1 << 8) /* only writes a packet at a time */ +#define MMC_CAP_PSEUDOOVERWRITE (1 << 9) /* overwrite through replacement */ +#define MMC_CAP_ZEROLINKBLK (1 << 10) /* zero link block length capable */ +#define MMC_CAP_HW_DEFECTFREE (1 << 11) /* hardware defect management */ + +#define MMC_CAP_FLAGBITS \ + "\10\1SEQUENTIAL\2RECORDABLE\3ERASABLE\4BLANKABLE\5FORMATTABLE" \ + "\6REWRITABLE\7MRW\10PACKET\11STRICTOVERWRITE\12PSEUDOOVERWRITE" \ + "\13ZEROLINKBLK\14HW_DEFECTFREE" + +#define MMC_STATE_EMPTY 0 +#define MMC_STATE_INCOMPLETE 1 +#define MMC_STATE_FULL 2 +#define MMC_STATE_CLOSED 3 + +#define MMC_BGFSTATE_UNFORM 0 +#define MMC_BGFSTATE_STOPPED 1 +#define MMC_BGFSTATE_RUNNING 2 +#define MMC_BGFSTATE_COMPLETED 3 + + +struct mmc_trackinfo { + uint16_t tracknr; /* IN/OUT */ + uint16_t sessionnr; + + uint8_t track_mode; + uint8_t data_mode; + + uint16_t flags; + + uint32_t track_start; + uint32_t next_writable; + uint32_t free_blocks; + uint32_t packet_size; + uint32_t track_size; + uint32_t last_recorded; +}; +#define MMCGETTRACKINFO _IOWR('c', 29, struct mmc_trackinfo) + +#define MMC_TRACKINFO_COPY (1 << 0) +#define MMC_TRACKINFO_DAMAGED (1 << 1) +#define MMC_TRACKINFO_FIXED_PACKET (1 << 2) +#define MMC_TRACKINFO_INCREMENTAL (1 << 3) +#define MMC_TRACKINFO_BLANK (1 << 4) +#define MMC_TRACKINFO_RESERVED (1 << 5) +#define MMC_TRACKINFO_NWA_VALID (1 << 6) +#define MMC_TRACKINFO_LRA_VALID (1 << 7) +#define MMC_TRACKINFO_DATA (1 << 8) +#define MMC_TRACKINFO_AUDIO (1 << 9) +#define MMC_TRACKINFO_AUDIO_4CHAN (1 << 10) +#define MMC_TRACKINFO_PRE_EMPH (1 << 11) + +#define MMC_TRACKINFO_FLAGBITS \ + "\10\1COPY\2DAMAGED\3FIXEDPACKET\4INCREMENTAL\5BLANK" \ + "\6RESERVED\7NWA_VALID\10LRA_VALID\11DATA\12AUDIO" \ + "\13AUDIO_4CHAN\14PRE_EMPH" + +#endif /* _CDIO_MMC_EMU_H_ */ + diff --git a/usr.sbin/makefs/v7fs.c b/usr.sbin/makefs/v7fs.c new file mode 100644 index 000000000..7c479a35e --- /dev/null +++ b/usr.sbin/makefs/v7fs.c @@ -0,0 +1,191 @@ +/* $NetBSD: v7fs.c,v 1.8 2013/01/29 15:52:25 christos Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: v7fs.c,v 1.8 2013/01/29 15:52:25 christos Exp $"); +#endif /* !__lint */ + +#include +#include +#include +#include +#include +#include + +#include "makefs.h" +#include "v7fs.h" +#include "v7fs_impl.h" +#include "v7fs_makefs.h" +#include "newfs_v7fs.h" + + +#ifndef HAVE_NBTOOL_CONFIG_H +#include "progress.h" +static bool progress_bar_enable; +#endif +int v7fs_newfs_verbose; + +void +v7fs_prep_opts(fsinfo_t *fsopts) +{ + v7fs_opt_t *v7fs_opts = ecalloc(1, sizeof(*v7fs_opts)); + const option_t v7fs_options[] = { + { 'p', "pdp", &v7fs_opts->pdp_endian, OPT_INT32, false, true, + "PDP endian" }, + { 'P', "progress", &v7fs_opts->progress, OPT_INT32, false, true, + "Progress bar" }, + { .name = NULL } + }; + + fsopts->fs_specific = v7fs_opts; + fsopts->fs_options = copy_opts(v7fs_options); +} + +void +v7fs_cleanup_opts(fsinfo_t *fsopts) +{ + free(fsopts->fs_specific); + free(fsopts->fs_options); +} + +int +v7fs_parse_opts(const char *option, fsinfo_t *fsopts) +{ + + return set_option_var(fsopts->fs_options, option, "1", NULL, 0) != -1; +} + +void +v7fs_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts) +{ + struct v7fs_mount_device v7fs_mount; + int fd, endian, error = 1; + v7fs_opt_t *v7fs_opts = fsopts->fs_specific; + + v7fs_newfs_verbose = debug; +#ifndef HAVE_NBTOOL_CONFIG_H + if ((progress_bar_enable = v7fs_opts->progress)) { + progress_switch(progress_bar_enable); + progress_init(); + progress(&(struct progress_arg){ .cdev = image }); + } +#endif + + /* Determine filesystem image size */ + v7fs_estimate(dir, root, fsopts); + printf("Calculated size of `%s': %lld bytes, %ld inodes\n", + image, (long long)fsopts->size, (long)fsopts->inodes); + + if ((fd = open(image, O_RDWR | O_CREAT | O_TRUNC, 0666)) == -1) { + err(EXIT_FAILURE, "%s", image); + } + if (lseek(fd, fsopts->size - 1, SEEK_SET) == -1) { + goto err_exit; + } + if (write(fd, &fd, 1) != 1) { + goto err_exit; + } + if (lseek(fd, 0, SEEK_SET) == -1) { + goto err_exit; + } + fsopts->fd = fd; + v7fs_mount.device.fd = fd; + +#if !defined BYTE_ORDER +#error +#endif +#if BYTE_ORDER == LITTLE_ENDIAN + if (fsopts->needswap) + endian = BIG_ENDIAN; + else + endian = LITTLE_ENDIAN; +#else + if (fsopts->needswap) + endian = LITTLE_ENDIAN; + else + endian = BIG_ENDIAN; +#endif + if (v7fs_opts->pdp_endian) { + endian = PDP_ENDIAN; + } + + v7fs_mount.endian = endian; + v7fs_mount.sectors = fsopts->size >> V7FS_BSHIFT; + if (v7fs_newfs(&v7fs_mount, fsopts->inodes) != 0) { + goto err_exit; + } + + if (v7fs_populate(dir, root, fsopts, &v7fs_mount) != 0) { + error = 2; /* some files couldn't add */ + goto err_exit; + } + + close(fd); + return; + + err_exit: + close(fd); + err(error, "%s", image); +} + +void +progress(const struct progress_arg *p) +{ +#ifndef HAVE_NBTOOL_CONFIG_H + static struct progress_arg Progress; + static char cdev[32]; + static char label[32]; + + if (!progress_bar_enable) + return; + + if (p) { + Progress = *p; + if (p->cdev) + strcpy(cdev, p->cdev); + if (p->label) + strcpy(label, p->label); + } + + if (!Progress.tick) + return; + if (++Progress.cnt > Progress.tick) { + Progress.cnt = 0; + Progress.total++; + progress_bar(cdev, label, Progress.total, PROGRESS_BAR_GRANULE); + } +#endif +} diff --git a/usr.sbin/makefs/v7fs/Makefile.inc b/usr.sbin/makefs/v7fs/Makefile.inc new file mode 100644 index 000000000..6c8163d1c --- /dev/null +++ b/usr.sbin/makefs/v7fs/Makefile.inc @@ -0,0 +1,20 @@ +# $NetBSD: Makefile.inc,v 1.5 2013/01/23 21:03:15 christos Exp $ +# + +V7FS= ${NETBSDSRCDIR}/sys/fs/v7fs +V7FS_NEWFS= ${NETBSDSRCDIR}/sbin/newfs_v7fs +FSCK= ${NETBSDSRCDIR}/sbin/fsck # use progress meter. + +.PATH: ${.CURDIR}/v7fs ${V7FS} ${V7FS_NEWFS} ${FSCK} + +CPPFLAGS+= -DV7FS_EI -I${V7FS} -I${V7FS_NEWFS} -I${FSCK} + +SRCS += v7fs_endian.c v7fs_superblock.c v7fs_superblock_util.c v7fs_inode.c \ +v7fs_inode_util.c v7fs_datablock.c v7fs_dirent.c v7fs_io.c v7fs_file.c \ +v7fs_file_util.c v7fs_io_user.c +SRCS += main.c # newfs +.if !defined(HOSTPROG) +SRCS += progress.c # progress bar (fsck) +.endif + +SRCS += v7fs_estimate.c v7fs_populate.c diff --git a/usr.sbin/makefs/v7fs/v7fs_estimate.c b/usr.sbin/makefs/v7fs/v7fs_estimate.c new file mode 100644 index 000000000..74b7e25a6 --- /dev/null +++ b/usr.sbin/makefs/v7fs/v7fs_estimate.c @@ -0,0 +1,275 @@ +/* $NetBSD: v7fs_estimate.c,v 1.3 2012/04/19 17:28:26 christos Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: v7fs_estimate.c,v 1.3 2012/04/19 17:28:26 christos Exp $"); +#endif /* !__lint */ + +#include +#include +#include +#include + +#if !HAVE_NBTOOL_CONFIG_H +#include /*MAXPATHLEN */ +#endif + +#include "makefs.h" +#include "v7fs.h" +#include "v7fs_impl.h" +#include "v7fs_inode.h" +#include "v7fs_datablock.h" +#include "v7fs_makefs.h" + +struct v7fs_geometry { + v7fs_daddr_t ndatablock; + v7fs_ino_t ninode; + v7fs_daddr_t npuredatablk; +}; + +#define VPRINTF(fmt, args...) { if (v7fs_newfs_verbose) printf(fmt, ##args); } + +static int +v7fs_datablock_size(off_t sz, v7fs_daddr_t *nblk) +{ + struct v7fs_daddr_map map; + int error = 0; + + if (sz == 0) { + *nblk = 0; + return 0; + } + + if ((error = v7fs_datablock_addr(sz, &map))) { + return error; + } + switch (map.level) { + case 0: /* Direct */ + *nblk = map.index[0] + 1; + break; + case 1: + *nblk = V7FS_NADDR_DIRECT + /*direct */ + 1/*addr[V7FS_NADDR_INDEX1]*/ + map.index[0] + 1; + break; + case 2: + *nblk = V7FS_NADDR_DIRECT + /*direct */ + 1/*addr[V7FS_NADDR_INDEX1]*/ + + V7FS_DADDR_PER_BLOCK +/*idx1 */ + 1/*addr[V7FS_NADDR_INDEX2]*/ + + map.index[0] + /* # of idx2 index block(filled) */ + map.index[0] * V7FS_DADDR_PER_BLOCK + /* of its datablocks*/ + 1 + /*current idx2 indexblock */ + map.index[1] + 1; + break; + case 3: + *nblk = V7FS_NADDR_DIRECT + /*direct */ + 1/*addr[V7FS_NADDR_INDEX1]*/ + + V7FS_DADDR_PER_BLOCK +/*idx1 */ + 1/*addr[V7FS_NADDR_INDEX2]*/ + + V7FS_DADDR_PER_BLOCK + /* # of idx2 index block */ + V7FS_DADDR_PER_BLOCK * V7FS_DADDR_PER_BLOCK + + /*idx2 datablk */ + 1/*addr[v7FS_NADDR_INDEX3*/ + + map.index[0] + /* # of lv1 index block(filled) */ + map.index[0] * V7FS_DADDR_PER_BLOCK * V7FS_DADDR_PER_BLOCK + + 1 + /*lv1 */ + map.index[1] + /* #of lv2 index block(filled) */ + map.index[1] * V7FS_DADDR_PER_BLOCK + /*lv2 datablock */ + 1 + /* current lv2 index block */ + map.index[2] + 1; /*filled datablock */ + break; + default: + *nblk = 0; + error = EINVAL; + break; + } + + return error; +} + +static int +estimate_size_walk(fsnode *root, char *dir, struct v7fs_geometry *geom) +{ + fsnode *cur; + int nentries; + size_t pathlen = strlen(dir); + char *mydir = dir + pathlen; + fsinode *fnode; + v7fs_daddr_t nblk; + int n; + off_t sz; + + for (cur = root, nentries = 0; cur != NULL; cur = cur->next, + nentries++, geom->ninode++) { + switch (cur->type & S_IFMT) { + default: + break; + case S_IFDIR: + if (!cur->child) + break; + mydir[0] = '/'; + strncpy(&mydir[1], cur->name, MAXPATHLEN - pathlen); + n = estimate_size_walk(cur->child, dir, geom); + sz = (n + 1/*..*/) * sizeof(struct v7fs_dirent); + v7fs_datablock_size(sz, &nblk); + mydir[0] = '\0'; + geom->ndatablock += nblk; + geom->npuredatablk += + V7FS_ROUND_BSIZE(sz) >> V7FS_BSHIFT; + break; + case S_IFREG: + fnode = cur->inode; + if (!(fnode->flags & FI_SIZED)) { /*Skip hard-link */ + sz = fnode->st.st_size; + v7fs_datablock_size(sz, &nblk); + geom->ndatablock += nblk; + geom->npuredatablk += + V7FS_ROUND_BSIZE(sz) >> V7FS_BSHIFT; + fnode->flags |= FI_SIZED; + } + + break; + case S_IFLNK: + nblk = V7FSBSD_MAXSYMLINKLEN >> V7FS_BSHIFT; + geom->ndatablock += nblk; + geom->npuredatablk += nblk; + break; + } + } + + return nentries; +} + +static v7fs_daddr_t +calculate_fs_size(const struct v7fs_geometry *geom) +{ + v7fs_daddr_t fs_blk, ilist_blk; + + ilist_blk = V7FS_ROUND_BSIZE(geom->ninode * + sizeof(struct v7fs_inode_diskimage)) >> V7FS_BSHIFT; + fs_blk = geom->ndatablock + ilist_blk + V7FS_ILIST_SECTOR; + + VPRINTF("datablock:%d ilistblock:%d total:%d\n", geom->ndatablock, + ilist_blk, fs_blk); + + return fs_blk; +} + +static void +determine_fs_size(fsinfo_t *fsopts, struct v7fs_geometry *geom) +{ + v7fs_daddr_t nblk = geom->ndatablock; + v7fs_daddr_t fsblk, n; + int32_t nfiles = geom->ninode; + + VPRINTF("size=%lld inodes=%lld fd=%d superb=%p onlyspec=%d\n", + (long long)fsopts->size, (long long)fsopts->inodes, fsopts->fd, + fsopts->superblock, fsopts->onlyspec); + VPRINTF("minsize=%lld maxsize=%lld freefiles=%lld freefilepc=%d " + "freeblocks=%lld freeblockpc=%d sectorseize=%d\n", + (long long)fsopts->minsize, (long long)fsopts->maxsize, + (long long)fsopts->freefiles, fsopts->freefilepc, + (long long)fsopts->freeblocks, fsopts->freeblockpc, + fsopts->sectorsize); + + if ((fsopts->sectorsize > 0) && (fsopts->sectorsize != V7FS_BSIZE)) + warnx("v7fs sector size is 512byte only. '-S %d' is ignored.", + fsopts->sectorsize); + + /* Free inode */ + if (fsopts->freefiles) { + nfiles += fsopts->freefiles; + } else if ((n = fsopts->freefilepc)) { + nfiles += (nfiles * n) / (100 - n); + } + if (nfiles >= V7FS_INODE_MAX) { + errx(EXIT_FAILURE, "# of files(%d) over v7fs limit(%d).", + nfiles, V7FS_INODE_MAX); + } + + /* Free datablock */ + if (fsopts->freeblocks) { + nblk += fsopts->freeblocks; + } else if ((n = fsopts->freeblockpc)) { + nblk += (nblk * n) / (100 - n); + } + + /* Total size */ + geom->ndatablock = nblk; + geom->ninode = nfiles; + fsblk = calculate_fs_size(geom); + + if (fsblk >= V7FS_DADDR_MAX) { + errx(EXIT_FAILURE, "filesystem size(%d) over v7fs limit(%d).", + fsblk, V7FS_DADDR_MAX); + } + + n = fsopts->minsize >> V7FS_BSHIFT; + if (fsblk < n) + geom->ndatablock += (n - fsblk); + + n = fsopts->maxsize >> V7FS_BSHIFT; + if (fsopts->maxsize > 0 && fsblk > n) { + errx(EXIT_FAILURE, "# of datablocks %d > %d", fsblk, n); + } +} + +void +v7fs_estimate(const char *dir, fsnode *root, fsinfo_t *fsopts) +{ + v7fs_opt_t *v7fs_opts = fsopts->fs_specific; + char path[MAXPATHLEN + 1]; + int ndir; + off_t sz; + v7fs_daddr_t nblk; + struct v7fs_geometry geom; + + memset(&geom , 0, sizeof(geom)); + strncpy(path, dir, sizeof(path)); + + /* Calculate strict size. */ + ndir = estimate_size_walk(root, path, &geom); + sz = (ndir + 1/*..*/) * sizeof(struct v7fs_dirent); + v7fs_datablock_size(sz, &nblk); + geom.ndatablock += nblk; + + /* Consider options. */ + determine_fs_size(fsopts, &geom); + + fsopts->size = calculate_fs_size(&geom) << V7FS_BSHIFT; + fsopts->inodes = geom.ninode; + v7fs_opts->npuredatablk = geom.npuredatablk; /* for progress bar */ +} diff --git a/usr.sbin/makefs/v7fs/v7fs_populate.c b/usr.sbin/makefs/v7fs/v7fs_populate.c new file mode 100644 index 000000000..5b6215858 --- /dev/null +++ b/usr.sbin/makefs/v7fs/v7fs_populate.c @@ -0,0 +1,298 @@ +/* $NetBSD: v7fs_populate.c,v 1.3 2011/08/10 11:31:49 uch Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: v7fs_populate.c,v 1.3 2011/08/10 11:31:49 uch Exp $"); +#endif /* !__lint */ + +#include +#include +#include +#include +#include +#include +#include + +#if !HAVE_NBTOOL_CONFIG_H +#include +#endif + +#include "makefs.h" +#include "v7fs.h" +#include "v7fs_impl.h" +#include "v7fs_inode.h" +#include "v7fs_superblock.h" +#include "v7fs_datablock.h" +#include "v7fs_endian.h" +#include "v7fs_file.h" +#include "v7fs_makefs.h" +#include "newfs_v7fs.h" + +#define VPRINTF(fmt, args...) { if (v7fs_newfs_verbose) printf(fmt, ##args); } + +static void +attr_setup(fsnode *node, struct v7fs_fileattr *attr) +{ + struct stat *st = &node->inode->st; + + attr->mode = node->type | st->st_mode; + attr->uid = st->st_uid; + attr->gid = st->st_gid; + attr->device = 0; + attr->ctime = st->st_ctime; + attr->atime = st->st_atime; + attr->mtime = st->st_mtime; +} + +static int +allocate(struct v7fs_self *fs, struct v7fs_inode *parent_inode, fsnode *node, + dev_t dev, struct v7fs_inode *inode) +{ + int error; + v7fs_ino_t ino; + struct v7fs_fileattr attr; + + memset(inode, 0, sizeof(*inode)); + + attr_setup(node, &attr); + attr.device = dev; + if ((error = v7fs_file_allocate(fs, parent_inode, node->name, &attr, + &ino))) { + errno = error; + warn("%s", node->name); + return error; + } + node->inode->ino = ino; + node->inode->flags |= FI_ALLOCATED; + if ((error = v7fs_inode_load(fs, inode, ino))) { + errno = error; + warn("%s", node->name); + return error; + } + + return 0; +} + +struct copy_arg { + int fd; + uint8_t buf[V7FS_BSIZE]; +}; + +static int +copy_subr(struct v7fs_self *fs, void *ctx, v7fs_daddr_t blk, size_t sz) +{ + struct copy_arg *p = ctx; + + if (read(p->fd, p->buf, sz) != (ssize_t)sz) { + return V7FS_ITERATOR_ERROR; + } + + if (!fs->io.write(fs->io.cookie, p->buf, blk)) { + errno = EIO; + return V7FS_ITERATOR_ERROR; + } + progress(0); + + return 0; +} + +static int +file_copy(struct v7fs_self *fs, struct v7fs_inode *parent_inode, fsnode *node, + const char *filepath) +{ + struct v7fs_inode inode; + const char *errmsg; + fsinode *fnode = node->inode; + int error = 0; + int fd; + + /* Check hard-link */ + if ((fnode->nlink > 1) && (fnode->flags & FI_ALLOCATED)) { + if ((error = v7fs_inode_load(fs, &inode, fnode->ino))) { + errmsg = "inode load"; + goto err_exit; + } + if ((error = v7fs_file_link(fs, parent_inode, &inode, + node->name))) { + errmsg = "hard link"; + goto err_exit; + } + return 0; + } + + /* Allocate file */ + if ((error = allocate(fs, parent_inode, node, 0, &inode))) { + errmsg = "file allocate"; + goto err_exit; + } + if ((error = v7fs_datablock_expand(fs, &inode, fnode->st.st_size))) { + errmsg = "datablock expand"; + goto err_exit; + } + + /* Data copy */ + if ((fd = open(filepath, O_RDONLY)) == -1) { + error = errno; + errmsg = "source file"; + goto err_exit; + } + + error = v7fs_datablock_foreach(fs, &inode, copy_subr, + &(struct copy_arg){ .fd = fd }); + if (error != V7FS_ITERATOR_END) { + errmsg = "data copy"; + close(fd); + goto err_exit; + } else { + error = 0; + } + close(fd); + + return error; + +err_exit: + errno = error; + warn("%s %s", node->name, errmsg); + + return error; +} + +static int +populate_walk(struct v7fs_self *fs, struct v7fs_inode *parent_inode, + fsnode *root, char *dir) +{ + fsnode *cur; + char *mydir = dir + strlen(dir); + char srcpath[MAXPATHLEN + 1]; + struct v7fs_inode inode; + int error = 0; + bool failed = false; + + for (cur = root; cur != NULL; cur = cur->next) { + switch (cur->type & S_IFMT) { + default: + VPRINTF("%x\n", cur->flags & S_IFMT); + break; + case S_IFCHR: + /*FALLTHROUGH*/ + case S_IFBLK: + if ((error = allocate(fs, parent_inode, cur, + cur->inode->st.st_rdev, &inode))) { + errno = error; + warn("%s", cur->name); + } + break; + case S_IFDIR: + if (!cur->child) /*'.'*/ + break; + /* Allocate this directory. */ + if ((error = allocate(fs, parent_inode, cur, 0, + &inode))) { + errno = error; + warn("%s", cur->name); + } else { + /* Populate children. */ + mydir[0] = '/'; + strcpy(&mydir[1], cur->name); + error = populate_walk(fs, &inode, cur->child, + dir); + mydir[0] = '\0'; + } + break; + case S_IFREG: + snprintf(srcpath, sizeof(srcpath), "%s/%s", dir, + cur->name); + error = file_copy(fs, parent_inode, cur, srcpath); + break; + case S_IFLNK: + if ((error = allocate(fs, parent_inode, cur, 0, + &inode))) { + errno = error; + warn("%s", cur->name); + } else { + v7fs_file_symlink(fs, &inode, cur->symlink); + } + break; + } + if (error) + failed = true; + } + + return failed ? 2 : 0; +} + +int +v7fs_populate(const char *dir, fsnode *root, fsinfo_t *fsopts, + const struct v7fs_mount_device *device) +{ + v7fs_opt_t *v7fs_opts = fsopts->fs_specific; + static char path[MAXPATHLEN + 1]; + struct v7fs_inode root_inode; + struct v7fs_self *fs; + int error; + + if ((error = v7fs_io_init(&fs, device, V7FS_BSIZE))) { + errno = error; + warn("I/O setup failed."); + return error; + } + fs->endian = device->endian; + v7fs_endian_init(fs); + + if ((error = v7fs_superblock_load(fs))) { + errno = error; + warn("Can't load superblock."); + return error; + } + fsopts->superblock = &fs->superblock; /* not used. */ + + if ((error = v7fs_inode_load(fs, &root_inode, V7FS_ROOT_INODE))) { + errno = error; + warn("Can't load root inode."); + return error; + } + + progress(&(struct progress_arg){ .label = "populate", .tick = + v7fs_opts->npuredatablk / PROGRESS_BAR_GRANULE }); + + strncpy(path, dir, sizeof(path)); + error = populate_walk(fs, &root_inode, root, path); + + v7fs_inode_writeback(fs, &root_inode); + v7fs_superblock_writeback(fs); + + return error; +} diff --git a/usr.sbin/makefs/v7fs_makefs.h b/usr.sbin/makefs/v7fs_makefs.h new file mode 100644 index 000000000..1c047caa4 --- /dev/null +++ b/usr.sbin/makefs/v7fs_makefs.h @@ -0,0 +1,49 @@ +/* $NetBSD: v7fs_makefs.h,v 1.2 2011/08/10 11:31:49 uch Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by UCHIYAMA Yasushi. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _MAKEFS_V7FS_H_ +#define _MAKEFS_V7FS_H_ + +typedef struct { + int pdp_endian; + int progress; + v7fs_daddr_t npuredatablk; /* for progress bar */ +} v7fs_opt_t; + +__BEGIN_DECLS +void v7fs_estimate(const char *, fsnode *, fsinfo_t *); +int v7fs_populate(const char *, fsnode *, fsinfo_t *, + const struct v7fs_mount_device *); +struct progress_arg; +void progress(const struct progress_arg *); +extern int v7fs_newfs_verbose; +__END_DECLS +#endif /*!_MAKEFS_V7FS_H_*/ diff --git a/usr.sbin/makefs/walk.c b/usr.sbin/makefs/walk.c new file mode 100644 index 000000000..5e585ac83 --- /dev/null +++ b/usr.sbin/makefs/walk.c @@ -0,0 +1,691 @@ +/* $NetBSD: walk.c,v 1.28 2013/02/03 06:16:53 christos Exp $ */ + +/* + * Copyright (c) 2001 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Luke Mewburn for Wasabi Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: walk.c,v 1.28 2013/02/03 06:16:53 christos Exp $"); +#endif /* !__lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "makefs.h" +#include "mtree.h" + +static void apply_specdir(const char *, NODE *, fsnode *, int); +static void apply_specentry(const char *, NODE *, fsnode *); +static fsnode *create_fsnode(const char *, const char *, const char *, + struct stat *); +static fsinode *link_check(fsinode *); + + +/* + * walk_dir -- + * build a tree of fsnodes from `root' and `dir', with a parent + * fsnode of `parent' (which may be NULL for the root of the tree). + * append the tree to a fsnode of `join' if it is not NULL. + * each "level" is a directory, with the "." entry guaranteed to be + * at the start of the list, and without ".." entries. + */ +fsnode * +walk_dir(const char *root, const char *dir, fsnode *parent, fsnode *join, + int replace) +{ + fsnode *first, *cur, *prev, *last; + DIR *dirp; + struct dirent *dent; + char path[MAXPATHLEN + 1]; + struct stat stbuf; + char *name, *rp; + int dot, len; + + assert(root != NULL); + assert(dir != NULL); + + len = snprintf(path, sizeof(path), "%s/%s", root, dir); + if (len >= (int)sizeof(path)) + errx(1, "Pathname too long."); + if (debug & DEBUG_WALK_DIR) + printf("walk_dir: %s %p\n", path, parent); + if ((dirp = opendir(path)) == NULL) + err(1, "Can't opendir `%s'", path); + rp = path + strlen(root) + 1; + if (join != NULL) { + first = cur = join; + while (cur->next != NULL) + cur = cur->next; + prev = last = cur; + } else + last = first = prev = NULL; + while ((dent = readdir(dirp)) != NULL) { + name = dent->d_name; + dot = 0; + if (name[0] == '.') + switch (name[1]) { + case '\0': /* "." */ + if (join != NULL) + continue; + dot = 1; + break; + case '.': /* ".." */ + if (name[2] == '\0') + continue; + /* FALLTHROUGH */ + default: + dot = 0; + } + if (debug & DEBUG_WALK_DIR_NODE) + printf("scanning %s/%s/%s\n", root, dir, name); + if (snprintf(path + len, sizeof(path) - len, "/%s", name) >= + (int)sizeof(path) - len) + errx(1, "Pathname too long."); + if (lstat(path, &stbuf) == -1) + err(1, "Can't lstat `%s'", path); +#ifdef S_ISSOCK + if (S_ISSOCK(stbuf.st_mode & S_IFMT)) { + if (debug & DEBUG_WALK_DIR_NODE) + printf(" skipping socket %s\n", path); + continue; + } +#endif + + if (join != NULL) { + cur = join->next; + for (;;) { + if (cur == NULL || strcmp(cur->name, name) == 0) + break; + if (cur == last) { + cur = NULL; + break; + } + cur = cur->next; + } + if (cur != NULL) { + if (S_ISDIR(cur->type) && + S_ISDIR(stbuf.st_mode)) { + if (debug & DEBUG_WALK_DIR_NODE) + printf("merging %s with %p\n", + path, cur->child); + cur->child = walk_dir(root, rp, cur, + cur->child, replace); + continue; + } + if (!replace) + errx(1, "Can't merge %s `%s' with " + "existing %s", + inode_type(stbuf.st_mode), path, + inode_type(cur->type)); + else { + if (debug & DEBUG_WALK_DIR_NODE) + printf("replacing %s %s\n", + inode_type(stbuf.st_mode), + path); + if (cur == join->next) + join->next = cur->next; + else { + fsnode *p; + for (p = join->next; + p->next != cur; p = p->next) + continue; + p->next = cur->next; + } + free(cur); + } + } + } + + cur = create_fsnode(root, dir, name, &stbuf); + cur->parent = parent; + if (dot) { + /* ensure "." is at the start of the list */ + cur->next = first; + first = cur; + if (! prev) + prev = cur; + cur->first = first; + } else { /* not "." */ + if (prev) + prev->next = cur; + prev = cur; + if (!first) + first = cur; + cur->first = first; + if (S_ISDIR(cur->type)) { + cur->child = walk_dir(root, rp, cur, NULL, + replace); + continue; + } + } + if (stbuf.st_nlink > 1) { + fsinode *curino; + + curino = link_check(cur->inode); + if (curino != NULL) { + free(cur->inode); + cur->inode = curino; + cur->inode->nlink++; + if (debug & DEBUG_WALK_DIR_LINKCHECK) + printf("link_check: found [%llu, %llu]\n", + (unsigned long long)curino->st.st_dev, + (unsigned long long)curino->st.st_ino); + } + } + if (S_ISLNK(cur->type)) { + char slink[PATH_MAX+1]; + int llen; + + llen = readlink(path, slink, sizeof(slink) - 1); + if (llen == -1) + err(1, "Readlink `%s'", path); + slink[llen] = '\0'; + cur->symlink = estrdup(slink); + } + } + assert(first != NULL); + if (join == NULL) + for (cur = first->next; cur != NULL; cur = cur->next) + cur->first = first; + if (closedir(dirp) == -1) + err(1, "Can't closedir `%s/%s'", root, dir); + return (first); +} + +static fsnode * +create_fsnode(const char *root, const char *path, const char *name, + struct stat *stbuf) +{ + fsnode *cur; + + cur = ecalloc(1, sizeof(*cur)); + cur->path = estrdup(path); + cur->name = estrdup(name); + cur->inode = ecalloc(1, sizeof(*cur->inode)); + cur->root = root; + cur->type = stbuf->st_mode & S_IFMT; + cur->inode->nlink = 1; + cur->inode->st = *stbuf; + return (cur); +} + +/* + * free_fsnodes -- + * Removes node from tree and frees it and all of + * its decendents. + */ +void +free_fsnodes(fsnode *node) +{ + fsnode *cur, *next; + + assert(node != NULL); + + /* for ".", start with actual parent node */ + if (node->first == node) { + assert(node->name[0] == '.' && node->name[1] == '\0'); + if (node->parent) { + assert(node->parent->child == node); + node = node->parent; + } + } + + /* Find ourselves in our sibling list and unlink */ + if (node->first != node) { + for (cur = node->first; cur->next; cur = cur->next) { + if (cur->next == node) { + cur->next = node->next; + node->next = NULL; + break; + } + } + } + + for (cur = node; cur != NULL; cur = next) { + next = cur->next; + if (cur->child) { + cur->child->parent = NULL; + free_fsnodes(cur->child); + } + if (cur->inode->nlink-- == 1) + free(cur->inode); + if (cur->symlink) + free(cur->symlink); + free(cur->path); + free(cur->name); + free(cur); + } +} + +/* + * apply_specfile -- + * read in the mtree(8) specfile, and apply it to the tree + * at dir,parent. parameters in parent on equivalent types + * will be changed to those found in specfile, and missing + * entries will be added. + */ +void +apply_specfile(const char *specfile, const char *dir, fsnode *parent, int speconly) +{ + struct timeval start; + FILE *fp; + NODE *root; + + assert(specfile != NULL); + assert(parent != NULL); + + if (debug & DEBUG_APPLY_SPECFILE) + printf("apply_specfile: %s, %s %p\n", specfile, dir, parent); + + /* read in the specfile */ + if ((fp = fopen(specfile, "r")) == NULL) + err(1, "Can't open `%s'", specfile); + TIMER_START(start); + root = spec(fp); + TIMER_RESULTS(start, "spec"); + if (fclose(fp) == EOF) + err(1, "Can't close `%s'", specfile); + + /* perform some sanity checks */ + if (root == NULL) + errx(1, "Specfile `%s' did not contain a tree", specfile); + assert(strcmp(root->name, ".") == 0); + assert(root->type == F_DIR); + + /* merge in the changes */ + apply_specdir(dir, root, parent, speconly); + + free_nodes(root); +} + +static void +apply_specdir(const char *dir, NODE *specnode, fsnode *dirnode, int speconly) +{ + char path[MAXPATHLEN + 1]; + NODE *curnode; + fsnode *curfsnode; + + assert(specnode != NULL); + assert(dirnode != NULL); + + if (debug & DEBUG_APPLY_SPECFILE) + printf("apply_specdir: %s %p %p\n", dir, specnode, dirnode); + + if (specnode->type != F_DIR) + errx(1, "Specfile node `%s/%s' is not a directory", + dir, specnode->name); + if (dirnode->type != S_IFDIR) + errx(1, "Directory node `%s/%s' is not a directory", + dir, dirnode->name); + + apply_specentry(dir, specnode, dirnode); + + /* Remove any filesystem nodes not found in specfile */ + /* XXX inefficient. This is O^2 in each dir and it would + * have been better never to have walked this part of the tree + * to begin with + */ + if (speconly) { + fsnode *next; + assert(dirnode->name[0] == '.' && dirnode->name[1] == '\0'); + for (curfsnode = dirnode->next; curfsnode != NULL; curfsnode = next) { + next = curfsnode->next; + for (curnode = specnode->child; curnode != NULL; + curnode = curnode->next) { + if (strcmp(curnode->name, curfsnode->name) == 0) + break; + } + if (curnode == NULL) { + if (debug & DEBUG_APPLY_SPECONLY) { + printf("apply_specdir: trimming %s/%s %p\n", dir, curfsnode->name, curfsnode); + } + free_fsnodes(curfsnode); + } + } + } + + /* now walk specnode->child matching up with dirnode */ + for (curnode = specnode->child; curnode != NULL; + curnode = curnode->next) { + if (debug & DEBUG_APPLY_SPECENTRY) + printf("apply_specdir: spec %s\n", + curnode->name); + for (curfsnode = dirnode->next; curfsnode != NULL; + curfsnode = curfsnode->next) { +#if 0 /* too verbose for now */ + if (debug & DEBUG_APPLY_SPECENTRY) + printf("apply_specdir: dirent %s\n", + curfsnode->name); +#endif + if (strcmp(curnode->name, curfsnode->name) == 0) + break; + } + if ((size_t)snprintf(path, sizeof(path), "%s/%s", + dir, curnode->name) >= sizeof(path)) + errx(1, "Pathname too long."); + if (curfsnode == NULL) { /* need new entry */ + struct stat stbuf; + + /* + * don't add optional spec entries + * that lack an existing fs entry + */ + if ((curnode->flags & F_OPT) && + lstat(path, &stbuf) == -1) + continue; + + /* check that enough info is provided */ +#define NODETEST(t, m) \ + if (!(t)) \ + errx(1, "`%s': %s not provided", path, m) + NODETEST(curnode->flags & F_TYPE, "type"); + NODETEST(curnode->flags & F_MODE, "mode"); + /* XXX: require F_TIME ? */ + NODETEST(curnode->flags & F_GID || + curnode->flags & F_GNAME, "group"); + NODETEST(curnode->flags & F_UID || + curnode->flags & F_UNAME, "user"); + if (curnode->type == F_BLOCK || curnode->type == F_CHAR) + NODETEST(curnode->flags & F_DEV, + "device number"); +#undef NODETEST + + if (debug & DEBUG_APPLY_SPECFILE) + printf("apply_specdir: adding %s\n", + curnode->name); + /* build minimal fsnode */ + memset(&stbuf, 0, sizeof(stbuf)); + stbuf.st_mode = nodetoino(curnode->type); + stbuf.st_nlink = 1; + stbuf.st_mtime = stbuf.st_atime = + stbuf.st_ctime = start_time.tv_sec; +#if HAVE_STRUCT_STAT_ST_MTIMENSEC + stbuf.st_mtimensec = stbuf.st_atimensec = + stbuf.st_ctimensec = start_time.tv_nsec; +#endif + curfsnode = create_fsnode(".", ".", curnode->name, + &stbuf); + curfsnode->parent = dirnode->parent; + curfsnode->first = dirnode; + curfsnode->next = dirnode->next; + dirnode->next = curfsnode; + if (curfsnode->type == S_IFDIR) { + /* for dirs, make "." entry as well */ + curfsnode->child = create_fsnode(".", ".", ".", + &stbuf); + curfsnode->child->parent = curfsnode; + curfsnode->child->first = curfsnode->child; + } + if (curfsnode->type == S_IFLNK) { + assert(curnode->slink != NULL); + /* for symlinks, copy the target */ + curfsnode->symlink = estrdup(curnode->slink); + } + } + apply_specentry(dir, curnode, curfsnode); + if (curnode->type == F_DIR) { + if (curfsnode->type != S_IFDIR) + errx(1, "`%s' is not a directory", path); + assert (curfsnode->child != NULL); + apply_specdir(path, curnode, curfsnode->child, speconly); + } + } +} + +static void +apply_specentry(const char *dir, NODE *specnode, fsnode *dirnode) +{ + + assert(specnode != NULL); + assert(dirnode != NULL); + + if (nodetoino(specnode->type) != dirnode->type) + errx(1, "`%s/%s' type mismatch: specfile %s, tree %s", + dir, specnode->name, inode_type(nodetoino(specnode->type)), + inode_type(dirnode->type)); + + if (debug & DEBUG_APPLY_SPECENTRY) + printf("apply_specentry: %s/%s\n", dir, dirnode->name); + +#define ASEPRINT(t, b, o, n) \ + if (debug & DEBUG_APPLY_SPECENTRY) \ + printf("\t\t\tchanging %s from " b " to " b "\n", \ + t, o, n) + + if (specnode->flags & (F_GID | F_GNAME)) { + ASEPRINT("gid", "%d", + dirnode->inode->st.st_gid, specnode->st_gid); + dirnode->inode->st.st_gid = specnode->st_gid; + } + if (specnode->flags & F_MODE) { + ASEPRINT("mode", "%#o", + dirnode->inode->st.st_mode & ALLPERMS, specnode->st_mode); + dirnode->inode->st.st_mode &= ~ALLPERMS; + dirnode->inode->st.st_mode |= (specnode->st_mode & ALLPERMS); + } + /* XXX: ignoring F_NLINK for now */ + if (specnode->flags & F_SIZE) { + ASEPRINT("size", "%lld", + (long long)dirnode->inode->st.st_size, + (long long)specnode->st_size); + dirnode->inode->st.st_size = specnode->st_size; + } + if (specnode->flags & F_SLINK) { + assert(dirnode->symlink != NULL); + assert(specnode->slink != NULL); + ASEPRINT("symlink", "%s", dirnode->symlink, specnode->slink); + free(dirnode->symlink); + dirnode->symlink = estrdup(specnode->slink); + } + if (specnode->flags & F_TIME) { + ASEPRINT("time", "%ld", + (long)dirnode->inode->st.st_mtime, + (long)specnode->st_mtimespec.tv_sec); + dirnode->inode->st.st_mtime = specnode->st_mtimespec.tv_sec; + dirnode->inode->st.st_atime = specnode->st_mtimespec.tv_sec; + dirnode->inode->st.st_ctime = start_time.tv_sec; +#if HAVE_STRUCT_STAT_ST_MTIMENSEC + dirnode->inode->st.st_mtimensec = specnode->st_mtimespec.tv_nsec; + dirnode->inode->st.st_atimensec = specnode->st_mtimespec.tv_nsec; + dirnode->inode->st.st_ctimensec = start_time.tv_nsec; +#endif + } + if (specnode->flags & (F_UID | F_UNAME)) { + ASEPRINT("uid", "%d", + dirnode->inode->st.st_uid, specnode->st_uid); + dirnode->inode->st.st_uid = specnode->st_uid; + } +#if HAVE_STRUCT_STAT_ST_FLAGS + if (specnode->flags & F_FLAGS) { + ASEPRINT("flags", "%#lX", + (unsigned long)dirnode->inode->st.st_flags, + (unsigned long)specnode->st_flags); + dirnode->inode->st.st_flags = specnode->st_flags; + } +#endif + if (specnode->flags & F_DEV) { + ASEPRINT("rdev", "%#llx", + (unsigned long long)dirnode->inode->st.st_rdev, + (unsigned long long)specnode->st_rdev); + dirnode->inode->st.st_rdev = specnode->st_rdev; + } +#undef ASEPRINT + + dirnode->flags |= FSNODE_F_HASSPEC; +} + + +/* + * dump_fsnodes -- + * dump the fsnodes from `cur' + */ +void +dump_fsnodes(fsnode *root) +{ + fsnode *cur; + char path[MAXPATHLEN + 1]; + + printf("dump_fsnodes: %s %p\n", root->path, root); + for (cur = root; cur != NULL; cur = cur->next) { + if (snprintf(path, sizeof(path), "%s/%s", cur->path, + cur->name) >= (int)sizeof(path)) + errx(1, "Pathname too long."); + + if (debug & DEBUG_DUMP_FSNODES_VERBOSE) + printf("cur=%8p parent=%8p first=%8p ", + cur, cur->parent, cur->first); + printf("%7s: %s", inode_type(cur->type), path); + if (S_ISLNK(cur->type)) { + assert(cur->symlink != NULL); + printf(" -> %s", cur->symlink); + } else { + assert (cur->symlink == NULL); + } + if (cur->inode->nlink > 1) + printf(", nlinks=%d", cur->inode->nlink); + putchar('\n'); + + if (cur->child) { + assert (cur->type == S_IFDIR); + dump_fsnodes(cur->child); + } + } + printf("dump_fsnodes: finished %s/%s\n", root->path, root->name); +} + + +/* + * inode_type -- + * for a given inode type `mode', return a descriptive string. + * for most cases, uses inotype() from mtree/misc.c + */ +const char * +inode_type(mode_t mode) +{ + + if (S_ISLNK(mode)) + return ("symlink"); /* inotype() returns "link"... */ + return (inotype(mode)); +} + + +/* + * link_check -- + * return pointer to fsinode matching `entry's st_ino & st_dev if it exists, + * otherwise add `entry' to table and return NULL + */ +/* This was borrowed from du.c and tweaked to keep an fsnode + * pointer instead. -- dbj@netbsd.org + */ +static fsinode * +link_check(fsinode *entry) +{ + static struct entry { + fsinode *data; + } *htable; + static int htshift; /* log(allocated size) */ + static int htmask; /* allocated size - 1 */ + static int htused; /* 2*number of insertions */ + int h, h2; + uint64_t tmp; + /* this constant is (1<<64)/((1+sqrt(5))/2) + * aka (word size)/(golden ratio) + */ + const uint64_t HTCONST = 11400714819323198485ULL; + const int HTBITS = 64; + + /* Never store zero in hashtable */ + assert(entry); + + /* Extend hash table if necessary, keep load under 0.5 */ + if (htused<<1 >= htmask) { + struct entry *ohtable; + + if (!htable) + htshift = 10; /* starting hashtable size */ + else + htshift++; /* exponential hashtable growth */ + + htmask = (1 << htshift) - 1; + htused = 0; + + ohtable = htable; + htable = ecalloc(htmask+1, sizeof(*htable)); + /* populate newly allocated hashtable */ + if (ohtable) { + int i; + for (i = 0; i <= htmask>>1; i++) + if (ohtable[i].data) + link_check(ohtable[i].data); + free(ohtable); + } + } + + /* multiplicative hashing */ + tmp = entry->st.st_dev; + tmp <<= HTBITS>>1; + tmp |= entry->st.st_ino; + tmp *= HTCONST; + h = tmp >> (HTBITS - htshift); + h2 = 1 | ( tmp >> (HTBITS - (htshift<<1) - 1)); /* must be odd */ + + /* open address hashtable search with double hash probing */ + while (htable[h].data) { + if ((htable[h].data->st.st_ino == entry->st.st_ino) && + (htable[h].data->st.st_dev == entry->st.st_dev)) { + return htable[h].data; + } + h = (h + h2) & htmask; + } + + /* Insert the current entry into hashtable */ + htable[h].data = entry; + htused++; + return NULL; +}