Skip to content

Implement Darwin (MacOS)#169

Open
lancepioch wants to merge 10 commits intomainfrom
lance/mac
Open

Implement Darwin (MacOS)#169
lancepioch wants to merge 10 commits intomainfrom
lance/mac

Conversation

@lancepioch
Copy link
Member

@lancepioch lancepioch commented Feb 7, 2026

Summary by CodeRabbit

  • New Features

    • Full macOS/Darwin support and a Darwin build target, including platform-aware filesystem behavior.
  • Bug Fixes

    • Container resource handling now applies only on Linux, improving macOS Docker Desktop compatibility.
  • Documentation

    • Comprehensive macOS setup and build guidance added.
  • Chores

    • Reduced websocket log verbosity for common cleanup/error cases; updated build targets to include Darwin.
  • Tests

    • Added platform-aware tests for filesystem behavior and feature detection.

@lancepioch lancepioch self-assigned this Feb 7, 2026
@lancepioch lancepioch requested a review from a team as a code owner February 7, 2026 19:05
@lancepioch lancepioch added the enhancement New feature or request label Feb 7, 2026
@coderabbitai
Copy link

coderabbitai bot commented Feb 7, 2026

📝 Walkthrough

Walkthrough

Adds macOS/Darwin support and platform-specific splits across filesystem, openat2 detection, system info, and build tooling; introduces Darwin-specific implementations and tests, updates Makefile targets, and adjusts websocket log levels.

Changes

Cohort / File(s) Summary
Build Configuration
Makefile
Added build-darwin target, updated cross-build dependencies and .PHONY to include Darwin cross-build targets.
OpenAt2 Detection
config/config.go, config/config_openat_darwin.go, config/config_openat_linux.go, config/config_openat_linux_test.go, config/config_openat_test.go
Moved Openat2 detection into platform files: Darwin stub always false; Linux runtime probe with atomic caching; removed Linux-specific logic from main config and added tests.
Filesystem Core (ufs)
internal/ufs/file.go, internal/ufs/file_darwin.go, internal/ufs/file_linux.go, internal/ufs/fs_darwin.go, internal/ufs/fs_linux.go, internal/ufs/fs_unix.go, internal/ufs/fs_unix_test.go
Split FS behavior per OS: fdPath, open/openat2 handling, Chtimesat differences, atomic openat2 toggle, and resolving symlinks at FS init; tests adjusted for symlinked temp dirs.
Dirent / Directory Walking
internal/ufs/walk_darwin.go, internal/ufs/walk_linux.go, internal/ufs/walk_unix.go, internal/ufs/walk_dirent_darwin.go, internal/ufs/walk_dirent_linux.go, internal/ufs/walk_dirent_*.go
Introduced OS-specific getdents and dirent helpers; removed prior reflect/unsafe manual name parsing; added per-OS dirent info/open implementations.
Platform Tests
internal/ufs/fs_platform_test.go, internal/ufs/readdirmap_darwin_test.go
Added unix/Darwin-targeted tests exercising fd path resolution, openat path validation, Chtimesat zero-time behavior, ReadDirMap concurrency, and O_LARGEFILE expectations.
File Flags Constants
internal/ufs/file.go, internal/ufs/file_darwin.go, internal/ufs/file_linux.go
Removed generic O_LARGEFILE/AT_EMPTY_PATH from common file; provided OS-specific definitions (Darwin noop, Linux alias to unix.O_LARGEFILE).
Server Filesystem & Stat
server/filesystem/stat_darwin.go, server/filesystem/stat_test.go, server/filesystem/filesystem_test.go
Added Darwin-specific Stat.CTime(), tests for CTime and Stat JSON marshaling; tests now resolve tmpdir symlinks.
System Detection & Config
system/system.go, config/config.go
Short-circuited OS detection on Darwin (return macOS/darwin without reading os-release); EnsurePelicanUser uses runtime user info on Darwin.
Environment Settings
environment/settings.go
Guarded BlkioWeight assignment to only set on Linux (runtime.GOOS check).
Websocket Logging
router/router_server_ws.go, router/websocket/listeners.go
Downgraded several websocket-related log levels from Error/Warning to Info for expected disconnect/cleanup scenarios.
Documentation
macos.md
New macOS guide describing prerequisites, build/run instructions, platform-specific rationale, and developer notes for macOS compatibility.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐰
I nibble symlinks full of cheer,
Darwin paths now crystal clear.
Openat2 sleeps on macOS shore,
Cross-build hops and tests explore.
A rabbit claps — the builds run more!

