mirror of
https://github.com/codewhale-hq/Codewhale.git
synced 2026-11-07 10:36:05 -10:00
A GUI start on HarmonyOS hmdfs failed one feature at a time: the workspace drive lease, then the owner control socket as `failed to build daemon state`, then the OAuth credential directory as `Codewhale credentials directory must be owned by the current user`. Each of those admitted a directory by proving it was current-user-owned and closed to group and other, and on that mount no directory can: it reports the logged-in user's uid and a group-writable mode for every object, whatever uid created it, so the calling process is never the reported owner and `chmod` answers EPERM. Each fix revealed the next, because the same mechanism sits under every private directory Codewhale keeps. The mechanism now names the condition once. `private_footing` creates one `0o700` directory under the target, reads back what the mount made of it, and — when that is not the private owner and mode it asked for — asks the mount to narrow it and looks again. Only a mount that refuses, or that keeps its own values after being asked, is synthesising; a filesystem that applies an unexpected mode at creation but honours the correction stores modes and keeps the full guard, so a default ACL cannot silently relax a check. The probe runs only after a strict check has already failed, and its answer (with its one warning) is remembered per directory, so no write path probes a healthy filesystem. Where the mount is synthesising, the private object is admitted with the ownership guard recorded as unverifiable. The no-follow component walk, the single-link check, atomic publication and the retained handle all still apply, and the mount's own access control governs the rest. Two capabilities are kept apart, because conflating them is what made the original failure look like a missing feature. Exclusivity is the kernel lock, not the ownership proof: the workspace lease is still taken, and a second driver is still refused with the holder's pid, client, store and age. Only a filesystem that refuses advisory locks themselves (`EPERM`, `EACCES`, `ENOTSUP`, `EINVAL`, `ENOSYS`, and never `EWOULDBLOCK`) is reported as unable to provide exclusivity, once per admitted workspace scope. The owner control socket has nothing else to fall back on for discovery, so it degrades to HTTP only and says so; a daemon-only run and ACP publication still require it and name the reason. Narrowing a file or directory after writing it goes through one helper that attempts the `chmod`, keeps the error on a filesystem that stores modes, and excuses only a mount that cannot apply them — applied to the handle that was opened where one exists, so a substituted path cannot be the object narrowed. Checks that repeated the ownership proof on an object already admitted (the retained-directory identity comparison, an owner receipt's mode, a published socket's owner) now follow the guard it was admitted with. `scripts/ohos-sign-binary.sh` cross-builds, strips and self-signs the Engine and verifies the signature before handing the file back; `scripts/ohos-lease-footing-probe.sh` reports what a directory on a device does with ownership, mode and advisory locks; `scripts/ohos-secrets-store-smoke.sh` exercises the secret store's degraded path against a real mount.
132 lines
4.8 KiB
Bash
Executable File
132 lines
4.8 KiB
Bash
Executable File
#!/usr/bin/env sh
|
|
# Cross-build and self-sign the Codewhale Engine for a HarmonyOS / OpenHarmony
|
|
# device, using the signing tools inside the DevEco Studio SDK.
|
|
#
|
|
# A binary only runs on device when it carries a code signature: the loader
|
|
# refuses an unsigned ELF, and the failure it reports ("Permission denied") is
|
|
# indistinguishable from a plain missing exec bit. Native HarmonyOS therefore
|
|
# needs both this step and `strip` before the file is useful.
|
|
#
|
|
# The signature is SELF-SIGNED. `binary-sign-tool` writes a descriptor digest
|
|
# and sets FLAG_SELF_SIGN; it does not embed a certificate and needs no private
|
|
# key. That is the same shape as the binary already running on the device —
|
|
# `display-sign` reports "code signature is self-sign" for both. It is not an
|
|
# AppGallery-signed build and carries no bundle identity.
|
|
#
|
|
# Usage:
|
|
# scripts/ohos-sign-binary.sh [--strip] [--out PATH]
|
|
#
|
|
# Requires:
|
|
# OHOS_NATIVE_SDK the OpenHarmony native SDK (llvm/, sysroot/, build-tools/)
|
|
#
|
|
# Steps this runs:
|
|
# 1. cargo build --release --target aarch64-unknown-linux-ohos -p codewhale-cli
|
|
# 2. llvm-strip the copy (optional, ~2.6 MiB smaller)
|
|
# 3. binary-sign-tool sign -selfSign 1
|
|
# 4. binary-sign-tool display-sign, so a failure is not silent
|
|
set -eu
|
|
|
|
repo_root=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd)
|
|
cd "$repo_root"
|
|
|
|
strip_binary=no
|
|
out=
|
|
while [ $# -gt 0 ]; do
|
|
case $1 in
|
|
--strip) strip_binary=yes; shift ;;
|
|
--out) out=${2:?--out needs a path}; shift 2 ;;
|
|
-h|--help) sed -n '2,26p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
|
*) echo "unknown argument: $1" >&2; exit 2 ;;
|
|
esac
|
|
done
|
|
|
|
if [ -z "${OHOS_NATIVE_SDK:-}" ]; then
|
|
echo "error: set OHOS_NATIVE_SDK to the OpenHarmony native SDK directory." >&2
|
|
echo " e.g. /Applications/DevEco-Studio.app/Contents/sdk/default/openharmony/native" >&2
|
|
exit 1
|
|
fi
|
|
|
|
sdk=$(cd "$OHOS_NATIVE_SDK" && pwd)
|
|
toolchains=$sdk/../toolchains
|
|
sign_jar=$toolchains/lib/binary-sign-tool.jar
|
|
# The keystore is only consulted for its alias name in self-sign mode; the
|
|
# password is the published one that ships with the SDK sample material.
|
|
keystore=$toolchains/lib/OpenHarmony.p12
|
|
keystore_pwd=123456
|
|
key_alias='openharmony application release'
|
|
strip_tool=$sdk/llvm/bin/llvm-strip
|
|
|
|
for file in "$sign_jar" "$keystore" "$strip_tool"; do
|
|
[ -f "$file" ] || { echo "error: missing SDK file: $file" >&2; exit 1; }
|
|
done
|
|
command -v java >/dev/null 2>&1 || { echo "error: java not found; install a JDK or use DevEco's jbr." >&2; exit 1; }
|
|
|
|
# 1. Build. The environment is shared with the documented manual flow so there
|
|
# is one place that decides the target flags.
|
|
# shellcheck disable=SC1091
|
|
. ./scripts/ohos-env.sh >/dev/null
|
|
|
|
echo "== building codewhale-cli for aarch64-unknown-linux-ohos"
|
|
cargo build --locked --release --target aarch64-unknown-linux-ohos -p codewhale-cli
|
|
|
|
built=target/aarch64-unknown-linux-ohos/release/codewhale
|
|
[ -f "$built" ] || { echo "error: build produced no binary at $built" >&2; exit 1; }
|
|
|
|
out=${out:-target/aarch64-unknown-linux-ohos/release/codewhale-signed}
|
|
work=$(mktemp -d)
|
|
trap 'rm -rf "$work"' EXIT HUP INT TERM
|
|
|
|
# 2. Strip a copy. Never strip the build output in place: cargo would consider
|
|
# the artifact current and leave a stripped binary where it expects an
|
|
# unstripped one.
|
|
cp "$built" "$work/unsigned"
|
|
if [ "$strip_binary" = yes ]; then
|
|
echo "== stripping"
|
|
"$strip_tool" --strip-unneeded "$work/unsigned"
|
|
fi
|
|
|
|
# 3. Sign.
|
|
echo "== signing (self-sign)"
|
|
java -jar "$sign_jar" sign \
|
|
-mode localSign \
|
|
-keyAlias "$key_alias" \
|
|
-keystoreFile "$keystore" \
|
|
-keystorePwd "$keystore_pwd" \
|
|
-inFile "$work/unsigned" \
|
|
-signAlg SHA256withECDSA \
|
|
-outFile "$work/signed" \
|
|
-selfSign 1 2>&1 | sed -n 's/^.*INFO - / /p'
|
|
|
|
# 4. Verify, and refuse to hand back a file that did not sign.
|
|
echo "== verifying"
|
|
verify=$(java -jar "$sign_jar" display-sign -inFile "$work/signed" 2>&1) || {
|
|
echo "$verify" >&2
|
|
echo "error: display-sign failed" >&2
|
|
exit 1
|
|
}
|
|
case $verify in
|
|
*"code signature is self-sign"*) echo " code signature is self-sign" ;;
|
|
*) echo "$verify" >&2; echo "error: the output is not self-signed" >&2; exit 1 ;;
|
|
esac
|
|
|
|
cp "$work/signed" "$out"
|
|
chmod +x "$out"
|
|
echo
|
|
echo "signed: $out"
|
|
echo "size : $(wc -c < "$out") bytes"
|
|
echo "sha256: $(shasum -a 256 "$out" | cut -d' ' -f1)"
|
|
cat <<'NOTE'
|
|
|
|
To run it on a device, note that two separate gates apply:
|
|
|
|
* the file needs its exec bits. HarmonyOS hmdfs (the user directory) refuses
|
|
`chmod`, and `hdc file send` creates files at 0660, so copy on device from
|
|
something already executable:
|
|
cp ~/.cargo/bin/codewhale ~/codewhale-new # keeps 0750
|
|
# then overwrite its content with the new build
|
|
* it must be executed from an app process (HiShell, or the CodeArts
|
|
integrated terminal). The hdc debug shell cannot exec a file on hmdfs at
|
|
all — it reports "Permission denied" even for a binary that runs fine
|
|
elsewhere.
|
|
NOTE
|