kqjy kqjy
  • Joined on 2024-09-06
kqjy pushed to next at kqjy/MyFSIO 2026-08-16 06:15:32 +00:00
dd8d1d90aa Remove the login card's gradient top border and centre the sign-in layout.
kqjy pushed to next at kqjy/MyFSIO 2026-08-15 13:46:31 +00:00
b7e8fcac30 Prove the bit-rot lifecycle end to end and document the read-time integrity model. A new test writes an object through the real PUT path, flips its bytes on disk, and asserts the full chain: before any scan the rotten bytes are served, which is the documented detection gap; the integrity scan detects the etag mismatch and carries the stored etag in its issue detail; healing quarantines the object; subsequent reads fail closed with ObjectCorrupted instead of serving garbage; and the corrupted bytes are preserved under the quarantine directory for forensics. The durability invariants doc gains a bit-rot section recording the three existing layers - upload-time checksum verification, AES-GCM chunk authentication that makes every read of an encrypted object self-verifying so SSE objects can never serve rotten bytes, and the offline scanner with quarantine and peer-based healing - plus the honest statement of the gap: an unencrypted object serves corrupted content from the moment of corruption until the next scan pass.
kqjy pushed to next at kqjy/MyFSIO 2026-08-15 13:34:32 +00:00
00b6c347c2 Document site sync semantics and prove conflict-resolution convergence. The bi-directional site sync worker's behavior was implemented but never specified; a new semantics doc records the actual model: a periodic pull worker layered over event-driven push replication that resolves same-key conflicts last-writer-wins outside the clock-skew tolerance and by a deterministic lexicographic etag tiebreaker within it, applies inbound deletions only to objects tracked as remote-origin whose local etag and mtime are unchanged since the last sync, aborts the whole cycle when the remote bucket is missing rather than treating it as empty, and under partition resolves write-versus-delete races to resurrection in preference to data loss. The doc also records the honest caveats: losing concurrent writes survive only as per-site noncurrent versions, only current objects propagate, there is no anti-entropy digest beyond the per-cycle listing comparison, and losing the tracking state stops inbound deletions and can resurrect remotely deleted objects via push replication. The conflict decision is extracted into a pure resolve_conflict_decision function with unit tests including a two-thousand-case randomized convergence property: for any pair of timestamps and etags, either both sites skip or exactly one side pulls, so two sites always converge instead of ping-ponging or diverging permanently.
kqjy pushed to next at kqjy/MyFSIO 2026-08-15 13:26:21 +00:00
cddff0e988 Harden the metadata migration with preflight, rollback backups, and crash-proven resumability. --migrate-meta now runs a preflight scan before modifying anything: it counts index files and entries, and reports corrupt _index.json files and sidecar-name collisions - including two entries in one directory hashing to the same sidecar filename - aborting with the data untouched when problems are found. A fully migrated index file is renamed to _index.json.migrated instead of being deleted, turning the previously one-way migration into one with a documented rollback: delete the new sidecar files and rename the backups back, then delete the backups once satisfied; nothing reads the .migrated name so the backups are inert. A new migrate:sidecar-write failpoint proves the crash story: a simulated crash mid-migration leaves the index in place and every object readable, since sidecar-first read precedence makes a half-migrated directory fully servable, and a re-run completes the migration; injected per-entry write failures are contained in the report with the index still serving and a retry succeeding. The durability invariants doc records the migration contract.
kqjy pushed to next at kqjy/MyFSIO 2026-08-15 13:04:49 +00:00
a1aa3e9e6c Add concurrency storm tests with a full invariant audit. Two multi-thread tests hammer shared key spaces to answer the concurrent-load question directly: an unversioned storm runs sixteen parallel workers issuing four thousand mixed put, delete, list, and get operations against forty keys across nested prefixes, and a versioned storm runs twelve workers mixing overwrites, delete-marker creation, version listings, and reads against a versioning-enabled bucket. A shared audit then checks the invariants end to end: the incremental listing index must exactly equal a discarded-and-rebuilt listing, every listed object must have its metadata sidecar present, be readable, match its listed size, and carry an etag equal to its content MD5, every data file on disk must appear in the listing so nothing is orphaned, no temp or staged sidecar files may survive the storm, bucket stats must agree with the listing, and per-key version lists must contain unique version ids with at most one latest entry. Both tests pass repeatedly in about twelve seconds each, cheap enough for the regular suite, and the durability invariants doc records the harness plus the convention that new write paths join the storm's operation mix.
kqjy pushed to next at kqjy/MyFSIO 2026-08-15 12:58:22 +00:00
d6d136925a Fix four S3 conformance bug classes: KMS error mapping, object-lock bucket creation, SSE header echo, and ACL grant order. An unknown or disabled KMS key during SSE-KMS encryption surfaced as a generic 500 InternalError, and on streamed uploads the mid-body failure tore down the connection; the crypto layer now reports typed KmsKeyNotFound and KmsKeyDisabled errors which the server maps to AWS's 400 KMS.NotFoundException and KMS.DisabledException, and PutObject and CreateMultipartUpload validate the referenced key before draining the request body so streamed puts fail cleanly up front. CreateBucket silently ignored the x-amz-bucket-object-lock-enabled header, leaving lock buckets without versioning so puts returned no version id and the lock configuration read as absent; it now enables versioning and stores an Enabled object-lock configuration, matching AWS semantics. GET and HEAD responses on SSE-KMS objects echoed the algorithm but not the key id - x-amz-server-side-encryption-aws-kms-key-id is now emitted from the stored encryption metadata across all four object-response paths, and UploadPart and UploadPartCopy responses echo the destination upload's pending SSE algorithm and key id. ACL responses serialized the owner grant first, which crashes ceph/s3-tests' grant checker when it sorts grants whose group grantees have no DisplayName; group grants now serialize before canonical-user grants, matching rgw's ordering. Together these move the s3-tests conformance run from 343 to 402 passing of 838, with the InternalError and connection-drop classes eliminated entirely.
kqjy pushed to next at kqjy/MyFSIO 2026-08-15 12:16:40 +00:00
9ff7f16825 Include VersionId in DeleteObjects per-key error entries. AWS echoes the requested version id back inside each <Error> element of a DeleteResult; MyFSIO emitted only Key, Code, and Message, which broke clients that key their error handling on the version - notably the ceph/s3-tests cleanup helper, which crashed with a KeyError on the missing field when bulk deletes against object-locked keys returned AccessDenied errors. The delete handler now threads the requested version id into the error tuple and the XML writer emits <VersionId> after <Key> when one was supplied, omitting it for unversioned requests as AWS does. Found running the ceph/s3-tests conformance suite (838 tests: 343 pass / 365 fail / 20 error / 76 skip), which also surfaced follow-ups for a later pass: SSE-KMS with an unknown key returns InternalError or drops the connection instead of a 4xx, puts to object-lock-enabled buckets omit the version id in their response, UploadPartCopy does not echo SSE headers, and ACL grants omit DisplayName and ID.
kqjy pushed to next at kqjy/MyFSIO 2026-08-15 11:24:34 +00:00
c9a4f6ccfe Make interrupted segments-layout multipart completes recoverable on retry. Completing a multipart upload in the segments layout moves the part files into the segment directory before the commit, so a crash in that window used to consume the upload: the retry failed with a missing-part error and the moved parts sat as an orphaned segment directory until the GC sweep. The complete now resolves each part from the upload directory or, when a previous crashed attempt already moved it, from the segment directory - sizing and hashing whichever source exists and falling back to the manifest digest as before - and the move loop skips parts that are already in place, so a retry succeeds anywhere the crash landed, including mid-move with mixed part sources. If the retry assembles by concatenation instead, for example after a layout switch, it also reads recovered segment files and removes the leftover segment directory once the object commits. The segments crash test now asserts recovery instead of documenting the failure, a new test covers the partially-moved case by moving one part into the segment directory by hand, and the durability invariants doc records the recovery semantics plus the one accepted gap: a single-part complete interrupted between its part rename and the commit still loses the upload, which is trivially re-uploadable.
kqjy pushed to next at kqjy/MyFSIO 2026-08-15 11:15:44 +00:00
30748b90d5 Add a kill-the-process crash test that aborts the real server binary mid-commit and verifies recovery across restart. The failpoint registry gains an Abort action that calls std::process::abort with no unwinding or cleanup, the truest in-process stand-in for a power cut, and the server arms failpoints from a MYFSIO_FAILPOINTS environment variable, read only when built with the new failpoints cargo feature that forwards to myfsio-storage/failpoints, so none of this machinery exists in normal builds. The feature-gated integration test spawns the actual myfsio-server binary against a temp storage root using legacy header auth, aborts it during a PUT at put:before-data-rename and separately at put:before-publish-sidecar, restarts it clean after each, and asserts the durability invariants end to end: a kill before the data rename leaves the previous object byte-identical with its original etag, a kill after the rename yields the documented torn state of new data under the previous sidecar which a subsequent put repairs, the put whose process died never reported success, and every restart runs the real unclean-shutdown recovery. The durability invariants doc records the mechanism and the cargo test --features failpoints invocation.
kqjy pushed to next at kqjy/MyFSIO 2026-08-15 10:42:29 +00:00
5b45ea5ed0 Add test-only fault-injection failpoints and crash-consistency tests for the PUT commit. A new myfsio-storage failpoints module, compiled only under cfg(test) or the new failpoints cargo feature and absent from release builds, lets tests inject an I/O error or simulate a process crash at named commit boundaries; the registry is scoped by storage root so parallel tests against separate tempdir backends cannot trip each other's failpoints, and a crash-action panic unwinds without running cleanup so the on-disk state is exactly what a kill at that point would leave. Three failpoints cover the PUT commit protocol - during sidecar staging, before the data rename, and before the sidecar publish - with tests asserting the durability invariants: an error while staging aborts the put cleanly with the old object and etag intact and no staged files left behind, a crash before the data rename leaves the old object untouched, and a crash between the data rename and the sidecar publish leaves new data under the previous metadata, detectable as an etag mismatch, with the staged sidecar remaining in the tmp directory the GC sweep already collects. The durability invariants doc gains a failpoints section recording the convention that every new commit boundary gets a failpoint and a matching test.
kqjy pushed to next at kqjy/MyFSIO 2026-08-15 10:34:25 +00:00
adb77040cd Make the PUT commit crash-safe and add an on-disk format version marker. finalize_put_sync previously published the metadata sidecar before renaming the data file into place, so a crash between the two steps left metadata describing data that was absent or stale — a ghost object visible in listings whose GET failed, or an overwrite serving old bytes under the new etag and size. The sidecar-layout commit now stages the new sidecar to a fsynced temp file under .myfsio.sys/tmp, renames the data into place, fsyncs the parent directory, and only then publishes the sidecar by rename, making the sidecar publish the commit point: a crash now yields either a PUT that never happened or new data under the previous sidecar, which the integrity scanner can flag, and ENOSPC still aborts cleanly because the sidecar bytes are written before anything is destroyed. Crash-orphaned staged files sit in the tmp directory GC already sweeps, and the legacy index metadata layout keeps its old ordering since per-key staging of the shared _index.json is not atomic. Adds .myfsio.sys/config/format.json, enforced at serve startup: it records the format version plus per-feature layout high-water marks (metadata, multipart, listing index) that only ever ratchet upward, and the server refuses to start with a restore-from-backup message when the marker declares a format version or authoritative layout this binary does not understand, while a newer listing-index version only warns because that index is derived and rebuilt. Unknown marker fields are preserved.
kqjy pushed to next at kqjy/MyFSIO 2026-08-15 10:00:33 +00:00
e84dd77448 Rework the Remote Connections page: swap card order, replace the title icon, merge the form buttons into one row, drop the connection-count badge, and stop the connections table overflowing on phones. The page title now carries the same network icon as the sidebar Connections nav instead of a cloud whose second path rendered as an artifact. Existing Connections moves to the wide left column with the add form on the right, and the two cards stack below lg rather than md, since at md the col-md-7 split left the table wrapper ~240px wide and forced an inner scrollbar. Test Connection and Add Connection share one row as two col-6 halves, with the test button relabeled Test to fit the narrow column. The table opts into .table-fluid (the global .table-responsive rule pins tables to min-width 600px with nowrap cells), the Endpoint column folds under the connection name where it wraps on overflow-wrap anywhere, and Region and Access Key stay columns only from xl up, collapsing into a badge/code row under the name below that; the card body drops to px-3 on small screens. connections-management.js emits the same structure from its row builder and its empty-state table header, and edit-in-place now writes through .conn-endpoint/.conn-region/.conn-access-key with querySelectorAll because each of those values renders twice, so the old .text-truncate/.badge.bg-primary/code.small selectors would have updated only the hidden copy. The N connections badge and its now-dead updateConnectionCount helper are gone.
kqjy pushed to next at kqjy/MyFSIO 2026-08-15 09:35:56 +00:00
d41fb2a34b Rework the Domain Mappings page: swap card order, drop the mapping-count badge, replace the globe icons, gate the bucket picker on website hosting, add a DNS check, and stop the mappings table overflowing. The page title now carries the same link icon as the sidebar Domains nav, Active Mappings takes the list icon, and the per-row globe becomes a clickable DNS status indicator. Active Mappings moves to the wide left column with the add form on the right, and the useless '1 mapping' badge is gone. The add and edit bucket pickers list only buckets whose config carries a website block, with a disabled empty state explaining how to enable it, and create_website_domain/update_website_domain re-check that server-side so a forged bucket value cannot create a mapping that could never serve. Adds GET /ui/website-domains/{domain}/dns-check (admin-gated) which resolves the domain with a 5s timeout and compares the answers against the bind address plus the local addresses of the default IPv4/IPv6 routes, returning ok (loopback or a local address), warning (resolves elsewhere, still valid behind NAT/proxy/CDN), or error (NXDOMAIN/timeout/no records); it never connects to the domain, keeping the SSRF surface at zero and the verdict honest. The global .table-responsive rule pins every table to min-width 600px with nowrap cells, which forced the long DNS message onto one line and kept the table wider than a phone viewport, so add an opt-in .table-fluid override and apply it here; the message is also shortened with the answer list capped at two addresses and the full list moved to the icon tooltip, the Bucket column collapses into the domain cell below 576px, and the status line wraps inside a bounded block. Also fixes the pre-existing render_metrics test which lacked display_timezone since the metrics timestamp commit.
kqjy pushed to next at kqjy/MyFSIO 2026-08-15 07:50:33 +00:00
275a36e623 Show the date alongside the time in the metrics Recent Errors table and honor DISPLAY_TIMEZONE. The table reused formatTime, a chart-axis helper that renders only hour:minute in the browser's local timezone, so rows from the 256-entry error ring were indistinguishable across days. Add formatEventTimestamp rendering date over time with the full localized timestamp as a tooltip, leave the chart labels on formatTime, and insert display_timezone into the metrics page context, which it was never given.
kqjy pushed to next at kqjy/MyFSIO 2026-08-14 09:35:22 +00:00
60c053fea1 Make the UI zip download's encrypted read path segment-aware and lock the encrypted-object invariant. read_object_bytes_for_zip previously decrypted encrypted objects straight from get_object_path, which for a segments-layout object is the sparse stub rather than the assembled content; it now materializes segmented objects to a temp file first (cleaned up on both success and failure), matching the replication and S3 Select Parquet paths. This combination is currently unreachable because segments_allowed requires the absence of every pending-SSE marker and CopyObject strips __segments__, but the post-complete SSE encryption and read_mpu_part_plain_sizes paths rely on the same unstated invariant, so add test_sse_multipart_never_uses_segments_layout asserting an SSE multipart object carries encryption metadata without __segments__, with an unencrypted control upload proving the segments layout is active so the assertion cannot pass vacuously.
kqjy released MyFSIO v0.5.8 Beta at kqjy/MyFSIO 2026-08-12 06:24:11 +00:00
kqjy pushed tag v0.5.8 to kqjy/MyFSIO 2026-08-12 06:24:11 +00:00
kqjy pushed to main at kqjy/MyFSIO 2026-08-12 06:06:39 +00:00
1fbb143a3e MyFSIO v0.5.8 Release
f6adff3f65 Fix quarantine GC protection reading the wrong metadata root, multipart corruption false positives, and issue-count reporting; drop the legacy heal path
4225182b8e Harden authorization, object lock, storage commits, background services, secrets at rest, website serving, request limits, sessions, and the installer. x-amz-bypass-governance-retention is now honored only for admins or principals granted the new bypass_governance IAM action (or s3:BypassGovernanceRetention in a bucket policy), evaluated per key on bulk delete, so callers with plain delete access can no longer strip GOVERNANCE retention; is_admin requires an unrestricted policy prefix so a prefix-scoped wildcard policy no longer short-circuits every authorization check; bucket-policy Resource keys match case-sensitively while bucket and Action matching stay case-insensitive; and Date-based lifecycle rules are a no-op until the date passes instead of using the configured date as an age cutoff. PutBucketObjectLockConfiguration validates the XML and requires versioning (409 InvalidBucketState), and its DefaultRetention is actually applied at every user-facing object-creation path with explicit headers winning and replication/peer-pull/restore paths untouched. Retention and legal-hold updates are check-and-set under the per-object stripe lock via new update_object_retention/update_object_legal_hold trait methods, so concurrent requests can no longer shorten COMPLIANCE retention and legal-hold writes no longer clobber concurrently written metadata; same-mode extension of either mode needs no bypass while shortening, removal and mode changes keep the bypass rules. A failed PUT commit now rolls back the archived version and metadata sidecar, and the archived-null purge runs after the rename so a failed commit can never destroy the prior null version. Site sync aborts the cycle on any remote-listing failure instead of treating NoSuchBucket/404 as an empty bucket (sync_deletions could erase every locally synchronized object) and records last_error/last_error_at per bucket so failing cycles are visible on the Sites page; GC skips a bucket's segment sweep whenever the reference scan hit a read error and reports the skip instead of deleting live segment data on an incomplete scan; and the process exits non-zero when either listener task dies so systemd restarts it. IAM mutations are serialized behind a mutation lock and persisted atomically (tmp+fsync+rename), first-run and reset admin credentials are written encrypted with a one-time plaintext-to-encrypted migration at startup and env-provided secrets no longer echoed to stdout, SECRET_KEY auto-generation to .myfsio.sys/config/.secret is implemented with dev-secret-key rejected from both env and file, and iam.json, KMS master keys, kms_keys.json, .connections_key, connections.json and .secret are all written owner-only through shared atomic helpers. Static website hosting streams through the extracted handlers/object_read data plane: SSE-S3/SSE-KMS assets that were served as ciphertext are decrypted, HEAD reports plaintext Content-Length, 206 ranges are computed from plaintext offsets, SSE-C assets return 403, and no path buffers whole objects in memory; ranged S3 GETs validate SSE-C keys like the whole-object path (400/403 instead of 500). Every config/XML/JSON body ingress is capped (1 MiB config XML and Select, 8 MiB CompleteMultipartUpload/DeleteObjects, 1-2 MiB JSON, bounded relay-outbound, 1 MiB non-file multipart form fields) with a new MaxMessageLengthExceeded error, and the UI part upload streams to disk with a 5 GiB cap instead of buffering each part twice in memory. Anonymous requests use ephemeral sessions that never enter the session store, capacity eviction prefers unauthenticated sessions, GET/POST /login is rate limited per IP (RATE_LIMIT_UI_LOGIN, default 20 per minute) with a styled 429 page and Retry-After, and failed UI uploads surface the real S3 reason in the toast and the upload dialog instead of a bare failure count. install.sh and uninstall.sh canonicalize and refuse system paths, guard foreign data directories behind --adopt-data-dir, track installer-created users in a manifest so uninstall preserves pre-existing accounts, and re-installs preserve myfsio.env (--overwrite-env backs up then regenerates). Adds handlers/object_read.rs, myfsio_common::fs_util, login_rate_limited.html, the bypass_governance IAM action, S3ErrorCode::{MaxMessageLengthExceeded,InvalidBucketState}, StorageError::InvalidArgument and RATE_LIMIT_UI_LOGIN; ~90 new regression tests.
f1c2ff4b8b Reject unknown encoding-type and cap max-keys at 1000; fix dead cluster-page JS, copy/move destination default, audio preview stage height, and UI polish
42c5bb3b9c Fix three P0 authorization defects: bucket-name path escape, and query-selector precedence mismatch at both the bucket and object level. Enforce full S3 bucket-name syntax plus canonical containment inside the storage backend (require_bucket/bucket_exists/create_bucket/multipart_upload_dir/list_multipart_uploads/is_versioning_enabled/get_versioning_status) instead of only in create_bucket, so a percent-encoded absolute path in the bucket position can no longer replace STORAGE_ROOT; percent-decode bucket, key and copy-source in the auth middleware so it authorizes the same strings the handler acts on; guard the service-layer joins the UI reaches without the storage backend (lifecycle history, replication ledger, archived/restore paths). Replace the divergent query scans with one BucketSubresource and one ObjectSubresource parser shared by the middleware and every dispatcher: multiple selectors are rejected with InvalidArgument at authorization time, non-dispatchable selectors return MethodNotAllowed instead of falling through, and a non-dispatchable pair authorizes as the method default so it is never weaker than the request. Closes the authenticated list-to-policy escalation, its unauthenticated variant on public-read buckets, and a read-only principal overwriting or deleting objects via PUT/DELETE ?attributes|?select|?uploads and DELETE ?retention. ?ownershipControls/?publicAccessBlock gain dedicated ownership_controls/public_access_block IAM actions; PutBucketLogging TargetBucket and website-domain mappings reject invalid names directly; replication validates only the source bucket strictly (a remote target may carry a name this server would not issue); ListBuckets and --rebuild-listing skip invalid-named directories with a warning instead of trapping or failing. Removes now-dead has_query_key; adds validation::bucket_name_rejection and 7 regression tests.
Compare 6 commits »
kqjy merged pull request kqjy/MyFSIO#45 2026-08-12 06:06:39 +00:00
MyFSIO v0.5.8 Release
kqjy pushed to main at kqjy/ServerMonitor 2026-08-11 13:40:12 +00:00
17460a742f Pin a ranked needs-attention section with fleet counts on the hosts page, fold the host-detail agent-health strip into one headline with collapsible details, add container filtering/scope, memory-limit meters and per-window CPU/memory/net peaks (new cpu_max/mem_max/io_rate_max in the container series query), and lay an a11y baseline of focus-visible rings, reduced-motion, live-region announcements and real expand buttons; bump to 0.4.5