🚥 Pre-merge checks | ✅ 2 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 41.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Merge Conflict Detection ⚠️ Warning ❌ Merge conflicts detected (13 files):

⚔️ Makefile (content)
⚔️ config/config.go (content)
⚔️ environment/settings.go (content)
⚔️ go.mod (content)
⚔️ internal/ufs/file.go (content)
⚔️ internal/ufs/fs_unix.go (content)
⚔️ internal/ufs/fs_unix_test.go (content)
⚔️ internal/ufs/walk_unix.go (content)
⚔️ parser/parser.go (content)
⚔️ router/router_server_ws.go (content)
⚔️ router/websocket/listeners.go (content)
⚔️ server/filesystem/filesystem_test.go (content)
⚔️ system/system.go (content)

These conflicts must be resolved before merging into main.
Resolve conflicts locally and push changes to this branch.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'Implement Darwin (MacOS)' accurately reflects the main objective of adding macOS/Darwin support throughout the codebase, including platform-specific implementations for filesystem operations, configuration, and system detection.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch lance/mac
⚔️ Resolve merge conflicts (beta)
  • Auto-commit resolved conflicts to branch lance/mac
  • Create stacked PR with resolved conflicts
  • Post resolved changes as copyable diffs in a comment

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
internal/ufs/readdirmap_darwin_test.go (3)

19-22: Unchecked errors in test setup may produce confusing failures.

If WriteFile or MkdirAll fails (e.g., permissions), the test will fail later with a misleading "expected 5 entries, got N" message instead of a clear setup error. Same pattern appears in the other two tests (lines 75-79, 142-144).

♻️ Suggested fix (apply similarly to other tests)
 		for _, name := range []string{"file1.txt", "file2.txt", "server.jar", "eula.txt"} {
-			os.WriteFile(filepath.Join(tmp, name), []byte("test data"), 0644)
+			if err := os.WriteFile(filepath.Join(tmp, name), []byte("test data"), 0644); err != nil {
+				t.Fatal(err)
+			}
 		}
-		os.MkdirAll(filepath.Join(tmp, "logs"), 0755)
+		if err := os.MkdirAll(filepath.Join(tmp, "logs"), 0755); err != nil {
+			t.Fatal(err)
+		}

47-56: Consider comma-ok form for the type assertion to avoid a panic on failure.

A bare type assertion will panic with a stack trace rather than a clear test failure message. Using the comma-ok form and t.Fatal would make debugging easier if DirEntry ever stops satisfying the Open() interface. Same applies at lines 104 and 174.

♻️ Suggested improvement
-			eO := e.(interface {
-				Open() (File, error)
-			})
+			eO, ok := e.(interface {
+				Open() (File, error)
+			})
+			if !ok {
+				return result{}, fmt.Errorf("entry %s does not implement Open()", e.Name())
+			}

198-203: Nit: strconv.FormatBool(b) does the same thing.

Trivial helper that could be replaced by a stdlib call, but no strong preference.

internal/ufs/walk_dirent_linux.go (1)

1-5: Build tag is broader than the _linux.go filename constraint.

The //go:build unix && !darwin tag would include FreeBSD, OpenBSD, etc., but the _linux.go suffix restricts compilation to Linux only (Go uses filename suffixes as implicit build constraints). The effective constraint is the intersection: Linux only.

If the intent is to cover all non-Darwin Unix platforms, rename the file to e.g. walk_dirent_other.go or walk_dirent_unix.go and keep the build tag. If only Linux is intended, the build tag is redundant.

internal/ufs/walk_unix.go (1)

109-118: Optional refactoring: Remove redundant variable assignments in loop.

With Go 1.24.0, the idx := i assignment is used only once and can be replaced with i directly. The e := e pattern is also unnecessary since loop variables are per-iteration by default and no longer need manual capture.

Suggested simplification
 	out := make([]T, len(entries))
 	for i, e := range entries {
-		idx := i
-		e := e
 		v, err := fn(e)
 		if err != nil {
 			return nil, err
 		}
-		out[idx] = v
+		out[i] = v
 	}
📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3532707 and b4630fa.

📒 Files selected for processing (4)
  • internal/ufs/readdirmap_darwin_test.go
  • internal/ufs/walk_dirent_darwin.go
  • internal/ufs/walk_dirent_linux.go
  • internal/ufs/walk_unix.go
🧰 Additional context used
🧬 Code graph analysis (3)
internal/ufs/walk_dirent_darwin.go (1)
internal/ufs/file.go (2)
  • FileInfo (86-86)
  • O_RDONLY (152-152)
internal/ufs/readdirmap_darwin_test.go (3)
internal/ufs/fs_unix.go (1)
  • NewUnixFS (38-51)
internal/ufs/walk_unix.go (1)
  • ReadDirMap (92-121)
internal/ufs/file.go (1)
  • DirEntry (14-14)
internal/ufs/walk_dirent_linux.go (1)
internal/ufs/file.go (2)
  • FileInfo (86-86)
  • O_RDONLY (152-152)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build and Test (ubuntu-22.04, 1.24.7, linux, amd64)
  • GitHub Check: Analyze (go)
  • GitHub Check: Build and Test (ubuntu-22.04, 1.25.1, linux, amd64)
🔇 Additional comments (7)
internal/ufs/readdirmap_darwin_test.go (3)

1-11: LGTM! Build tag and imports are appropriate for Darwin-specific test coverage.


71-132: Well-structured concurrency test. The WaitGroup + buffered error channel pattern is clean, the buffer size matches the goroutine count, and entry counts are correct (5 files + 2 dirs = 7).


134-196: Good coverage for macOS symlink resolution. This is a valuable test given the /var/private/var symlink on macOS.

internal/ufs/walk_dirent_linux.go (1)

10-17: LGTM — fd-based operations are correct for Linux.

Using Lstatat/OpenFileat with the stored directory fd and entry name is the right approach on Linux where the directory fd remains valid after Getdents.

internal/ufs/walk_unix.go (2)

254-266: LGTM — clean delegation to platform-specific info() and open().

The Info() and Open() methods now delegate to de.info() / de.open(), which are implemented per-platform in the new walk_dirent_*.go files. This cleanly abstracts the Linux vs. Darwin differences.


185-187: Darwin implementation of getdents exists and is correct.

The concern about platform availability is already addressed: getdents is properly implemented in walk_darwin.go using unix.Getdirentries (the macOS equivalent of Linux's unix.Getdents), and both implementations share the same signature. The code will compile correctly on macOS.

internal/ufs/walk_dirent_darwin.go (1)

13-15: Path resolution in de.path is correctly handled via safePath.

In readDir (walk_unix.go:227), de.path is set as a relative path. The implementation correctly anchors these relative paths through safePath:

  • Lstat(de.path)_fstatfs.safePath(name) (fs_unix.go:536–542)
  • OpenFile(de.path, ...)openFilefs.safePath(name) (fs_unix.go:244–247)

The safePath call returns a directory file descriptor and a name relative to that fd, ensuring paths are resolved correctly regardless of the process CWD. This design pattern is the reason the Darwin code path works reliably for nested directory walks and avoids the EBADF issues mentioned in the comment.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@internal/ufs/fs_darwin.go`:
- Around line 40-58: The code in UnixFS.Chtimesat uses linux-style Stat_t fields
Atim/Mtim which don't exist on macOS; replace references to st.Atim and st.Mtim
with Darwin names st.Atimespec and st.Mtimespec and convert their timespecs to
time.Time (e.g., using st.Atimespec.Unix()/UnixNano() or an equivalent
conversion) before assigning atime/mtime so the function compiles and uses the
correct Darwin fields.

In `@internal/ufs/fs_unix.go`:
- Around line 653-660: The field useOpenat2 on UnixFS is written without
synchronization causing a data race; change the UnixFS struct to use an
atomic.Bool for useOpenat2, update all reads to use fs.useOpenat2.Load() and the
write in the open path to fs.useOpenat2.Store(false) (so the branch around
fs._openat2 / fs._openat remains correct), and add the "sync/atomic" import (or
the atomic package alias used) so the race detector is silenced.

In `@internal/ufs/walk_linux.go`:
- Around line 18-33: The defensive fallback in nameFromDirent uses
name[:len(de.Name)] which can panic when ml (derived from de.Reclen) is smaller
than len(de.Name); change the fallback to return the already-sliced name (the
variable name) directly so you never slice past its length—update the fallback
in function nameFromDirent to return name instead of name[:len(de.Name)].

In `@Makefile`:
- Line 24: The cross-build target currently depends on "clean build compress"
but omits "build-darwin" and references a non-existent "compress" target; update
the cross-build dependency list to include build-darwin (so cross-build produces
darwin artifacts) and either add a well-defined compress target (e.g.,
packaging/compression step) or remove "compress" from the dependency list; edit
the Makefile entry for the cross-build target and verify the related targets
"build", "build-darwin", and "clean" are present and correctly implemented.
🧹 Nitpick comments (8)
router/router_server_ws.go (1)

78-78: Log level downgrade for websocket close failure looks intentional and consistent.

This aligns with the similar changes in router/websocket/listeners.go. However, a failed close could occasionally indicate a real issue (e.g., resource leak). Consider using Warn as a middle ground so these don't get completely buried in production, while still not alarming on expected closes.

router/websocket/listeners.go (1)

100-100: Consider keeping Warn for websocket send failures.

Unlike close errors (which are expected during shutdown), a failure to send an event over the websocket may indicate a genuine issue — broken pipe, serialization error, or resource exhaustion. Logging this at Info level risks burying actionable signals in production. Warn would be a more appropriate middle ground.

Proposed change
-		h.Logger().WithField("event", evt).WithField("error", err2).Info("failed to send event over server websocket")
+		h.Logger().WithField("event", evt).WithField("error", err2).Warn("failed to send event over server websocket")
environment/settings.go (1)

120-123: Platform guard for BlkioWeight is reasonable but may be overly conservative.

BlkioWeight is a Linux cgroup feature, so guarding it makes sense for native macOS execution. However, note that Docker Desktop on macOS runs a Linux VM, and the Docker API may accept BlkioWeight there. If users running Wings on macOS with Docker Desktop need IO weight support, this guard would silently skip it.

Consider adding a comment explaining the rationale (e.g., Darwin doesn't support cgroup blkio) so future maintainers understand the intent.

Suggested comment
+	// BlkioWeight is only supported on Linux (cgroup blkio controller).
+	// On macOS, Docker Desktop uses a Linux VM but may not expose this setting.
 	if runtime.GOOS == "linux" {
 		resources.BlkioWeight = l.IoWeight
 	}
Makefile (1)

29-29: Add test to .PHONY declaration.

The static analysis tool correctly flags that test is missing from the .PHONY list. Since test is a valid filename, not declaring it as phony could cause issues if a file named test exists.

Proposed fix
-.PHONY: all build build-darwin compress clean
+.PHONY: all build build-darwin cross-build compress clean test debug rmdebug
macos.md (1)

40-47: Docker socket path may vary by Docker Desktop version.

The path ~/.docker/run/docker.sock is the default for recent Docker Desktop versions, but some installations use /var/run/docker.sock natively or $HOME/.docker/desktop/docker.sock. Consider adding a brief note to verify the actual socket path (e.g., docker context inspect or ls ~/.docker/run/docker.sock) before creating the symlink.

config/config.go (1)

505-515: Darwin user resolution looks correct.

The approach mirrors the existing rootless-mode logic (lines 525–534). Since macOS lacks useradd/adduser, using the current user is the right call.

Note: the user.Current()MustInt(u.Uid)MustInt(u.Gid) pattern is now repeated in three places (darwin, distroless, rootless). Consider extracting a small helper like setCurrentUser() to reduce duplication, but this is not blocking.

♻️ Optional: extract helper to reduce duplication
+// setCurrentUser sets the system user configuration from the current OS user.
+func setCurrentUser() error {
+	u, err := user.Current()
+	if err != nil {
+		return err
+	}
+	_config.System.Username = u.Username
+	_config.System.User.Uid = system.MustInt(u.Uid)
+	_config.System.User.Gid = system.MustInt(u.Gid)
+	return nil
+}

Then in EnsurePelicanUser, the darwin, distroless, and rootless blocks could call setCurrentUser() (with the distroless block keeping its env-var override logic separately).

config/config_openat_linux.go (1)

15-40: Benign race on concurrent first calls to UseOpenat2().

Two goroutines can both observe openat2Set as false and enter the detection path concurrently. Because the syscall result is deterministic for a given kernel, the final cached value will be correct in practice, but the detection work (including the Openat2 syscall + Close) may execute more than once.

A sync.Once would eliminate the redundant work and make the intent clearer. Not a correctness bug given the deterministic nature of the check, so feel free to defer.

internal/ufs/fs_linux.go (1)

58-72: Nit: Sec field in the UTIME_OMIT timespec.

On Line 62, both Sec and Nsec are set to unix.UTIME_OMIT. Per utimensat(2), only the tv_nsec field is inspected for the UTIME_OMIT sentinel — tv_sec is ignored. This works correctly but is slightly misleading. Consider setting Sec: 0 for clarity.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4586d7b and 3a7a902.

📒 Files selected for processing (25)
  • Makefile
  • config/config.go
  • config/config_openat_darwin.go
  • config/config_openat_linux.go
  • config/config_openat_linux_test.go
  • config/config_openat_test.go
  • environment/settings.go
  • internal/ufs/file.go
  • internal/ufs/file_darwin.go
  • internal/ufs/file_linux.go
  • internal/ufs/fs_darwin.go
  • internal/ufs/fs_linux.go
  • internal/ufs/fs_platform_test.go
  • internal/ufs/fs_unix.go
  • internal/ufs/fs_unix_test.go
  • internal/ufs/walk_darwin.go
  • internal/ufs/walk_linux.go
  • internal/ufs/walk_unix.go
  • macos.md
  • router/router_server_ws.go
  • router/websocket/listeners.go
  • server/filesystem/filesystem_test.go
  • server/filesystem/stat_darwin.go
  • server/filesystem/stat_test.go
  • system/system.go
🧰 Additional context used
🧬 Code graph analysis (11)
server/filesystem/stat_darwin.go (1)
server/filesystem/stat.go (1)
  • Stat (14-17)
internal/ufs/file_linux.go (1)
internal/ufs/file_darwin.go (1)
  • O_LARGEFILE (4-4)
internal/ufs/fs_platform_test.go (1)
server/filesystem/stat.go (1)
  • Stat (14-17)
config/config_openat_darwin.go (1)
config/config_openat_linux.go (1)
  • UseOpenat2 (15-41)
config/config.go (2)
system/system.go (1)
  • System (60-67)
system/utils.go (1)
  • MustInt (35-41)
config/config_openat_linux.go (3)
config/config_openat_darwin.go (1)
  • UseOpenat2 (5-7)
config/config.go (1)
  • Get (439-447)
system/system.go (1)
  • System (60-67)
config/config_openat_test.go (3)
config/config.go (3)
  • Set (408-420)
  • Configuration (320-379)
  • SystemConfiguration (138-250)
config/config_openat_darwin.go (1)
  • UseOpenat2 (5-7)
config/config_openat_linux.go (1)
  • UseOpenat2 (15-41)
server/filesystem/stat_test.go (3)
server/filesystem/filesystem_test.go (1)
  • NewFs (20-55)
server/filesystem/stat.go (1)
  • Stat (14-17)
server/filesystem/errors.go (1)
  • Error (25-37)
internal/ufs/fs_unix.go (1)
internal/ufs/file.go (2)
  • O_DIRECTORY (169-169)
  • O_RDONLY (152-152)
internal/ufs/fs_linux.go (3)
internal/ufs/fs_unix.go (1)
  • UnixFS (23-30)
internal/ufs/file.go (1)
  • O_CLOEXEC (172-172)
internal/ufs/file_linux.go (1)
  • O_LARGEFILE (5-5)
internal/ufs/fs_darwin.go (1)
internal/ufs/fs_unix.go (1)
  • UnixFS (23-30)
🪛 checkmake (0.2.2)
Makefile

[warning] 29-29: Missing required phony target "test"

(minphony)

🪛 LanguageTool
macos.md

[style] ~160-~160: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ... not exist on Darwin. - unix.Getdents does not exist on Darwin; `unix.Getdirentrie...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[grammar] ~174-~174: Ensure spelling is correct
Context: ...at_linux.go| OriginalCTime()usingunix.Stat_t.Ctim(was//go:build linux) | | stat_darw...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[grammar] ~175-~175: Ensure spelling is correct
Context: ...at_darwin.go|CTime()handling bothunix.Stat_t.Ctimandsyscall.Stat_t.Ctimespec| gola...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Analyze (go)
  • GitHub Check: Build and Test (ubuntu-22.04, 1.24.7, linux, amd64)
  • GitHub Check: Build and Test (ubuntu-22.04, 1.25.1, linux, amd64)
🔇 Additional comments (31)
server/filesystem/filesystem_test.go (1)

34-37: Good fix for macOS symlink resolution in test setup.

This correctly handles the /var/private/var symlink on macOS, preventing spurious ErrBadPathResolution failures in tests.

router/websocket/listeners.go (1)

30-33: Log level changes for websocket lifecycle events are reasonable.

These log during normal connection teardown, so Info is appropriate.

internal/ufs/file_linux.go (1)

1-5: Clean platform-specific constant definition.

The Linux/Darwin split for O_LARGEFILE is correct: Darwin universally supports large files (hence 0), while Linux needs the explicit flag.

internal/ufs/file.go (1)

172-173: No action needed. AT_EMPTY_PATH is not referenced anywhere in the codebase and is not defined in any platform-specific files, confirming it was safely removed and is not required for the current implementation.

system/system.go (2)

115-129: LGTM — Darwin-specific OS detection looks correct.

Clean branching: Darwin skips osrelease.Read() (which would fail on macOS) and returns a sensible hardcoded value, while the Linux path retains the existing fallback chain.


209-218: LGTM — Early return avoids reading a non-existent file on macOS.

server/filesystem/stat_darwin.go (1)

10-22: The comment and TODO are accurate — Ctim/Ctimespec is status-change time, not creation time.

On macOS, the actual file creation (birth) time is available via Birthtimespec on syscall.Stat_t. Since the TODO already flags this for removal and the Linux counterpart has the same semantics, this is consistent. Just noting for awareness that if the intent ever shifts to truly returning creation time on Darwin, Birthtimespec would be the correct field.

server/filesystem/stat_test.go (2)

9-29: Test looks good but may be fragile if CTime() returns zero time.

On line 23, the test asserts CTime() is non-zero, but the Darwin implementation falls back to time.Time{} if neither unix.Stat_t nor syscall.Stat_t is the underlying type. If a future Go or platform change alters the Sys() return type, this test would fail with a non-obvious error. Consider a more descriptive message or logging st.Sys() type on failure for easier debugging.

That said, this is minor — the current implementations should always match one of the two branches.


31-71: LGTM — Good coverage of JSON marshaling fields.

The test validates the key fields (created, modified, name) and timestamp format. Clean and readable.

macos.md (1)

1-203: Well-written documentation covering both user setup and developer context.

The separation between user-facing setup instructions and developer-focused code change documentation is clear and useful. The explanations for why specific settings are needed (Docker Desktop file sharing paths, machine_id.enable: false, etc.) are particularly helpful.

internal/ufs/file_darwin.go (1)

1-4: LGTM — Correct no-op constant for Darwin.

internal/ufs/fs_unix_test.go (1)

37-40: LGTM — Necessary symlink resolution for macOS test compatibility.

Consistent with the same pattern applied in server/filesystem/filesystem_test.go's NewFs().

internal/ufs/walk_darwin.go (1)

9-18: LGTM — Clean Darwin-specific dirent handling.

Using Namlen directly is the correct approach for Darwin and avoids the NUL-scanning complexity required on Linux. The unsafe.Slice usage is idiomatic for this pattern.

config/config_openat_test.go (1)

8-28: LGTM — reasonable cross-platform smoke test.

Clean setup and the OS-specific assertions are correct: Darwin's UseOpenat2() always returns false, and Linux's result is kernel-dependent.

One minor note: on Linux, if TestUseOpenat2ConfigOverride (in config_openat_linux_test.go) runs first, the atomic cache (openat2Set) will already be populated, so this test won't actually exercise the "auto" detection path — it'll return the cached value. Since the stated goal is just "doesn't panic," that's fine, but worth being aware of if you ever want stronger coverage of the auto path in isolation.

config/config_openat_darwin.go (1)

1-7: LGTM!

Clean platform stub. The comment accurately explains why openat2 is unavailable on Darwin.

config/config.go (1)

804-807: LGTM — prevents osrelease.Read() failure on macOS.

/etc/os-release doesn't exist on macOS, so this early return is necessary to avoid a runtime error.

config/config_openat_linux_test.go (1)

5-25: LGTM — clean config override test.

Properly resets the atomic cache before each assertion, ensuring the config mode is re-evaluated. Both forced "openat" and "openat2" paths are covered.

internal/ufs/fs_unix.go (3)

36-49: LGTM — symlink resolution in base path is essential for macOS.

On macOS, /var is a symlink to /private/var, so without this resolution, the unsafeIsPathInsideOfBase prefix check would fail for any fd whose resolved path goes through /private/var. Silently ignoring EvalSymlinks errors is reasonable here — the path may not exist yet during initialization.


682-683: Good: fdPath(fd) replaces the Linux-specific /proc/self/fd approach.

This is the correct abstraction for cross-platform fd-to-path resolution (Linux uses /proc/self/fd, Darwin uses fcntl(F_GETPATH)).


762-762: Correct: AT_FDCWD is the right choice for opening an absolute path.

Using AT_FDCWD with the absolute fs.basePath is semantically correct and portable across Unix platforms.

internal/ufs/fs_platform_test.go (4)

15-53: LGTM — clean stat validation test.

Covers the essential fields (name, size, directory flag, mod time) through the UnixFS abstraction.


55-88: Good sandbox escape test.

This exercises the critical security invariant that symlinks pointing outside the sandbox root are blocked. Using os.Symlink directly (bypassing UnixFS) correctly simulates an external actor creating the escape link.


90-130: Test depends on timing — consider a more robust assertion.

The 50ms sleep + 100ms tolerance window is reasonable for CI but could be flaky on heavily loaded machines where the filesystem timestamp granularity is coarse (e.g., some filesystems have 1-second or 2-second granularity like HFS+, which is relevant for macOS).

If the test target filesystem on macOS uses APFS (nanosecond granularity), this should be fine. But if run on HFS+ volumes, origMtime and the post-Chtimes mtime could both round to the same second, making the test pass vacuously (not actually verifying preservation). Worth noting but not blocking.


132-145: LGTM — validates platform-specific constant.

Correctly asserts O_LARGEFILE is non-zero on Linux (from unix.O_LARGEFILE) and zero on Darwin (where large file support is default).

internal/ufs/walk_unix.go (1)

187-187: LGTM — clean delegation to platform-specific getdents wrapper.

This change properly delegates OS-specific directory entry reading to the walk_linux.go and walk_darwin.go implementations, with both getdents and nameFromDirent correctly defined in each platform file.

config/config_openat_linux.go (1)

10-13: LGTM on the atomic caching variables.

The two package-level atomic booleans cleanly separate the "is set" flag from the "value" flag.

internal/ufs/fs_linux.go (2)

13-14: LGTM — standard /proc/self/fd approach for Linux.


23-54: LGTM — solid openat2 wrapper with proper flag defaults and RESOLVE_BENEATH.

The EINTR/EAGAIN pass-through without ensurePathError wrapping is correct, as callers will retry the operation.

internal/ufs/fs_darwin.go (2)

15-27: LGTM — correct use of F_GETPATH with runtime.KeepAlive to prevent premature GC of the buffer.


32-34: LGTM — clean stub returning ENOSYS.

internal/ufs/walk_linux.go (1)

10-13: LGTM — thin wrapper over Getdents.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

On Darwin, unix.Getdirentries is a user-space simulation that can leave
the directory fd in a bad state for subsequent fstatat calls. Use
path-based Lstat/OpenFile on Darwin instead of dirfd-based Lstatat/
OpenFileat. Also fix readDir setting dirent.path to the directory name
instead of the child entry name for root-level entries.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant