Skip to content

Conversation

redbeam
Copy link
Contributor

@redbeam redbeam commented Aug 6, 2025

Note

Together with this PR, we also need to merge
crc-org/snc#1123 to add the 9pfs client to the bundle.


This adds 9p file sharing support as discussed in #4858.

Summary by CodeRabbit

  • New Features

    • Windows hosts can expose user home via a built-in 9P file server; guests can mount host dirs using a new "9p" shared-dir option alongside existing types. 9p shares are created in addition to CIFS for input directories.
  • Bug Fixes

    • More robust listener/address handling for network bindings.
    • Improved mount error handling to mask secrets and ensure proper pre-mount ownership on 9p mounts.
  • Chores

    • Vendored a cross-platform 9P implementation (client/server/protocol), utilities, and docs; Windows 9P server lifecycle is started and monitored during initialization.

@redbeam redbeam self-assigned this Aug 6, 2025
@coderabbitai
Copy link

coderabbitai bot commented Aug 6, 2025

Walkthrough

Adds a vendored 9P implementation and integrates Plan9 (9P) sharing: new fs9p server, vendored p9/proto client-server/FS code, constants and go.mod dependency, Windows daemon logic to serve host home over TCP:564, guest-side mount support, and driver changes to advertise 9P shares.

Changes

Cohort / File(s) Summary
Daemon & constants
cmd/crc/cmd/daemon.go, pkg/crc/constants/constants.go, go.mod
Use net.JoinHostPort for listener addresses; add Plan9Msize and Plan9TcpPort; vendor github.com/DeedleFake/p9 dependency; on Windows create TCP listener on GatewayIP:564 and start/monitor a fs9p server serving host home, stopping it on shutdown.
Windows driver: share advertising
pkg/crc/machine/libhvee/driver_windows.go
Advertise both CIFS and a corresponding 9p share per input directory by appending a Type: "9p" sharedDir with computed Target.
VM mount logic
pkg/crc/machine/start.go
Add handling for shared-dir type "9p": chown mount target to core:core and mount via 9pfs (VSock gateway); preserve existing virtiofs/cifs paths and mask CIFS mount secrets on error.
fs9p server
pkg/fileserver/fs9p/server.go
New fs9p.Server type with New9pServer, Start(), Stop(), WaitForError(); validates expose dir, builds vendored p9 FileSystem, serves over provided net.Listener using vendored proto handlers and Plan9Msize.
Vendored 9P library (full)
All under vendor/github.com/DeedleFake/p9/..., vendor/modules.txt, vendor/.../LICENSE, vendor/.../README.md
Add a complete vendored 9P implementation: core types/constants, message encodings, proto framing/server/client, FS handler and Dir backend (platform-specific files), Client/Remote APIs, internal util/debug, docs, and MIT license. New public APIs include Client, Remote, FileSystem/Attachment/File interfaces, Stat/DirEntry, and proto handlers.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant User
  participant Daemon
  participant FS as fs9p Server
  Note over Daemon: daemon startup (cross-platform listeners)
  User->>Daemon: start daemon
  Daemon->>Daemon: bind listeners (net.JoinHostPort)
  alt Windows
    Daemon->>FS: listen TCP on GatewayIP:564
    FS-->>Daemon: Start() (serving host home)
    Daemon->>FS: monitor WaitForError() (background)
    Daemon->>FS: Stop() on shutdown
  end
  Daemon-->>User: signal startupDone
Loading
sequenceDiagram
  autonumber
  participant Starter as VM Starter
  participant VM
  participant HostFS as Host fs9p Server
  Starter->>Starter: advertise shares (CIFS + 9P)
  Starter->>VM: prepare mount target (chown core:core)
  VM->>HostFS: TCP connect to GatewayIP:564 (or VSock gateway)
  VM->>HostFS: 9P handshake & attach
  HostFS-->>VM: serve filesystem ops (read/write/readdir)
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Poem

I’m a rabbit building tunnels neat,
Ports and paths where host and guest shall meet.
Nine little doors on TCP they hum,
Home-carrots shared, one byte at a time they come.
Hop, mount, and nibble — files cross the glen. 🐰✨

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The pull request description is largely incomplete compared to the required template. While it does provide a basic description referencing issue #4858 and noting that 9p file sharing support is being added, it fails to include most required template sections. Specifically, the "Type of change" checkboxes, "Proposed changes" list, "Testing" section, and "Contribution Checklist" are entirely absent. Only the core Description section is minimally filled, making the submission incomplete and non-compliant with the repository's contribution guidelines.
Docstring Coverage ⚠️ Warning Docstring coverage is 64.86% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The title "9p file sharing" clearly and concisely describes the main purpose of the pull request. It directly corresponds to the primary changes in the changeset, which involve adding support for 9p file sharing across multiple components (daemon initialization, fs9p server implementation, Windows-specific 9p shares, and shared directory handling). The title is specific enough to convey the key change while remaining brief and readable.
✨ 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 9p-deedlefake

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.

@openshift-ci
Copy link

openshift-ci bot commented Aug 6, 2025

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign anjannath for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci bot requested review from albfan and evidolob August 6, 2025 08:57
@redbeam redbeam moved this to Ready for review in Project planning: crc Aug 6, 2025
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: 24

🧹 Nitpick comments (14)
vendor/github.com/DeedleFake/p9/internal/util/util.go (1)

44-52: Consider edge cases in error handling

The Errorf function only checks for io.EOF in the special handling. Consider if other common error types (like nil) should also be handled directly.

Apply this diff to handle nil errors as well:

 func Errorf(str string, args ...interface{}) error {
 	for _, arg := range args {
+		if arg == nil {
+			return nil
+		}
 		if arg == io.EOF {
 			return arg.(error)
 		}
 	}
vendor/github.com/DeedleFake/p9/internal/debug/nodebug.go (1)

1-5: Consider updating to modern build constraint syntax

The implementation is correct and follows the standard pattern for conditional compilation. However, consider updating to the modern build constraint syntax for consistency with Go 1.17+.

Apply this diff to use modern build constraint syntax:

-// +build !p9debug
+//go:build !p9debug

The functionality remains identical, but the new syntax is preferred in modern Go projects.

vendor/github.com/DeedleFake/p9/addr_unix.go (1)

17-29: Consider consistency with other platform implementation.

The implementation is functionally correct, but there's an inconsistency with addr_other.go which uses os.TempDir() instead of hardcoded "/tmp".

For consistency, consider using os.TempDir():

-	return filepath.Join("/", "tmp", "ns."+u.Username+"."+display)
+	return filepath.Join(os.TempDir(), "ns."+u.Username+"."+display)

However, if the hardcoded "/tmp" is intentional for Unix systems (following 9p conventions), this should be documented in a comment.

vendor/github.com/DeedleFake/p9/README.md (1)

12-51: Address markdown formatting consistency.

The documentation is well-written and provides clear examples, but there are some formatting inconsistencies flagged by static analysis.

Consider fixing the markdown formatting issues:

  1. Replace hard tabs with spaces in code blocks (lines 17, 26, 32, 37, 43, 49)
  2. Use consistent heading style (either ATX # or Setext underline style)

These are minor formatting issues that don't affect functionality but improve consistency.

vendor/github.com/DeedleFake/p9/doc.go (1)

40-40: Update deprecated ioutil.ReadAll in documentation example.

The example uses ioutil.ReadAll which has been deprecated since Go 1.16. Consider updating to io.ReadAll for better practices in documentation examples.

-//    buf, _ := ioutil.ReadAll(file)
+//    buf, _ := io.ReadAll(file)
vendor/github.com/DeedleFake/p9/dir_linux.go (1)

57-60: Consider dynamic QID version handling.

The QID version is hardcoded to 0, which may not reflect actual file changes. In 9P, the version field typically changes when file content is modified to invalidate client caches.

vendor/github.com/DeedleFake/p9/dir_darwin.go (1)

57-60: Consider dynamic QID version handling.

Same as the Linux implementation, the QID version is hardcoded to 0. Consider implementing proper version tracking for file change detection.

vendor/github.com/DeedleFake/p9/encoding.go (1)

16-30: Consider memory efficiency for large directories.

The function reads all directory entries into memory at once, which could be problematic for very large directories. The recommendation about buffering is good, but consider also documenting memory implications.

Add a comment about memory usage:

 // ReadDir decodes a series of directory entries from a reader. It
 // reads until EOF, so it doesn't return io.EOF as a possible error.
 //
+// Note: This function loads all directory entries into memory. For very
+// large directories, consider implementing streaming or batched reading.
+//
 // It is recommended that the reader passed to ReadDir have some form
pkg/fileserver/fs9p/shares.go (1)

46-53: Add context support for graceful shutdown.

The error monitoring goroutine runs indefinitely. Consider adding context support for graceful shutdown when the application terminates.

-		go func() {
+		go func(server *Server, serverDir string, addr string) {
 			if err := server.WaitForError(); err != nil {
-				logrus.Errorf("Error from 9p server on %s for %s: %v", m.Listener.Addr().String(), serverDir, err)
+				logrus.Errorf("Error from 9p server on %s for %s: %v", addr, serverDir, err)
 			} else {
 				// We do not expect server exits - this should run until the program exits.
-				logrus.Warnf("9p server on %s for %s exited without error", m.Listener.Addr().String(), serverDir)
+				logrus.Warnf("9p server on %s for %s exited without error", addr, serverDir)
 			}
-		}()
+		}(server, serverDir, m.Listener.Addr().String())

This fixes variable capture issues and makes the goroutine parameters explicit.

vendor/github.com/DeedleFake/p9/proto/client.go (1)

138-145: Potential infinite loop with tag exhaustion

If all possible tag values (0-65535) are in use, the tag allocation loop will run forever. While unlikely, this could cause a deadlock in high-concurrency scenarios.

Consider adding a maximum iteration limit:

 case c.nextTag <- nextTag:
+    iterations := 0
     for {
         nextTag++
+        iterations++
+        if iterations > 65536 {
+            // All tags exhausted
+            break
+        }
         if _, ok := tags[nextTag]; !ok {
             break
         }
     }
vendor/github.com/DeedleFake/p9/remote.go (1)

273-275: Unusual use of io.EOF for partial writes

Returning io.EOF when the write count is less than the data length is non-standard. Short writes typically don't indicate EOF in Go's io conventions.

Consider using io.ErrShortWrite or a custom error:

 if write.Count < uint32(len(data)) {
-    return int(write.Count), io.EOF
+    return int(write.Count), io.ErrShortWrite
 }
vendor/github.com/DeedleFake/p9/proto/server.go (1)

19-22: Consider handling temporary network errors gracefully

The server terminates on any accept error, but some errors might be temporary (e.g., EMFILE, ENFILE). Consider checking if the error is temporary and implementing a backoff strategy to keep the server running.

 for {
   c, err := lis.Accept()
   if err != nil {
+    // Check if this is a temporary error
+    if ne, ok := err.(net.Error); ok && ne.Temporary() {
+      log.Printf("Temporary accept error: %v", err)
+      time.Sleep(time.Millisecond * 10)
+      continue
+    }
     return err
   }
vendor/github.com/DeedleFake/p9/dir.go (1)

95-103: Hardcoded file permissions in Open method

The Open method uses hardcoded 0644 permissions when opening files (line 99). While this parameter is typically ignored for existing files, it would be better to use 0 or get the actual file permissions.

 func (d Dir) Open(p string, mode uint8) (File, error) {
   flag := toOSFlags(mode)
 
-  file, err := os.OpenFile(d.path(p), flag, 0644)
+  file, err := os.OpenFile(d.path(p), flag, 0)
   return &dirFile{
     File: file,
   }, err
 }
vendor/github.com/DeedleFake/p9/msg.go (1)

145-145: Consider implementing a proper Mode type as indicated by TODOs

The TODO comments indicate that Mode fields should have a proper type instead of using raw uint8. This would improve type safety and make the code more maintainable.

Would you like me to create an issue to track the implementation of a proper Mode type for these fields?

Also applies to: 157-157

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2b90bf2 and 74bc69b.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (34)
  • cmd/crc/cmd/daemon.go (4 hunks)
  • go.mod (2 hunks)
  • pkg/crc/constants/constants.go (1 hunks)
  • pkg/crc/machine/libhvee/driver_windows.go (1 hunks)
  • pkg/crc/machine/start.go (2 hunks)
  • pkg/fileserver/fs9p/server.go (1 hunks)
  • pkg/fileserver/fs9p/shares.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/LICENSE (1 hunks)
  • vendor/github.com/DeedleFake/p9/README.md (1 hunks)
  • vendor/github.com/DeedleFake/p9/addr.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/addr_other.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/addr_unix.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/client.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_darwin.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_linux.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_other.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_plan9.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_windows.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/doc.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/encoding.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/fs.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/internal/debug/debug.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/internal/debug/nodebug.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/internal/util/util.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/msg.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/p9.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/proto/client.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/proto/encoding.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/proto/proto.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/proto/server.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/remote.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/stat.go (1 hunks)
  • vendor/modules.txt (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (19)
vendor/github.com/DeedleFake/p9/internal/debug/debug.go (1)
vendor/github.com/DeedleFake/p9/internal/debug/nodebug.go (1)
  • Log (5-5)
vendor/github.com/DeedleFake/p9/internal/debug/nodebug.go (1)
vendor/github.com/DeedleFake/p9/internal/debug/debug.go (1)
  • Log (10-12)
vendor/github.com/DeedleFake/p9/dir_other.go (1)
vendor/github.com/DeedleFake/p9/stat.go (3)
  • DirEntry (255-267)
  • FileMode (23-23)
  • ModeFromOS (43-78)
cmd/crc/cmd/daemon.go (2)
pkg/crc/constants/constants.go (2)
  • Plan9Port (59-59)
  • GetHomeDir (164-170)
pkg/fileserver/fs9p/shares.go (2)
  • StartShares (26-57)
  • Mount9p (13-16)
vendor/github.com/DeedleFake/p9/addr.go (2)
vendor/github.com/DeedleFake/p9/addr_other.go (1)
  • NamespaceDir (17-29)
vendor/github.com/DeedleFake/p9/addr_unix.go (1)
  • NamespaceDir (17-29)
vendor/github.com/DeedleFake/p9/encoding.go (2)
vendor/github.com/DeedleFake/p9/stat.go (2)
  • DirEntry (255-267)
  • Stat (156-168)
vendor/github.com/DeedleFake/p9/proto/encoding.go (2)
  • Read (34-38)
  • Write (25-31)
vendor/github.com/DeedleFake/p9/client.go (4)
vendor/github.com/DeedleFake/p9/proto/client.go (3)
  • Client (18-31)
  • NewClient (35-56)
  • Dial (60-67)
vendor/github.com/DeedleFake/p9/msg.go (8)
  • Proto (75-77)
  • Tversion (79-82)
  • Tversion (84-84)
  • Rversion (86-89)
  • Tauth (95-99)
  • Rauth (101-103)
  • Tattach (105-110)
  • Rattach (112-114)
vendor/github.com/DeedleFake/p9/p9.go (3)
  • Version (10-10)
  • NoFID (18-18)
  • QID (42-46)
vendor/github.com/DeedleFake/p9/remote.go (1)
  • Remote (19-27)
vendor/github.com/DeedleFake/p9/proto/encoding.go (1)
vendor/github.com/DeedleFake/p9/internal/util/util.go (1)
  • Errorf (44-52)
vendor/github.com/DeedleFake/p9/addr_other.go (1)
vendor/github.com/DeedleFake/p9/addr_unix.go (1)
  • NamespaceDir (17-29)
pkg/crc/machine/start.go (1)
pkg/os/exec.go (1)
  • RunPrivileged (48-59)
pkg/crc/machine/libhvee/driver_windows.go (1)
vendor/github.com/crc-org/machine/libmachine/drivers/base.go (1)
  • SharedDir (27-35)
vendor/github.com/DeedleFake/p9/p9.go (1)
vendor/github.com/DeedleFake/p9/stat.go (1)
  • FileMode (23-23)
vendor/github.com/DeedleFake/p9/addr_unix.go (1)
vendor/github.com/DeedleFake/p9/addr_other.go (1)
  • NamespaceDir (17-29)
vendor/github.com/DeedleFake/p9/dir_windows.go (1)
vendor/github.com/DeedleFake/p9/stat.go (3)
  • DirEntry (255-267)
  • FileMode (23-23)
  • ModeFromOS (43-78)
vendor/github.com/DeedleFake/p9/remote.go (6)
vendor/github.com/DeedleFake/p9/client.go (1)
  • Client (22-26)
vendor/github.com/DeedleFake/p9/p9.go (4)
  • QID (42-46)
  • QIDType (49-49)
  • Version (10-10)
  • IOHeaderSize (70-70)
vendor/github.com/DeedleFake/p9/msg.go (14)
  • Twalk (133-137)
  • Rwalk (139-141)
  • Topen (143-146)
  • Ropen (148-151)
  • Tcreate (153-158)
  • Rcreate (160-163)
  • Tremove (192-194)
  • Tread (165-169)
  • Rread (171-173)
  • Twrite (175-179)
  • Rwrite (181-183)
  • Tclunk (185-187)
  • Tstat (199-201)
  • Rstat (203-205)
vendor/github.com/DeedleFake/p9/stat.go (3)
  • FileMode (23-23)
  • Stat (156-168)
  • DirEntry (255-267)
vendor/github.com/DeedleFake/p9/internal/util/util.go (1)
  • Errorf (44-52)
vendor/github.com/DeedleFake/p9/encoding.go (1)
  • ReadDir (16-30)
vendor/github.com/DeedleFake/p9/dir_plan9.go (3)
vendor/github.com/DeedleFake/p9/stat.go (3)
  • DirEntry (255-267)
  • FileMode (23-23)
  • ModeFromOS (43-78)
vendor/github.com/DeedleFake/p9/dir.go (1)
  • Dir (16-16)
vendor/github.com/DeedleFake/p9/p9.go (2)
  • QID (42-46)
  • Version (10-10)
vendor/github.com/DeedleFake/p9/msg.go (6)
vendor/github.com/DeedleFake/p9/proto/proto.go (2)
  • NewProto (44-60)
  • Proto (38-41)
vendor/github.com/DeedleFake/p9/p9.go (2)
  • Version (10-10)
  • QID (42-46)
vendor/github.com/DeedleFake/p9/proto/client.go (1)
  • P9NoTag (221-223)
vendor/github.com/DeedleFake/p9/stat.go (3)
  • FileMode (23-23)
  • Stat (156-168)
  • ErrLargeStat (18-18)
vendor/github.com/DeedleFake/p9/proto/encoding.go (2)
  • Write (25-31)
  • Read (34-38)
vendor/github.com/DeedleFake/p9/internal/util/util.go (1)
  • LimitedReader (13-17)
vendor/github.com/DeedleFake/p9/proto/server.go (2)
vendor/github.com/DeedleFake/p9/msg.go (1)
  • Proto (75-77)
vendor/github.com/DeedleFake/p9/proto/proto.go (1)
  • Proto (38-41)
vendor/github.com/DeedleFake/p9/dir.go (3)
vendor/github.com/DeedleFake/p9/stat.go (4)
  • Stat (156-168)
  • DirEntry (255-267)
  • StatChanges (316-318)
  • FileMode (23-23)
vendor/github.com/DeedleFake/p9/fs.go (3)
  • File (100-119)
  • Attachment (44-74)
  • FileSystem (21-34)
vendor/github.com/DeedleFake/p9/p9.go (7)
  • OWRITE (27-27)
  • ORDWR (28-28)
  • OEXEC (29-29)
  • OTRUNC (31-31)
  • OCEXEC (32-32)
  • ORCLOSE (33-33)
  • OREAD (26-26)
🪛 markdownlint-cli2 (0.17.2)
vendor/github.com/DeedleFake/p9/README.md

12-12: Heading style
Expected: setext; Actual: atx

(MD003, heading-style)


17-17: Hard tabs
Column: 1

(MD010, no-hard-tabs)


21-21: Heading style
Expected: setext; Actual: atx

(MD003, heading-style)


26-26: Hard tabs
Column: 1

(MD010, no-hard-tabs)


32-32: Hard tabs
Column: 1

(MD010, no-hard-tabs)


37-37: Hard tabs
Column: 1

(MD010, no-hard-tabs)


43-43: Hard tabs
Column: 1

(MD010, no-hard-tabs)


49-49: Hard tabs
Column: 1

(MD010, no-hard-tabs)

⏰ 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). (18)
  • GitHub Check: build (ubuntu-latest, 1.23)
  • GitHub Check: build (macOS-13, 1.23)
  • GitHub Check: build (macOS-14, 1.23)
  • GitHub Check: build (windows-2022, 1.23)
  • GitHub Check: build (windows-2022, 1.23)
  • GitHub Check: build-qe (windows, amd64)
  • GitHub Check: verify-devcontainer
  • GitHub Check: build (macOS-14, 1.23)
  • GitHub Check: build (macOS-13, 1.23)
  • GitHub Check: build-qe (linux, arm64)
  • GitHub Check: build (ubuntu-22.04, 1.23)
  • GitHub Check: build (ubuntu-latest, 1.23)
  • GitHub Check: build-qe (darwin, amd64)
  • GitHub Check: Run OKD bundle with crc (1.23)
  • GitHub Check: build-qe (linux, amd64)
  • GitHub Check: build-qe (darwin, arm64)
  • GitHub Check: build-installer (windows-2022, 1.23)
  • GitHub Check: Konflux kflux-prd-rh02 / crc-binary-on-pull-request
🔇 Additional comments (34)
vendor/github.com/DeedleFake/p9/internal/util/util.go (2)

8-17: LGTM - Well-documented LimitedReader implementation

The type definition and documentation clearly explain the differences from the standard io.LimitedReader. The use of uint32 for N is beneficial for larger sizes on 32-bit systems, and the custom error field E provides better error handling flexibility.


27-39: Ignore boundary‐condition tests for vendored code

This file lives under vendor/… and is an upstream dependency. We don’t add or maintain tests for vendored packages in our repo—any fixes or additional coverage should be contributed back to the original DeedleFake/p9 project. Please disregard this request here.

Likely an incorrect or invalid review comment.

vendor/modules.txt (1)

13-18: Dependency update verified: p9 v0.6.12 is up-to-date and secure

I’ve confirmed that v0.6.12 is the latest release of github.com/DeedleFake/p9 (as of August 2025) and no publicly reported security vulnerabilities affect this version. No changes are needed.

pkg/crc/constants/constants.go (1)

59-60: LGTM - Appropriate 9P protocol constants

The constants are well-chosen:

  • Port 564 is the standard IANA-assigned port for the 9P protocol
  • 1MB message size (Plan9Msize) provides a good balance between performance and memory usage
vendor/github.com/DeedleFake/p9/LICENSE (1)

1-22: LGTM - Standard MIT License

The MIT License is permissive and compatible with most projects. The license text is standard and complete.

vendor/github.com/DeedleFake/p9/internal/debug/debug.go (1)

10-12: LGTM! Clean debug logging implementation.

The conditional compilation approach with build tags is well-implemented, providing debug logging only when needed while maintaining a no-op version for production builds.

go.mod (1)

7-7: LGTM! Dependencies appropriately added for 9p support.

The addition of github.com/DeedleFake/p9 v0.6.12 and promotion of github.com/linuxkit/virtsock to a direct dependency aligns with the 9p file sharing functionality being introduced.

Also applies to: 34-34

pkg/crc/machine/start.go (1)

262-269: 9p mount configuration verified

The hardcoded gateway IP (192.168.127.1) matches constants.VSockGateway in pkg/crc/constants/constants.go and the 9pfs command is the correct Plan 9 filesystem client (port 564). Approving these changes as-is.

vendor/github.com/DeedleFake/p9/addr_other.go (1)

17-29: LGTM! Proper cross-platform namespace directory implementation.

The use of os.TempDir() instead of hardcoded /tmp is appropriate for non-Unix platforms, while maintaining consistent behavior with the Unix version for DISPLAY handling and error conditions.

vendor/github.com/DeedleFake/p9/dir_other.go (1)

7-14: LGTM! Appropriate minimal fallback implementation.

This provides essential file metadata conversion for platforms without specific implementations. The missing fields (UID, GID, ATime, etc.) are reasonable omissions for a fallback version.

cmd/crc/cmd/daemon.go (2)

27-27: LGTM - Clean integration of fs9p package.

The import is appropriately placed and follows the existing import structure.


181-181: LGTM - Improved address construction.

Using net.JoinHostPort provides better IPv6 compatibility and cleaner code compared to string concatenation.

Also applies to: 197-197

vendor/github.com/DeedleFake/p9/dir_windows.go (1)

9-27: LGTM - Well-implemented platform-specific file metadata handling.

The implementation correctly handles Windows-specific file attributes while providing a graceful fallback. The type assertion is properly checked, and the timestamp conversion from Windows FILETIME to Go time.Time is accurate.

vendor/github.com/DeedleFake/p9/dir_linux.go (1)

12-44: LGTM! Robust Linux-specific file metadata handling.

The infoToEntry function properly handles both cases where syscall information is available and when it's not, with graceful fallback for user/group name resolution.

vendor/github.com/DeedleFake/p9/dir_darwin.go (1)

12-44: LGTM! Platform-appropriate Darwin file metadata handling.

The implementation correctly uses Darwin-specific syscall.Stat_t fields, particularly sys.Atimespec.Unix() for access time, which differs appropriately from the Linux implementation.

vendor/github.com/DeedleFake/p9/dir_plan9.go (2)

10-31: Excellent native Plan 9 implementation.

This implementation leverages Plan 9's native understanding of 9P concepts, directly accessing UID/GID as strings and using the native syscall.Dir structure. This is cleaner than the user lookup required on other platforms.


44-48: Perfect QID handling for Plan 9.

Unlike other platforms, this correctly uses the actual QID information from the filesystem (sys.Qid.Type, sys.Qid.Vers, sys.Qid.Path) rather than constructing it. This provides proper version tracking and is the reference implementation other platforms should aspire to.

vendor/github.com/DeedleFake/p9/addr.go (2)

32-66: Comprehensive address parsing with good edge case handling.

The ParseAddr function handles multiple 9P address formats correctly, including namespace addresses, Unix sockets, TCP with special port mappings, and bang-separated network specifications. The logic flow is well-structured with appropriate fallbacks.


70-72: Confirm panic behavior in NamespaceDir

We verified that both implementations of NamespaceDir (addr_other.go and addr_unix.go) will panic if user.Current() returns an error, and that GetNamespace simply calls it without recovery. As a result, any failure in user lookup will crash the process when ParseAddr is given a “$name” address.

Points to address:

  • vendor/github.com/DeedleFake/p9/addr_other.go, addr_unix.go: NamespaceDir() panics on user.Current() error.
  • vendor/github.com/DeedleFake/p9/addr.go: GetNamespace(name) joins NamespaceDir() without error handling.

Action items:

  • Decide whether a hard panic here is acceptable in your environment (e.g., user.Current should never fail on your target platforms).
  • If not, refactor NamespaceDir to return (string, error), update GetNamespace (and ParseAddr) to propagate errors, and handle them where ParseAddr is invoked.
  • At minimum, document this panic behavior clearly so callers know to avoid or catch it.
pkg/fileserver/fs9p/server.go (1)

26-37: LGTM - Proper input validation for security.

The function correctly validates that the exposed directory path is absolute, exists, and is actually a directory. This prevents common security issues like relative path traversal attacks.

vendor/github.com/DeedleFake/p9/encoding.go (1)

32-42: LGTM - Clean error handling.

The WriteDir function has appropriate error handling and follows Go conventions for early return on error.

vendor/github.com/DeedleFake/p9/p9.go (3)

7-11: LGTM - Standard 9P protocol version.

Using "9P2000" as the version string is correct according to the 9P protocol specification.


63-66: LGTM - FileMode conversion follows specification.

The left shift by 24 bits to convert QIDType to FileMode follows the standard 9P convention where QID type bits are stored in the upper byte of the mode field.


52-61: QIDType constants correctly match the 9P2000 spec
The current bit-shift pattern produces exactly the defined values (0x00, 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02), so no changes are needed.

vendor/github.com/DeedleFake/p9/client.go (1)

49-69: LGTM - Proper handshake implementation.

The handshake method correctly implements the 9P version negotiation protocol, including version validation and msize configuration.

pkg/fileserver/fs9p/shares.go (1)

59-82: LGTM - Clean VSOCK integration.

The VSOCK share functionality properly converts GUIDs to listeners and delegates to the standard share management. The error handling is appropriate.

vendor/github.com/DeedleFake/p9/proto/encoding.go (2)

13-20: LGTM!

The Size function correctly calculates message size using the encoder in size mode.


22-31: LGTM!

The Write function properly encodes and writes messages without buffering, as documented.

vendor/github.com/DeedleFake/p9/fs.go (1)

297-346: Well-implemented walk handler

The walk implementation correctly handles partial walks according to the 9P specification, properly distinguishing between first-element failures and subsequent failures.

vendor/github.com/DeedleFake/p9/remote.go (2)

34-71: Well-implemented walk function

The walk implementation correctly handles path traversal and properly marks incomplete walks with an invalid QID.


229-249: Correct implementation of chunked reading

The ReadAt implementation properly handles message size limits by splitting large reads into multiple requests, with appropriate error handling.

vendor/github.com/DeedleFake/p9/proto/proto.go (2)

44-60: Well-designed protocol mapping initialization

The NewProto function correctly creates bidirectional mappings and properly handles both pointer and non-pointer types.


125-171: Efficient buffered sending with proper resource management

The Send implementation correctly uses buffer pooling and ensures atomic message writes with proper cleanup in all code paths.

vendor/github.com/DeedleFake/p9/proto/server.go (1)

81-92: Incorrect msize duplicate check logic

The warning for setting msize twice (line 83) is inside the sync.Once.Do() callback, which only executes once. The check should be outside the Do() call.

 if rmsg, ok := rmsg.(Msizer); ok {
-  if msize > 0 {
-    log.Println("Warning: Attempted to set msize twice.")
-  }
-
   setter.Do(func() {
     msize = rmsg.P9Msize()
     mode = func(f func()) {
       go f()
     }
   })
+  if msize > 0 && msize != rmsg.P9Msize() {
+    log.Printf("Warning: Attempted to set msize twice. Current: %d, Attempted: %d", msize, rmsg.P9Msize())
+  }
 }

Likely an incorrect or invalid review comment.

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: 1

♻️ Duplicate comments (1)
pkg/fileserver/fs9p/shares.go (1)

25-57: The error cleanup issue identified in the previous review still needs to be addressed.

The defer function inside the loop still captures defErr by closure and creates multiple defers that reference the same server variable, leading to unreliable cleanup on partial failure.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 74bc69b and e6c485d.

📒 Files selected for processing (6)
  • cmd/crc/cmd/daemon.go (4 hunks)
  • pkg/crc/constants/constants.go (1 hunks)
  • pkg/crc/machine/libhvee/driver_windows.go (1 hunks)
  • pkg/crc/machine/start.go (2 hunks)
  • pkg/fileserver/fs9p/server.go (1 hunks)
  • pkg/fileserver/fs9p/shares.go (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • pkg/crc/constants/constants.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • pkg/crc/machine/libhvee/driver_windows.go
  • cmd/crc/cmd/daemon.go
  • pkg/crc/machine/start.go
  • pkg/fileserver/fs9p/server.go
🧰 Additional context used
🪛 GitHub Check: build (macOS-14, 1.23)
pkg/fileserver/fs9p/shares.go

[failure] 69-69:
undefined: hvsock.Listen

⏰ 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). (17)
  • GitHub Check: build (windows-2022, 1.23)
  • GitHub Check: build (ubuntu-latest, 1.23)
  • GitHub Check: build (ubuntu-22.04, 1.23)
  • GitHub Check: build (macOS-13, 1.23)
  • GitHub Check: build-installer (windows-2022, 1.23)
  • GitHub Check: verify-devcontainer
  • GitHub Check: build (windows-2022, 1.23)
  • GitHub Check: build-qe (darwin, amd64)
  • GitHub Check: build (ubuntu-latest, 1.23)
  • GitHub Check: Run OKD bundle with crc (1.23)
  • GitHub Check: build-qe (linux, arm64)
  • GitHub Check: build-qe (darwin, arm64)
  • GitHub Check: build-qe (windows, amd64)
  • GitHub Check: build-qe (linux, amd64)
  • GitHub Check: build (macOS-13, 1.23)
  • GitHub Check: build (macOS-14, 1.23)
  • GitHub Check: Konflux kflux-prd-rh02 / crc-binary-on-pull-request
🔇 Additional comments (1)
pkg/fileserver/fs9p/shares.go (1)

11-23: LGTM!

The struct definitions are well-documented and appropriately designed for their purpose of representing 9P mounts with different connection types.

@anjannath
Copy link
Member

anjannath commented Aug 21, 2025

i think you can squash these 3 commits into one: 3c6ea2c , bf5745f and 4eafa75

need to test on windows, apart from that LGTM only have some small comments/questions

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: 10

♻️ Duplicate comments (13)
cmd/crc/cmd/daemon.go (1)

253-266: Do not share the entire home by default; gate and scope the share.

Expose a scoped directory (e.g., ~/.crc/share), make the feature opt‑in, and prefer read‑only. Fall back to TCP only when enabled.

Apply something like:

@@
-    // 9p home directory sharing
-    if runtime.GOOS == "windows" {
-        if _, err := fs9p.StartHvsockShares([]fs9p.HvsockMount9p{{Path: constants.GetHomeDir(), HvsockGUID: constants.Plan9HvsockGUID}}); err != nil {
+    // 9p file sharing (Windows only, opt-in)
+    if runtime.GOOS == "windows" && os.Getenv("CRC_ENABLE_9P_HOME") == "1" {
+        shareDir := filepath.Join(constants.CrcBaseDir, "share")
+        if err := os.MkdirAll(shareDir, 0700); err != nil {
+            return fmt.Errorf("preparing 9p share dir: %w", err)
+        }
+        if _, err := fs9p.StartHvsockShares([]fs9p.HvsockMount9p{{Path: shareDir, HvsockGUID: constants.Plan9HvsockGUID}}); err != nil {
             logging.Warnf("Failed to start 9p file server on hvsock: %v", err)
             logging.Warnf("Falling back to 9p over TCP")
-            listener9p, err := vn.Listen("tcp", net.JoinHostPort(configuration.GatewayIP, fmt.Sprintf("%d", constants.Plan9TcpPort)))
+            listener9p, err := vn.Listen("tcp", net.JoinHostPort(configuration.GatewayIP, fmt.Sprintf("%d", constants.Plan9TcpPort)))
             if err != nil {
                 return err
             }
-            if _, err := fs9p.StartShares([]fs9p.Mount9p{{Listener: listener9p, Path: constants.GetHomeDir()}}); err != nil {
+            if _, err := fs9p.StartShares([]fs9p.Mount9p{{Listener: listener9p, Path: shareDir}}); err != nil {
                 return err
             }
         }
     }

Optional next step: add a read‑only layer in fs9p that rejects create/write/chmod ops.

pkg/fileserver/fs9p/shares.go (1)

24-47: Remove defer-in-loop cleanup; do explicit cleanup on failure.

The closure relies on a named return and captures loop vars; replace with clear cleanup to avoid surprises.

-// StartShares starts a new 9p server for each of the supplied mounts.
-func StartShares(mounts []Mount9p) (servers []*Server, defErr error) {
-  servers9p := []*Server{}
+// StartShares starts a new 9p server for each of the supplied mounts.
+func StartShares(mounts []Mount9p) ([]*Server, error) {
+  servers9p := make([]*Server, 0, len(mounts))
+  cleanup := func() {
+    for _, s := range servers9p {
+      if err := s.Stop(); err != nil {
+        logrus.Errorf("Error stopping 9p server during cleanup: %v", err)
+      }
+    }
+  }
   for _, m := range mounts {
     server, err := New9pServer(m.Listener, m.Path)
     if err != nil {
-      return nil, fmt.Errorf("serving directory %s on %s: %w", m.Path, m.Listener.Addr().String(), err)
+      cleanup()
+      return nil, fmt.Errorf("serving directory %s on %s: %w", m.Path, m.Listener.Addr().String(), err)
     }
 
     err = server.Start()
     if err != nil {
-      return nil, fmt.Errorf("starting 9p server for directory %s: %w", m.Path, err)
+      cleanup()
+      return nil, fmt.Errorf("starting 9p server for directory %s: %w", m.Path, err)
     }
 
     servers9p = append(servers9p, server)
-
-    defer func() {
-      if defErr != nil {
-        if err := server.Stop(); err != nil {
-          logrus.Errorf("Error stopping 9p server: %v", err)
-        }
-      }
-    }()
vendor/github.com/DeedleFake/p9/proto/proto.go (1)

101-108: Remove dead code and drain invalid message payload.

err is nil here; also drain remaining bytes to keep stream aligned.

   t := p.TypeFromID(msgType)
   if t == nil {
-    if err != nil {
-      return nil, NoTag, err
-    }
-
-    return nil, NoTag, util.Errorf("receive: invalid message type: %v", msgType)
+    // Drain rest of payload so the connection can recover.
+    _, _ = io.Copy(io.Discard, lr)
+    return nil, NoTag, util.Errorf("receive: invalid message type: %v", msgType)
   }
vendor/github.com/DeedleFake/p9/proto/server.go (1)

70-78: Treat receive errors as fatal for the connection.

After a framing/deserialize error the stream is misaligned; bail out.

   tmsg, tag, err := p.Receive(c, msize)
   if err != nil {
     if err == io.EOF {
       return
     }
 
     log.Printf("Error reading message: %v", err)
-    continue
+    return
   }
vendor/github.com/DeedleFake/p9/fs.go (4)

12-12: Remove unsafe string-to-bytes; drop unused unsafe import

Replace the unsafe conversion and remove the unused import.

@@
-import (
+import (
   "bytes"
   "crypto/sha256"
   "encoding/binary"
   "fmt"
   "io"
   "path"
   "sync"
   "time"
-  "unsafe"
@@
-  sum := sha256.Sum256(*(*[]byte)(unsafe.Pointer(&p)))
+  sum := sha256.Sum256([]byte(p))

Also applies to: 179-181


121-130: Make directory reads spec-compliant by validating offsets

Track and enforce dir read offsets per 9P: offset must be 0 or previous offset + previous read length.

@@
 type fsFile struct {
   sync.RWMutex
 
   path string
 
   a Attachment
 
   file File
-  dir  bytes.Buffer
+  dir  bytes.Buffer
+  // last directory read offset to validate per-9P sequential reads
+  dirOff int64
 }
@@
   switch {
   case qid.Type&QTDir != 0:
-    if msg.Offset == 0 {
+    if msg.Offset == 0 {
       dir, err := file.file.Readdir()
       if err != nil {
         return &Rerror{
           Ename: err.Error(),
         }
       }
@@
       file.dir.Reset()
       err = WriteDir(&file.dir, dir)
       if err != nil {
         return &Rerror{
           Ename: err.Error(),
         }
       }
-    }
+      file.dirOff = 0
+    } else if int64(msg.Offset) != file.dirOff {
+      return &Rerror{Ename: "invalid directory read offset"}
+    }
 
-    // This technically isn't quite accurate to the 9P specification.
-    // The specification states that all reads of a directory must
-    // either be at offset 0 or at the previous offset plus the length
-    // of the previous read. Instead, this implemenation just ignores
-    // the offset if it's not zero. This is backwards compatible with
-    // the specification, however, so it's probably not really an
-    // issue.
     tmp, err := file.dir.Read(buf)
     if (err != nil) && (err != io.EOF) {
       return &Rerror{
         Ename: err.Error(),
       }
     }
     n = tmp
+    file.dirOff += int64(n)

Also applies to: 468-513


536-538: Use exclusive lock for writes

Writes mutate state; RLock allows concurrent writes -> data races.

-  file.RLock()         // Somewhat ironic that this doesn't require a
-  defer file.RUnlock() // full lock like read() does.
+  file.Lock()
+  defer file.Unlock()

593-598: Fix incorrect error detection after clunk

clunk returns protocol responses; check for *Rerror, not error.

   rsp := h.clunk(&Tclunk{
     FID: msg.FID,
   })
-  if _, ok := rsp.(error); ok {
+  if _, ok := rsp.(*Rerror); ok {
     return rsp
   }
vendor/github.com/DeedleFake/p9/stat.go (1)

9-13: Avoid unsafe conversion in FileMode.String; remove unsafe import

Use safe string(buf) and drop unsafe import.

@@
 import (
   "bytes"
   "errors"
   "io"
   "os"
   "time"
-  "unsafe"
@@
-  return *(*string)(unsafe.Pointer(&buf))
+  return string(buf)

Also applies to: 152-153

vendor/github.com/DeedleFake/p9/dir.go (2)

105-122: Directory create: don’t open with file flags; return a directory handle

Opening a directory with os.OpenFile + flags is invalid. Create dir, then open it read-only.

 func (d Dir) Create(p string, perm FileMode, mode uint8) (File, error) {
   p = d.path(p)
 
   if perm&ModeDir != 0 {
     err := os.Mkdir(p, os.FileMode(perm.Perm()))
     if err != nil {
       return nil, err
     }
+    f, err := os.Open(p)
+    return &dirFile{File: f}, err
   }
 
   flag := toOSFlags(mode)
 
   file, err := os.OpenFile(p, flag|os.O_CREATE, os.FileMode(perm.Perm()))
   return &dirFile{
     File: file,
   }, err
 }

239-260: Mutually exclusive access flags

ORDWR, OWRITE, OREAD should be exclusive; current logic can combine them.

 func toOSFlags(mode uint8) (flag int) {
-  if mode&OREAD != 0 {
-    flag |= os.O_RDONLY
-  }
-  if mode&OWRITE != 0 {
-    flag |= os.O_WRONLY
-  }
-  if mode&ORDWR != 0 {
-    flag |= os.O_RDWR
-  }
+  if mode&ORDWR != 0 {
+    flag |= os.O_RDWR
+  } else if mode&OWRITE != 0 {
+    flag |= os.O_WRONLY
+  } else {
+    // O_RDONLY is 0; nothing to set
+  }
   if mode&OTRUNC != 0 {
     flag |= os.O_TRUNC
   }
   //if mode&OEXCL != 0 {
   //  flag |= os.O_EXCL
   //}
   //if mode&OAPPEND != 0 {
   //  flag |= os.O_APPEND
   //}
 
   return flag
 }
vendor/github.com/DeedleFake/p9/msg.go (2)

207-217: Return early on encode error to avoid partial writes (Rstat.P9Encode)

Stop after the first failed write.

 func (stat *Rstat) P9Encode() ([]byte, error) {
   var buf bytes.Buffer
 
   err := proto.Write(&buf, stat.Stat.size()+2)
   if err != nil {
     return nil, err
   }
 
   err = proto.Write(&buf, stat.Stat)
-  return buf.Bytes(), err
+  if err != nil {
+    return nil, err
+  }
+  return buf.Bytes(), nil
 }

240-255: Return early on encode error to avoid partial writes (Twstat.P9Encode)

Same pattern as above.

 func (stat *Twstat) P9Encode() ([]byte, error) {
   var buf bytes.Buffer
 
   err := proto.Write(&buf, stat.FID)
   if err != nil {
     return nil, err
   }
 
   err = proto.Write(&buf, stat.Stat.size()+2)
   if err != nil {
     return nil, err
   }
 
   err = proto.Write(&buf, stat.Stat)
-  return buf.Bytes(), err
+  if err != nil {
+    return nil, err
+  }
+  return buf.Bytes(), nil
 }
🧹 Nitpick comments (9)
vendor/github.com/DeedleFake/p9/addr_other.go (3)

5-9: Add strings import required by sanitization.

 import (
 	"os"
 	"os/user"
 	"path/filepath"
+	"strings"
 )

11-16: Doc tweak: clarify behavior for this non-Linux/Darwin variant.

Current comment references Unix-like /tmp/... which doesn’t apply here.

-// NamespaceDir returns the path of the directory that is used for the
-//
-// current namespace. On Unix-like systems, this is
-//
-// /tmp/ns.$USER.$DISPLAY.
+// NamespaceDir returns the path of the directory used for the
+// current namespace on non-Linux/Darwin systems. Here it is:
+//    os.TempDir()/ns.<user>.<display>
+// On platforms with restrictive filename characters (e.g., Windows),
+// components are sanitized; DISPLAY defaults to "0" if unset.

18-21: Optional: avoid panic on user lookup failure.

If you want to harden the vendored lib against environment quirks, fall back to a generic username instead of panicking.

u, err := user.Current()
uname := "unknown"
if err == nil && u.Username != "" {
    uname = u.Username
}

Note: if keeping behavior parity with upstream is a goal, skip this change and keep the panic.

vendor/github.com/DeedleFake/p9/dir_windows.go (1)

9-27: Minor: use LastWriteTime for MTime for consistency with ATime source.

Keeps both timestamps from the same source; avoids subtle rounding differences.

-    return DirEntry{
-        FileMode:  ModeFromOS(fi.Mode()),
-        ATime:     time.Unix(0, sys.LastAccessTime.Nanoseconds()),
-        MTime:     fi.ModTime(),
-        Length:    uint64(fi.Size()),
-        EntryName: fi.Name(),
-    }
+    return DirEntry{
+        FileMode:  ModeFromOS(fi.Mode()),
+        ATime:     time.Unix(0, sys.LastAccessTime.Nanoseconds()),
+        MTime:     time.Unix(0, sys.LastWriteTime.Nanoseconds()),
+        Length:    uint64(fi.Size()),
+        EntryName: fi.Name(),
+    }
vendor/github.com/DeedleFake/p9/dir_plan9.go (2)

21-30: Populate QID fields in DirEntry when available.

Include Path and Version from sys.Qid to avoid losing QID info in directory listings.

  return DirEntry{
    FileMode:  ModeFromOS(fi.Mode()),
    ATime:     time.Unix(int64(sys.Atime), 0),
    MTime:     fi.ModTime(),
    Length:    uint64(fi.Size()),
    EntryName: fi.Name(),
    UID:       sys.Uid,
    GID:       sys.Gid,
    MUID:      sys.Muid,
+   Path:      sys.Qid.Path,
+   Version:   sys.Qid.Vers,
  }

39-42: Improve error to include path context.

Helps debugging when QID extraction fails.

-import "errors"
+import (
+  "errors"
+  "fmt"
+)

-  return QID{}, errors.New("failed to get QID: FileInfo was not Dir")
+  return QID{}, fmt.Errorf("failed to get QID for %q: FileInfo.Sys() was not *syscall.Dir", d.path(p))
vendor/github.com/DeedleFake/p9/remote.go (2)

273-275: Use io.ErrShortWrite for partial writes instead of io.EOF.

EOF on write is misleading; ErrShortWrite is the standard signal.

-  if write.Count < uint32(len(data)) {
-    return int(write.Count), io.EOF
-  }
+  if write.Count < uint32(len(data)) {
+    return int(write.Count), io.ErrShortWrite
+  }

323-331: Avoid receiver shadowing in Stat.

Local var named file shadows the receiver and harms readability.

-  if p != "" {
-    file, err := file.walk(p)
+  if p != "" {
+    rf, err := file.walk(p)
     if err != nil {
       return DirEntry{}, err
     }
-    defer file.Close()
+    defer rf.Close()
 
-    return file.Stat("")
+    return rf.Stat("")
   }
vendor/github.com/DeedleFake/p9/proto/server.go (1)

94-97: Close on send errors to avoid half-open sessions.

If writes fail, the connection is likely broken.

   err := p.Send(c, tag, rmsg)
   if err != nil {
     log.Printf("Error writing message: %v", err)
+    return
   }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4eafa75 and 31f4ba7.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (36)
  • cmd/crc/cmd/daemon.go (5 hunks)
  • go.mod (2 hunks)
  • packaging/windows/product.wxs.template (2 hunks)
  • pkg/crc/constants/constants.go (1 hunks)
  • pkg/crc/machine/libhvee/driver_windows.go (1 hunks)
  • pkg/crc/machine/start.go (2 hunks)
  • pkg/fileserver/fs9p/server.go (1 hunks)
  • pkg/fileserver/fs9p/shares.go (1 hunks)
  • pkg/fileserver/fs9p/shares_fallback.go (1 hunks)
  • pkg/fileserver/fs9p/shares_windows.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/LICENSE (1 hunks)
  • vendor/github.com/DeedleFake/p9/README.md (1 hunks)
  • vendor/github.com/DeedleFake/p9/addr.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/addr_other.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/addr_unix.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/client.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_darwin.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_linux.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_other.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_plan9.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_windows.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/doc.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/encoding.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/fs.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/internal/debug/debug.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/internal/debug/nodebug.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/internal/util/util.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/msg.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/p9.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/proto/client.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/proto/encoding.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/proto/proto.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/proto/server.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/remote.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/stat.go (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • vendor/github.com/DeedleFake/p9/LICENSE
  • vendor/github.com/DeedleFake/p9/doc.go
🚧 Files skipped from review as they are similar to previous changes (16)
  • vendor/github.com/DeedleFake/p9/internal/debug/nodebug.go
  • vendor/github.com/DeedleFake/p9/addr_unix.go
  • pkg/crc/machine/libhvee/driver_windows.go
  • vendor/github.com/DeedleFake/p9/encoding.go
  • vendor/github.com/DeedleFake/p9/dir_other.go
  • vendor/github.com/DeedleFake/p9/proto/client.go
  • vendor/github.com/DeedleFake/p9/internal/util/util.go
  • vendor/github.com/DeedleFake/p9/client.go
  • vendor/github.com/DeedleFake/p9/internal/debug/debug.go
  • pkg/fileserver/fs9p/server.go
  • vendor/github.com/DeedleFake/p9/addr.go
  • vendor/github.com/DeedleFake/p9/p9.go
  • pkg/crc/machine/start.go
  • vendor/github.com/DeedleFake/p9/dir_linux.go
  • vendor/github.com/DeedleFake/p9/dir_darwin.go
  • vendor/github.com/DeedleFake/p9/proto/encoding.go
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: redbeam
PR: crc-org/crc#4866
File: pkg/fileserver/fs9p/shares.go:0-0
Timestamp: 2025-08-06T09:48:10.441Z
Learning: The vsock implementation in pkg/fileserver/fs9p/shares.go was temporarily removed because 9pfs doesn't fully support it yet. It will be added back when 9pfs provides proper vsock support.
📚 Learning: 2025-08-06T09:48:10.441Z
Learnt from: redbeam
PR: crc-org/crc#4866
File: pkg/fileserver/fs9p/shares.go:0-0
Timestamp: 2025-08-06T09:48:10.441Z
Learning: The vsock implementation in pkg/fileserver/fs9p/shares.go was temporarily removed because 9pfs doesn't fully support it yet. It will be added back when 9pfs provides proper vsock support.

Applied to files:

  • pkg/fileserver/fs9p/shares_windows.go
  • pkg/fileserver/fs9p/shares_fallback.go
  • cmd/crc/cmd/daemon.go
  • pkg/fileserver/fs9p/shares.go
  • vendor/github.com/DeedleFake/p9/fs.go
  • vendor/github.com/DeedleFake/p9/README.md
🧬 Code graph analysis (14)
pkg/fileserver/fs9p/shares_windows.go (3)
pkg/fileserver/fs9p/shares_fallback.go (1)
  • StartHvsockShares (11-13)
pkg/fileserver/fs9p/shares.go (3)
  • HvsockMount9p (19-22)
  • Mount9p (12-15)
  • StartShares (25-61)
pkg/fileserver/fs9p/server.go (1)
  • Server (15-24)
pkg/fileserver/fs9p/shares_fallback.go (3)
pkg/fileserver/fs9p/shares_windows.go (1)
  • StartHvsockShares (12-33)
pkg/fileserver/fs9p/shares.go (1)
  • HvsockMount9p (19-22)
pkg/fileserver/fs9p/server.go (1)
  • Server (15-24)
cmd/crc/cmd/daemon.go (5)
pkg/fileserver/fs9p/shares_fallback.go (1)
  • StartHvsockShares (11-13)
pkg/fileserver/fs9p/shares_windows.go (1)
  • StartHvsockShares (12-33)
pkg/fileserver/fs9p/shares.go (3)
  • HvsockMount9p (19-22)
  • StartShares (25-61)
  • Mount9p (12-15)
pkg/crc/constants/constants.go (3)
  • GetHomeDir (166-172)
  • Plan9HvsockGUID (61-61)
  • Plan9TcpPort (60-60)
pkg/crc/logging/logging.go (1)
  • Warnf (100-102)
vendor/github.com/DeedleFake/p9/dir_plan9.go (3)
vendor/github.com/DeedleFake/p9/stat.go (3)
  • DirEntry (255-267)
  • FileMode (23-23)
  • ModeFromOS (43-78)
vendor/github.com/DeedleFake/p9/dir.go (1)
  • Dir (16-16)
vendor/github.com/DeedleFake/p9/p9.go (2)
  • QID (42-46)
  • Version (10-10)
vendor/github.com/DeedleFake/p9/addr_other.go (1)
vendor/github.com/DeedleFake/p9/addr_unix.go (1)
  • NamespaceDir (17-29)
pkg/fileserver/fs9p/shares.go (1)
pkg/fileserver/fs9p/server.go (2)
  • Server (15-24)
  • New9pServer (29-52)
vendor/github.com/DeedleFake/p9/proto/server.go (2)
vendor/github.com/DeedleFake/p9/msg.go (1)
  • Proto (75-77)
vendor/github.com/DeedleFake/p9/proto/proto.go (1)
  • Proto (38-41)
vendor/github.com/DeedleFake/p9/stat.go (3)
vendor/github.com/DeedleFake/p9/p9.go (3)
  • QIDType (49-49)
  • QID (42-46)
  • Version (10-10)
vendor/github.com/DeedleFake/p9/proto/encoding.go (3)
  • Write (25-31)
  • Read (34-38)
  • Size (14-20)
vendor/github.com/DeedleFake/p9/internal/util/util.go (1)
  • LimitedReader (13-17)
vendor/github.com/DeedleFake/p9/dir_windows.go (1)
vendor/github.com/DeedleFake/p9/stat.go (3)
  • DirEntry (255-267)
  • FileMode (23-23)
  • ModeFromOS (43-78)
vendor/github.com/DeedleFake/p9/proto/proto.go (2)
vendor/github.com/DeedleFake/p9/proto/encoding.go (3)
  • Read (34-38)
  • Write (25-31)
  • Size (14-20)
vendor/github.com/DeedleFake/p9/internal/util/util.go (2)
  • Errorf (44-52)
  • LimitedReader (13-17)
vendor/github.com/DeedleFake/p9/remote.go (7)
vendor/github.com/DeedleFake/p9/client.go (1)
  • Client (22-26)
vendor/github.com/DeedleFake/p9/proto/client.go (1)
  • Client (18-31)
vendor/github.com/DeedleFake/p9/p9.go (4)
  • QID (42-46)
  • QIDType (49-49)
  • Version (10-10)
  • IOHeaderSize (70-70)
vendor/github.com/DeedleFake/p9/msg.go (14)
  • Twalk (133-137)
  • Rwalk (139-141)
  • Topen (143-146)
  • Ropen (148-151)
  • Tcreate (153-158)
  • Rcreate (160-163)
  • Tremove (192-194)
  • Tread (165-169)
  • Rread (171-173)
  • Twrite (175-179)
  • Rwrite (181-183)
  • Tclunk (185-187)
  • Tstat (199-201)
  • Rstat (203-205)
vendor/github.com/DeedleFake/p9/stat.go (3)
  • FileMode (23-23)
  • Stat (156-168)
  • DirEntry (255-267)
vendor/github.com/DeedleFake/p9/internal/util/util.go (1)
  • Errorf (44-52)
vendor/github.com/DeedleFake/p9/encoding.go (1)
  • ReadDir (16-30)
vendor/github.com/DeedleFake/p9/fs.go (5)
vendor/github.com/DeedleFake/p9/stat.go (4)
  • Stat (156-168)
  • DirEntry (255-267)
  • StatChanges (316-318)
  • FileMode (23-23)
vendor/github.com/DeedleFake/p9/p9.go (7)
  • QID (42-46)
  • QIDType (49-49)
  • IOHeaderSize (70-70)
  • Version (10-10)
  • QTAuth (58-58)
  • NoFID (18-18)
  • QTDir (54-54)
vendor/github.com/DeedleFake/p9/proto/server.go (1)
  • MessageHandler (135-139)
vendor/github.com/DeedleFake/p9/msg.go (27)
  • Tversion (79-82)
  • Tversion (84-84)
  • Rerror (118-120)
  • Rversion (86-89)
  • Tauth (95-99)
  • Rauth (101-103)
  • Tflush (126-128)
  • Tattach (105-110)
  • Rattach (112-114)
  • Twalk (133-137)
  • Rwalk (139-141)
  • Topen (143-146)
  • Ropen (148-151)
  • Tcreate (153-158)
  • Rcreate (160-163)
  • Tread (165-169)
  • Rread (171-173)
  • Twrite (175-179)
  • Rwrite (181-183)
  • Tclunk (185-187)
  • Rclunk (189-190)
  • Tremove (192-194)
  • Rremove (196-197)
  • Tstat (199-201)
  • Rstat (203-205)
  • Twstat (235-238)
  • Rwstat (278-279)
vendor/github.com/DeedleFake/p9/encoding.go (1)
  • WriteDir (33-42)
vendor/github.com/DeedleFake/p9/msg.go (5)
vendor/github.com/DeedleFake/p9/proto/proto.go (2)
  • NewProto (44-60)
  • Proto (38-41)
vendor/github.com/DeedleFake/p9/p9.go (2)
  • Version (10-10)
  • QID (42-46)
vendor/github.com/DeedleFake/p9/stat.go (3)
  • FileMode (23-23)
  • Stat (156-168)
  • ErrLargeStat (18-18)
vendor/github.com/DeedleFake/p9/proto/encoding.go (2)
  • Write (25-31)
  • Read (34-38)
vendor/github.com/DeedleFake/p9/internal/util/util.go (1)
  • LimitedReader (13-17)
vendor/github.com/DeedleFake/p9/dir.go (3)
vendor/github.com/DeedleFake/p9/stat.go (5)
  • Stat (156-168)
  • DirEntry (255-267)
  • StatChanges (316-318)
  • FileMode (23-23)
  • ModeDir (27-27)
vendor/github.com/DeedleFake/p9/fs.go (4)
  • File (100-119)
  • Attachment (44-74)
  • FileSystem (21-34)
  • QIDFS (89-91)
vendor/github.com/DeedleFake/p9/p9.go (7)
  • OWRITE (27-27)
  • ORDWR (28-28)
  • OEXEC (29-29)
  • OTRUNC (31-31)
  • OCEXEC (32-32)
  • ORCLOSE (33-33)
  • OREAD (26-26)
🪛 markdownlint-cli2 (0.17.2)
vendor/github.com/DeedleFake/p9/README.md

12-12: Heading style
Expected: setext; Actual: atx

(MD003, heading-style)


17-17: Hard tabs
Column: 1

(MD010, no-hard-tabs)


21-21: Heading style
Expected: setext; Actual: atx

(MD003, heading-style)


26-26: Hard tabs
Column: 1

(MD010, no-hard-tabs)


32-32: Hard tabs
Column: 1

(MD010, no-hard-tabs)


37-37: Hard tabs
Column: 1

(MD010, no-hard-tabs)


43-43: Hard tabs
Column: 1

(MD010, no-hard-tabs)


49-49: Hard tabs
Column: 1

(MD010, no-hard-tabs)

⏰ 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). (18)
  • GitHub Check: build (macOS-14, 1.24)
  • GitHub Check: build (ubuntu-latest, 1.24)
  • GitHub Check: build (ubuntu-22.04, 1.24)
  • GitHub Check: build (macOS-13, 1.24)
  • GitHub Check: build (macOS-13, 1.24)
  • GitHub Check: Run OKD bundle with crc (1.24)
  • GitHub Check: build (windows-2022, 1.24)
  • GitHub Check: build (macOS-14, 1.24)
  • GitHub Check: build (ubuntu-latest, 1.24)
  • GitHub Check: build (windows-2022, 1.24)
  • GitHub Check: verify-devcontainer
  • GitHub Check: build-installer (windows-2022, 1.24)
  • GitHub Check: build-qe (darwin, arm64)
  • GitHub Check: build-qe (linux, amd64)
  • GitHub Check: build-qe (windows, amd64)
  • GitHub Check: build-qe (darwin, amd64)
  • GitHub Check: build-qe (linux, arm64)
  • GitHub Check: Konflux kflux-prd-rh02 / crc-binary-on-pull-request
🔇 Additional comments (7)
packaging/windows/product.wxs.template (1)

75-79: Registering the 9P hvsock GCS channel looks correct.

Component has KeyPath and is referenced by the feature; aligns with the GUID in constants. Uninstall will cleanly remove the value.

Also applies to: 138-138

vendor/github.com/DeedleFake/p9/README.md (1)

1-52: Vendor docs — leave as-is.

Upstream formatting nits from linters can be ignored for vendored README.

pkg/fileserver/fs9p/shares_fallback.go (1)

1-13: Non-Windows stub is fine.

Clear error on unsupported platforms keeps builds green.

cmd/crc/cmd/daemon.go (2)

182-186: Good: use net.JoinHostPort for HTTP listeners.

Also applies to: 198-201


14-14: New imports are appropriate for platform gating and 9P wiring.

Also applies to: 28-28

go.mod (1)

7-7: Deps verified — p9 vendored with LICENSE; linuxkit/virtsock direct; no other 9P libs found.

vendor/github.com/DeedleFake/p9 contains LICENSE; go.mod pins github.com/DeedleFake/p9 v0.6.12 (line 7) and github.com/linuxkit/virtsock is direct (line 34); repository imports 9P only via github.com/DeedleFake/p9 (pkg/fileserver/fs9p/server.go) and uses hvsock via github.com/linuxkit/virtsock/pkg/hvsock (pkg/fileserver/fs9p/shares_windows.go).

pkg/crc/constants/constants.go (1)

59-62: Document new constants; confirm unused ones or remove.

Add brief comments for each constant in pkg/crc/constants/constants.go (lines 59–62). Plan9HvsockPort appears unused in this PR; either wire it or drop it to avoid confusion. Ripgrep usage check returned "No files were searched" — manual verification of usages recommended.

-    Plan9Msize      = 1024 * 1024
+    // Plan9Msize is the negotiated maximum message size for 9P I/O (bytes).
+    Plan9Msize      = 1024 * 1024
-    Plan9TcpPort    = 564
+    // Plan9TcpPort is the default TCP port for 9P service inside the VM network.
+    Plan9TcpPort    = 564
-    Plan9HvsockGUID = "00009000-FACB-11E6-BD58-64006A7986D3"
+    // Plan9HvsockGUID identifies the Hyper-V GCS channel used for 9P.
+    Plan9HvsockGUID = "00009000-FACB-11E6-BD58-64006A7986D3"
-    Plan9HvsockPort = 36864
+    // Plan9HvsockPort (unused): remove if not required.
+    Plan9HvsockPort = 36864

Comment on lines +23 to +30
display, ok := os.LookupEnv("DISPLAY")
if !ok {
display = ":0"
}

return filepath.Join(os.TempDir(), "ns."+u.Username+"."+display)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Windows breakage: invalid path chars from DISPLAY default (":0") and possible backslash in Username.

On Windows, ":" and "" are illegal in filename components. ns.<username>.:0 will fail; user.Current().Username may include a domain prefix (DOMAIN\user), introducing "" as a path separator. This will break 9P namespace resolution on Windows (hvsock/TCP fallback paths in this PR).

Fix by sanitizing both Username and DISPLAY, stripping the domain component, and defaulting DISPLAY to "0" on non-Unix builds.

Apply this diff to the function body (imports and helper provided in separate comments):

 func NamespaceDir() string {
-	u, err := user.Current()
+	u, err := user.Current()
 	if err != nil {
 		panic(err)
 	}
 
-	display, ok := os.LookupEnv("DISPLAY")
-	if !ok {
-		display = ":0"
-	}
+	uname := u.Username
+	// Strip potential Windows domain prefix: DOMAIN\user -> user
+	if i := strings.LastIndex(uname, "\\"); i >= 0 {
+		uname = uname[i+1:]
+	}
+	uname = sanitizeComponent(uname)
+
+	display, ok := os.LookupEnv("DISPLAY")
+	if !ok || display == "" {
+		display = "0"
+	}
+	display = sanitizeComponent(display)
 
-	return filepath.Join(os.TempDir(), "ns."+u.Username+"."+display)
+	return filepath.Join(os.TempDir(), "ns."+uname+"."+display)
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
display, ok := os.LookupEnv("DISPLAY")
if !ok {
display = ":0"
}
return filepath.Join(os.TempDir(), "ns."+u.Username+"."+display)
}
func NamespaceDir() string {
u, err := user.Current()
if err != nil {
panic(err)
}
uname := u.Username
// Strip potential Windows domain prefix: DOMAIN\user -> user
if i := strings.LastIndex(uname, "\\"); i >= 0 {
uname = uname[i+1:]
}
uname = sanitizeComponent(uname)
display, ok := os.LookupEnv("DISPLAY")
if !ok || display == "" {
display = "0"
}
display = sanitizeComponent(display)
return filepath.Join(os.TempDir(), "ns."+uname+"."+display)
}
🤖 Prompt for AI Agents
In vendor/github.com/DeedleFake/p9/addr_other.go around lines 23–29, sanitize
the username and DISPLAY before composing the temp path: strip any domain prefix
from u.Username (remove everything up to and including a backslash), replace or
remove illegal filename characters (e.g., '\' and ':' on Windows) from both the
username and DISPLAY, and on non-Unix builds default DISPLAY to "0" instead of
":0"; then join using filepath.Join as before. Implement these changes by
normalizing the two strings (trim spaces, remove or replace ':' and path
separators) and using the provided helper functions/imports so the returned
filename is valid on Windows and Unix.

Comment on lines +18 to +20
func (d Dir) path(p string) string {
return filepath.Join(string(d), filepath.FromSlash(p))
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Windows path escape: absolute p overrides base

On Windows, Join(base, FromSlash(p)) with leading slash makes p absolute, escaping the share root. Sanitize to a relative path first.

@@
 func (d Dir) path(p string) string {
-  return filepath.Join(string(d), filepath.FromSlash(p))
+  ps := filepath.FromSlash(p)
+  // Trim any leading separators to force relative join
+  for len(ps) > 0 && (ps[0] == os.PathSeparator) {
+    ps = ps[1:]
+  }
+  return filepath.Join(string(d), ps)
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (d Dir) path(p string) string {
return filepath.Join(string(d), filepath.FromSlash(p))
}
func (d Dir) path(p string) string {
ps := filepath.FromSlash(p)
// Trim any leading separators to force relative join
for len(ps) > 0 && (ps[0] == os.PathSeparator) {
ps = ps[1:]
}
return filepath.Join(string(d), ps)
}

Comment on lines 244 to 249
func (h *fsHandler) flush(msg *Tflush) interface{} {
// TODO: Implement this.
return &Rerror{
Ename: "flush is not supported",
}
}
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Tflush unsupported — confirm client compatibility or return Rflush

Returning an error for flush can break clients that rely on it. Consider returning Rflush and documenting that cancellation isn’t guaranteed, or gate this behind a capability.


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "PWD: $(pwd)"
echo "---- Searching for Tflush/Rflush/flush occurrences ----"
rg -n --hidden --no-ignore -S '\bTflush\b|\bRflush\b|\bflush\s*\(' || true
echo
echo "---- Showing vendor/github.com/DeedleFake/p9/fs.go (first 400 lines) ----"
if [ -f vendor/github.com/DeedleFake/p9/fs.go ]; then
  sed -n '1,400p' vendor/github.com/DeedleFake/p9/fs.go || true
else
  echo "vendor/github.com/DeedleFake/p9/fs.go not found"
fi
echo
echo "---- Find fsHandler type and methods ----"
rg -n --hidden --no-ignore -S 'type\s+fsHandler\b' || true
rg -n --hidden --no-ignore -S 'func\s+\(h\s+\*\s*fsHandler\)\s+\w+' || true
echo
echo "---- Search for 'not supported' strings in p9 package ----"
rg -n --hidden --no-ignore -S 'is not supported' vendor/github.com/DeedleFake/p9 || true
echo
echo "---- Search for BUG/Tflush notes in package docs ----"
rg -n --hidden --no-ignore -S 'Tflush' || true

Length of output: 21611


Tflush unsupported — return Rflush or implement cancellation (vendor/.../p9/fs.go:244–249; BUG noted at vendor/.../p9/fs.go:147)

Returning an Rerror breaks clients that expect flush to cancel pending calls. Options:

  • Return &Rflush{} (and document that cancellation may not be guaranteed).
  • Implement cancellable ReadAt/WriteAt so Tflush can abort in-flight ops.
  • Gate flush behavior behind a capability and document the limitation.

Comment on lines 719 to 729
func (h *fsHandler) Close() error {
h.fids.Range(func(k, v interface{}) bool {
file := v.(*fsFile)
if file.file != nil {
file.file.Close()
}
return true
})

return nil
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Close should lock per-file before closing

Avoid races with in-flight ops by locking file objects during Close().

 func (h *fsHandler) Close() error {
   h.fids.Range(func(k, v interface{}) bool {
     file := v.(*fsFile)
-    if file.file != nil {
-      file.file.Close()
-    }
+    file.Lock()
+    if file.file != nil {
+      _ = file.file.Close()
+    }
+    file.Unlock()
     return true
   })
 
   return nil
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (h *fsHandler) Close() error {
h.fids.Range(func(k, v interface{}) bool {
file := v.(*fsFile)
if file.file != nil {
file.file.Close()
}
return true
})
return nil
}
func (h *fsHandler) Close() error {
h.fids.Range(func(k, v interface{}) bool {
file := v.(*fsFile)
file.Lock()
if file.file != nil {
_ = file.file.Close()
}
file.Unlock()
return true
})
return nil
}
🤖 Prompt for AI Agents
In vendor/github.com/DeedleFake/p9/fs.go around lines 719 to 729, the Close
implementation closes file.file without acquiring the file-level lock which can
race with in-flight ops; modify the Range loop to for each v.(*fsFile) acquire
that file's mutex (e.g. file.mu.Lock() or file.Lock()), check file.file != nil,
call file.file.Close(), set file.file = nil, then release the mutex
(file.mu.Unlock() or file.Unlock()); ensure you use the actual lock field/type
on fsFile and handle nil safely.

Comment on lines +85 to +94
if (msize > 0) && (size > msize) {
return nil, NoTag, util.Errorf("receive: %w", ErrLargeMessage)
}

lr := &util.LimitedReader{
R: r,
N: size,
E: ErrLargeMessage,
}

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Limit reader to payload (size-4) and guard short sizes.

Prevents over-reads and enforces framing strictly.

-  if (msize > 0) && (size > msize) {
+  if (msize > 0) && (size > msize) {
     return nil, NoTag, util.Errorf("receive: %w", ErrLargeMessage)
   }
 
-  lr := &util.LimitedReader{
+  if size < 4 {
+    return nil, NoTag, util.Errorf("receive: short message size: %d", size)
+  }
+  lr := &util.LimitedReader{
     R: r,
-    N: size,
+    N: size - 4,
     E: ErrLargeMessage,
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (msize > 0) && (size > msize) {
return nil, NoTag, util.Errorf("receive: %w", ErrLargeMessage)
}
lr := &util.LimitedReader{
R: r,
N: size,
E: ErrLargeMessage,
}
if (msize > 0) && (size > msize) {
return nil, NoTag, util.Errorf("receive: %w", ErrLargeMessage)
}
if size < 4 {
return nil, NoTag, util.Errorf("receive: short message size: %d", size)
}
lr := &util.LimitedReader{
R: r,
N: size - 4,
E: ErrLargeMessage,
}
🤖 Prompt for AI Agents
In vendor/github.com/DeedleFake/p9/proto/proto.go around lines 85 to 94, the
LimitedReader is currently set to read the entire declared size which can allow
over-reads; change logic to first guard against too-small frames (if size < 4
return ErrShortRead or ErrLargeMessage as appropriate) and set
util.LimitedReader.N to size-4 so it only reads the payload (excluding the
4-byte tag/length field). Ensure the short-size check happens before creating
the LimitedReader and preserve existing msize/size comparison behavior and error
returns.

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: 5

♻️ Duplicate comments (21)
pkg/fileserver/fs9p/server.go (3)

54-69: Acknowledge previous race condition concern.

A previous reviewer noted that the non-blocking select (lines 62-68) may miss early startup errors. The reviewer suggested adding a timeout (e.g., 100ms) to wait briefly for potential startup errors before returning success.

Based on learnings, you mentioned moving the server start code to a separate method (as agreed with anjannath). Ensure this refactoring is tracked if not yet completed.


71-81: Acknowledge previous zombie process concern.

A previous reviewer flagged the "BAD THINGS" comment about zombie processes and suggested improving the documentation and error wrapping to help operators understand the implications of stopping the server while clients are active.


83-87: Acknowledge previous indefinite blocking concern.

A previous reviewer noted that WaitForError blocks indefinitely and suggested adding context support to allow timeouts or cancellation.

cmd/crc/cmd/daemon.go (1)

253-266: Acknowledge previous security concern about sharing entire home directory.

A previous reviewer flagged that exposing constants.GetHomeDir() (line 259) shares the user's complete home directory without access controls or read-only enforcement. They recommended:

  • Share a dedicated subdirectory (e.g., $CRC_HOME/share) instead
  • Enforce read-only access by default
  • Implement path filters or authentication in fs9p

Additionally, consider whether 9P sharing should be conditional (opt-in via configuration) rather than automatically enabled on Windows.

#!/bin/bash
# Check if there are configuration flags or user preferences for enabling 9P sharing
rg -n -C3 "Enable.*9p|9p.*Enable|shared.*dir.*config" --type=go
vendor/github.com/DeedleFake/p9/client.go (3)

45-47: FID generator can wrap into NoFID (MaxUint32). Handle overflow or use uint64.

 func (c *Client) nextFID() uint32 {
-    return atomic.AddUint32(&c.fid, 1) - 1
+    fid := atomic.AddUint32(&c.fid, 1) - 1
+    if fid == NoFID {
+        // Skip reserved NoFID, wrap to 0.
+        atomic.StoreUint32(&c.fid, 0)
+        return 0
+    }
+    return fid
 }

Longer term: recycle freed FIDs to avoid unbounded growth and eventual wrap.


84-90: Add safe type assertion for Rauth response.

-    rauth := rsp.(*Rauth)
+    rauth, ok := rsp.(*Rauth)
+    if !ok {
+        return nil, fmt.Errorf("unexpected response type for auth: %T", rsp)
+    }

113-119: Add safe type assertion for Rattach response.

-    attach := rsp.(*Rattach)
+    attach, ok := rsp.(*Rattach)
+    if !ok {
+        return nil, fmt.Errorf("unexpected response type for attach: %T", rsp)
+    }
vendor/github.com/DeedleFake/p9/proto/encoding.go (3)

65-71: Size-mode for custom Encoder: count bytes explicitly

In size mode, explicitly add len(buf) to avoid any ambiguity with binary.Size on slices. Safer and clearer.

 case Encoder:
     buf, err := v.P9Encode()
     e.err = err
-    e.mode(buf)
+    if e.mode == e.size {
+        e.n += uint32(len(buf))
+    } else {
+        e.mode(buf)
+    }
     return

150-153: Avoid unsafe cast when reading 16-bit length

Unsafe pointer cast is endianness-unsafe. Read into a uint16 and widen.

-        default:
-            d.read((*uint16)(unsafe.Pointer(&length)))
+        default:
+            var len16 uint16
+            d.read(&len16)
+            length = uint32(len16)

168-171: Remove unsafe []byte→string conversion

Use safe conversion to preserve Go’s string immutability guarantees.

     buf := make([]byte, int(length))
     d.read(buf)
-
-    v.SetString(*(*string)(unsafe.Pointer(&buf)))
+    v.SetString(string(buf))
vendor/github.com/DeedleFake/p9/proto/client.go (1)

90-97: Don’t loop forever on read errors

Continuing on non-EOF errors can cause an endless loop. Log and return (or implement backoff). Minimal fix below.

-        if err != nil {
-            if (ctx.Err() != nil) || (err == io.EOF) {
-                return
-            }
-
-            continue
-        }
+        if err != nil {
+            if (ctx.Err() != nil) || (err == io.EOF) {
+                return
+            }
+            log.Printf("Read error: %v", err)
+            return
+        }
vendor/github.com/DeedleFake/p9/proto/server.go (1)

70-79: Skip processing after read error

Continue the loop after logging; don’t process a potentially invalid tmsg.

     tmsg, tag, err := p.Receive(c, msize)
     if err != nil {
         if err == io.EOF {
             return
         }
 
         log.Printf("Error reading message: %v", err)
+        continue
     }
vendor/github.com/DeedleFake/p9/stat.go (1)

152-152: Unsafe pointer conversion in vendored code.

The unsafe.Pointer conversion was flagged in previous reviews. While string(buf) would be safer, this is vendored third-party code from DeedleFake/p9. Consider reporting this upstream if it's a concern, but avoid modifying vendored dependencies directly.

vendor/github.com/DeedleFake/p9/fs.go (6)

179-180: Unsafe string-to-byte conversion in vendored code.

The unsafe.Pointer conversion to pass a string as []byte to SHA256 was previously flagged. While the function only reads the data, this violates Go's memory safety rules. This is vendored code from DeedleFake/p9 and should be addressed upstream.


499-512: Non-standard directory offset handling (documented in vendored code).

The implementation's directory read behavior deviates from strict 9P specification requirements, as noted in the inline comment. The code is backward-compatible but may not work with all 9P clients. This is vendored code with an acknowledged limitation.


536-537: Potential write race condition in vendored code.

Using RLock for write operations was previously flagged as a potential race condition. The comment acknowledges this is "ironic" but concurrent writes to file.file could still cause issues depending on the underlying File implementation. This is vendored code from DeedleFake/p9.


593-598: Error handling pattern in vendored code is actually correct.

The previous review comment suggested changing the error type assertion. However, since *Rerror implements the error interface (via its Error() string method at line 122-124 in msg.go), the current pattern if _, ok := rsp.(error); ok correctly detects error responses. This is vendored code.


244-249: Tflush not supported (documented limitation in vendored code).

The lack of Tflush support is documented in the BUG comment at line 147 and acknowledged with a TODO. This is a known limitation of the vendored DeedleFake/p9 library that may affect clients expecting full 9P cancellation support.


719-729: Close lacks per-file locking in vendored code.

The Close method iterates all files and closes them without acquiring per-file locks, which could race with in-flight operations. This was previously flagged. This is vendored code from DeedleFake/p9 and should be addressed upstream if it causes issues in practice.

vendor/github.com/DeedleFake/p9/msg.go (1)

207-217: Error handling in P9Encode is correct (vendored code).

The previous review comment suggested improving error handling, but the current implementation correctly returns early on all intermediate errors and only returns the buffer with the final write's error. Since only the last write could fail without early return, there's no partial write issue. This is vendored code.

vendor/github.com/DeedleFake/p9/remote.go (1)

37-43: Path splitting produces empty component for absolute paths (vendored code).

The previous review correctly identified that strings.Split("/a/b", "/") produces ["", "a", "b"], which includes an invalid empty component for Twalk. This is vendored code from DeedleFake/p9 and should be addressed upstream as it could cause walk failures for absolute paths.

🧹 Nitpick comments (10)
cmd/crc/cmd/daemon.go (1)

182-182: Prefer net.JoinHostPort for clarity.

Good refactor using net.JoinHostPort instead of fmt.Sprintf for constructing listener addresses. This is more idiomatic and handles IPv6 addresses correctly.

Also applies to: 198-198

vendor/github.com/DeedleFake/p9/internal/util/util.go (3)

19-25: Consider pointer receiver for consistency.

The err() method uses a value receiver (line 19), while Read() uses a pointer receiver (line 27). For consistency and to avoid copying the struct, consider changing line 19 to:

-func (lr LimitedReader) err() error {
+func (lr *LimitedReader) err() error {

This is a minor issue since the method only reads fields, but pointer receivers are more conventional for struct methods.


27-39: Simplify condition and verify underflow handling.

Two observations:

  1. Line 28: The condition lr.N <= 0 can be simplified to lr.N == 0 since N is uint32 and cannot be negative.

  2. Line 37: The decrement lr.N -= uint32(n) could underflow if the underlying reader returns more bytes than requested. However, since Go's io.Reader contract guarantees n <= len(buf) and we've already limited buf to lr.N (lines 32-34), this should be safe.

Apply this diff to simplify the condition:

-	if lr.N <= 0 {
+	if lr.N == 0 {
 		return 0, lr.err()
 	}

41-52: Verify type assertion safety in Errorf.

Line 47 performs an unchecked type assertion arg.(error). While this should be safe because we've confirmed arg == io.EOF (which is an error type), a safer approach would be:

if err, ok := arg.(error); ok && err == io.EOF {
    return err
}

However, since io.EOF is a known constant of type error, the current code is acceptable but slightly fragile if the logic changes.

Consider adding a comment explaining why the type assertion is safe:

 	for _, arg := range args {
-		if arg == io.EOF {
-			return arg.(error)
+		// io.EOF is always of type error, so this assertion is safe
+		if err, ok := arg.(error); ok && err == io.EOF {
+			return err
 		}
 	}
vendor/github.com/DeedleFake/p9/dir_darwin.go (1)

23-33: Optional: fallback UID/GID to numeric strings when lookups fail.

Empty UID/GID can hinder clients. Consider populating with the numeric IDs if lookup fails.

-    var uname string
-    uid, err := user.LookupId(strconv.FormatUint(uint64(sys.Uid), 10))
-    if err == nil {
-        uname = uid.Username
-    }
+    var uname = strconv.FormatUint(uint64(sys.Uid), 10)
+    if u, err := user.LookupId(uname); err == nil {
+        uname = u.Username
+    }

-    var gname string
-    gid, err := user.LookupGroupId(strconv.FormatUint(uint64(sys.Gid), 10))
-    if err == nil {
-        gname = gid.Name
-    }
+    var gname = strconv.FormatUint(uint64(sys.Gid), 10)
+    if g, err := user.LookupGroupId(gname); err == nil {
+        gname = g.Name
+    }
vendor/github.com/DeedleFake/p9/dir_linux.go (1)

23-33: Optional: fallback UID/GID to numeric strings when lookups fail.

Populate UID/GID with numeric IDs if lookups fail to avoid empty fields.

-    var uname string
-    uid, err := user.LookupId(strconv.FormatUint(uint64(sys.Uid), 10))
-    if err == nil {
-        uname = uid.Username
-    }
+    var uname = strconv.FormatUint(uint64(sys.Uid), 10)
+    if u, err := user.LookupId(uname); err == nil {
+        uname = u.Username
+    }

-    var gname string
-    gid, err := user.LookupGroupId(strconv.FormatUint(uint64(sys.Gid), 10))
-    if err == nil {
-        gname = gid.Name
-    }
+    var gname = strconv.FormatUint(uint64(sys.Gid), 10)
+    if g, err := user.LookupGroupId(gname); err == nil {
+        gname = g.Name
+    }
vendor/github.com/DeedleFake/p9/proto/encoding.go (1)

3-11: Drop unused unsafe import (after applying above changes)

Once unsafe sites are removed, the import becomes unused.

 import (
     "encoding/binary"
     "io"
     "reflect"
     "time"
-    "unsafe"
 
     "github.com/DeedleFake/p9/internal/util"
 )
vendor/github.com/DeedleFake/p9/proto/client.go (2)

69-75: Graceful shutdown: wait for goroutines to exit

Cancel, wait for coord to close done, then close the conn to avoid races.

 func (c *Client) Close() error {
-    c.cancel()
-    return c.c.Close()
+    c.cancel()
+    <-c.done
+    return c.c.Close()
 }

80-88: Make read timeout configurable (nice-to-have)

Hard-coded 10s may not fit all environments. Consider a field or option to tune it.

vendor/github.com/DeedleFake/p9/proto/server.go (1)

61-68: Concurrent writes to net.Conn may interleave

If proto.Send does multiple writes, responses can interleave across goroutines. Either guarantee atomic writes in proto.Send or guard with a per-conn write mutex. Minimal guard shown.

 func handleMessages(c net.Conn, p Proto, handler MessageHandler) {
     var setter sync.Once
 
     var msize uint32
+    var wmu sync.Mutex
@@
-            err := p.Send(c, ctag, rmsg)
+            wmu.Lock()
+            err := p.Send(c, ctag, rmsg)
+            wmu.Unlock()
             if err != nil {
                 log.Printf("Error writing message: %v", err)
             }

To verify if the guard is needed, please check whether proto.Send writes the entire message in a single conn.Write call.

Also applies to: 94-97

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 31f4ba7 and 3079019.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (33)
  • cmd/crc/cmd/daemon.go (5 hunks)
  • go.mod (2 hunks)
  • pkg/crc/constants/constants.go (1 hunks)
  • pkg/crc/machine/libhvee/driver_windows.go (1 hunks)
  • pkg/crc/machine/start.go (2 hunks)
  • pkg/fileserver/fs9p/server.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/LICENSE (1 hunks)
  • vendor/github.com/DeedleFake/p9/README.md (1 hunks)
  • vendor/github.com/DeedleFake/p9/addr.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/addr_other.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/addr_unix.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/client.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_darwin.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_linux.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_other.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_plan9.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_windows.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/doc.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/encoding.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/fs.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/internal/debug/debug.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/internal/debug/nodebug.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/internal/util/util.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/msg.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/p9.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/proto/client.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/proto/encoding.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/proto/proto.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/proto/server.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/remote.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/stat.go (1 hunks)
  • vendor/modules.txt (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • vendor/github.com/DeedleFake/p9/internal/debug/nodebug.go
🚧 Files skipped from review as they are similar to previous changes (13)
  • vendor/github.com/DeedleFake/p9/LICENSE
  • pkg/crc/machine/libhvee/driver_windows.go
  • vendor/github.com/DeedleFake/p9/encoding.go
  • vendor/github.com/DeedleFake/p9/addr_other.go
  • vendor/github.com/DeedleFake/p9/dir_other.go
  • vendor/github.com/DeedleFake/p9/proto/proto.go
  • pkg/crc/constants/constants.go
  • vendor/github.com/DeedleFake/p9/dir.go
  • vendor/github.com/DeedleFake/p9/addr_unix.go
  • go.mod
  • vendor/github.com/DeedleFake/p9/p9.go
  • vendor/github.com/DeedleFake/p9/dir_plan9.go
  • vendor/github.com/DeedleFake/p9/doc.go
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: redbeam
PR: crc-org/crc#4866
File: pkg/fileserver/fs9p/shares.go:0-0
Timestamp: 2025-08-06T09:48:10.441Z
Learning: The vsock implementation in pkg/fileserver/fs9p/shares.go was temporarily removed because 9pfs doesn't fully support it yet. It will be added back when 9pfs provides proper vsock support.
🧬 Code graph analysis (17)
vendor/github.com/DeedleFake/p9/proto/client.go (6)
vendor/github.com/DeedleFake/p9/msg.go (1)
  • Proto (75-77)
vendor/github.com/DeedleFake/p9/proto/proto.go (3)
  • Proto (38-41)
  • NoTag (27-27)
  • ErrClientClosed (22-22)
vendor/github.com/DeedleFake/p9/client.go (3)
  • NewClient (30-32)
  • Client (22-26)
  • Dial (36-43)
vendor/github.com/DeedleFake/p9/internal/debug/debug.go (1)
  • Log (10-12)
vendor/github.com/DeedleFake/p9/internal/debug/nodebug.go (1)
  • Log (5-5)
vendor/github.com/DeedleFake/p9/p9.go (1)
  • NoTag (15-15)
pkg/crc/machine/start.go (1)
pkg/os/exec.go (1)
  • RunPrivileged (48-59)
vendor/github.com/DeedleFake/p9/dir_linux.go (3)
vendor/github.com/DeedleFake/p9/stat.go (4)
  • DirEntry (255-267)
  • FileMode (23-23)
  • ModeFromOS (43-78)
  • Stat (156-168)
vendor/github.com/DeedleFake/p9/dir.go (1)
  • Dir (16-16)
vendor/github.com/DeedleFake/p9/p9.go (3)
  • QID (42-46)
  • QIDType (49-49)
  • Version (10-10)
vendor/github.com/DeedleFake/p9/internal/debug/debug.go (1)
vendor/github.com/DeedleFake/p9/internal/debug/nodebug.go (1)
  • Log (5-5)
vendor/github.com/DeedleFake/p9/dir_darwin.go (3)
vendor/github.com/DeedleFake/p9/stat.go (3)
  • DirEntry (255-267)
  • FileMode (23-23)
  • ModeFromOS (43-78)
vendor/github.com/DeedleFake/p9/dir.go (1)
  • Dir (16-16)
vendor/github.com/DeedleFake/p9/p9.go (3)
  • QID (42-46)
  • QIDType (49-49)
  • Version (10-10)
vendor/github.com/DeedleFake/p9/addr.go (2)
vendor/github.com/DeedleFake/p9/addr_other.go (1)
  • NamespaceDir (17-29)
vendor/github.com/DeedleFake/p9/addr_unix.go (1)
  • NamespaceDir (17-29)
cmd/crc/cmd/daemon.go (2)
pkg/crc/constants/constants.go (2)
  • Plan9TcpPort (60-60)
  • GetHomeDir (164-170)
pkg/fileserver/fs9p/server.go (1)
  • New9pServer (29-52)
vendor/github.com/DeedleFake/p9/msg.go (6)
vendor/github.com/DeedleFake/p9/proto/proto.go (2)
  • NewProto (44-60)
  • Proto (38-41)
vendor/github.com/DeedleFake/p9/p9.go (2)
  • Version (10-10)
  • QID (42-46)
vendor/github.com/DeedleFake/p9/proto/client.go (1)
  • P9NoTag (221-223)
vendor/github.com/DeedleFake/p9/stat.go (3)
  • FileMode (23-23)
  • Stat (156-168)
  • ErrLargeStat (18-18)
vendor/github.com/DeedleFake/p9/proto/encoding.go (2)
  • Write (25-31)
  • Read (34-38)
vendor/github.com/DeedleFake/p9/internal/util/util.go (1)
  • LimitedReader (13-17)
vendor/github.com/DeedleFake/p9/remote.go (7)
vendor/github.com/DeedleFake/p9/client.go (1)
  • Client (22-26)
vendor/github.com/DeedleFake/p9/proto/client.go (1)
  • Client (18-31)
vendor/github.com/DeedleFake/p9/p9.go (4)
  • QID (42-46)
  • QIDType (49-49)
  • Version (10-10)
  • IOHeaderSize (70-70)
vendor/github.com/DeedleFake/p9/msg.go (14)
  • Twalk (133-137)
  • Rwalk (139-141)
  • Topen (143-146)
  • Ropen (148-151)
  • Tcreate (153-158)
  • Rcreate (160-163)
  • Tremove (192-194)
  • Tread (165-169)
  • Rread (171-173)
  • Twrite (175-179)
  • Rwrite (181-183)
  • Tclunk (185-187)
  • Tstat (199-201)
  • Rstat (203-205)
vendor/github.com/DeedleFake/p9/stat.go (3)
  • FileMode (23-23)
  • Stat (156-168)
  • DirEntry (255-267)
vendor/github.com/DeedleFake/p9/internal/util/util.go (1)
  • Errorf (44-52)
vendor/github.com/DeedleFake/p9/encoding.go (1)
  • ReadDir (16-30)
vendor/github.com/DeedleFake/p9/proto/encoding.go (1)
vendor/github.com/DeedleFake/p9/internal/util/util.go (1)
  • Errorf (44-52)
vendor/github.com/DeedleFake/p9/proto/server.go (2)
vendor/github.com/DeedleFake/p9/msg.go (1)
  • Proto (75-77)
vendor/github.com/DeedleFake/p9/proto/proto.go (1)
  • Proto (38-41)
vendor/github.com/DeedleFake/p9/dir_windows.go (1)
vendor/github.com/DeedleFake/p9/stat.go (3)
  • DirEntry (255-267)
  • FileMode (23-23)
  • ModeFromOS (43-78)
vendor/github.com/DeedleFake/p9/fs.go (8)
vendor/github.com/DeedleFake/p9/stat.go (4)
  • Stat (156-168)
  • DirEntry (255-267)
  • StatChanges (316-318)
  • FileMode (23-23)
vendor/github.com/DeedleFake/p9/p9.go (7)
  • QID (42-46)
  • QIDType (49-49)
  • IOHeaderSize (70-70)
  • Version (10-10)
  • QTAuth (58-58)
  • NoFID (18-18)
  • QTDir (54-54)
vendor/github.com/DeedleFake/p9/proto/server.go (3)
  • MessageHandler (135-139)
  • ConnHandler (112-114)
  • ConnHandlerFunc (125-125)
vendor/github.com/DeedleFake/p9/internal/debug/debug.go (1)
  • Log (10-12)
vendor/github.com/DeedleFake/p9/internal/debug/nodebug.go (1)
  • Log (5-5)
vendor/github.com/DeedleFake/p9/msg.go (27)
  • Tversion (79-82)
  • Tversion (84-84)
  • Rerror (118-120)
  • Rversion (86-89)
  • Tauth (95-99)
  • Rauth (101-103)
  • Tflush (126-128)
  • Tattach (105-110)
  • Rattach (112-114)
  • Twalk (133-137)
  • Rwalk (139-141)
  • Topen (143-146)
  • Ropen (148-151)
  • Tcreate (153-158)
  • Rcreate (160-163)
  • Tread (165-169)
  • Rread (171-173)
  • Twrite (175-179)
  • Rwrite (181-183)
  • Tclunk (185-187)
  • Rclunk (189-190)
  • Tremove (192-194)
  • Rremove (196-197)
  • Tstat (199-201)
  • Rstat (203-205)
  • Twstat (235-238)
  • Rwstat (278-279)
vendor/github.com/DeedleFake/p9/client.go (1)
  • ErrUnsupportedVersion (14-14)
vendor/github.com/DeedleFake/p9/encoding.go (1)
  • WriteDir (33-42)
pkg/fileserver/fs9p/server.go (3)
vendor/github.com/DeedleFake/p9/fs.go (2)
  • FileSystem (21-34)
  • FSConnHandler (162-167)
vendor/github.com/DeedleFake/p9/proto/server.go (1)
  • Serve (17-42)
pkg/crc/constants/constants.go (1)
  • Plan9Msize (59-59)
vendor/github.com/DeedleFake/p9/client.go (5)
vendor/github.com/DeedleFake/p9/proto/client.go (3)
  • Client (18-31)
  • NewClient (35-56)
  • Dial (60-67)
vendor/github.com/DeedleFake/p9/msg.go (8)
  • Proto (75-77)
  • Tversion (79-82)
  • Tversion (84-84)
  • Rversion (86-89)
  • Tauth (95-99)
  • Rauth (101-103)
  • Tattach (105-110)
  • Rattach (112-114)
vendor/github.com/DeedleFake/p9/proto/proto.go (1)
  • Proto (38-41)
vendor/github.com/DeedleFake/p9/p9.go (3)
  • Version (10-10)
  • NoFID (18-18)
  • QID (42-46)
vendor/github.com/DeedleFake/p9/remote.go (1)
  • Remote (19-27)
vendor/github.com/DeedleFake/p9/internal/util/util.go (1)
vendor/github.com/DeedleFake/p9/proto/encoding.go (1)
  • Read (34-38)
vendor/github.com/DeedleFake/p9/stat.go (3)
vendor/github.com/DeedleFake/p9/p9.go (3)
  • QIDType (49-49)
  • QID (42-46)
  • Version (10-10)
vendor/github.com/DeedleFake/p9/proto/encoding.go (3)
  • Write (25-31)
  • Read (34-38)
  • Size (14-20)
vendor/github.com/DeedleFake/p9/internal/util/util.go (1)
  • LimitedReader (13-17)
🪛 markdownlint-cli2 (0.18.1)
vendor/github.com/DeedleFake/p9/README.md

12-12: Heading style
Expected: setext; Actual: atx

(MD003, heading-style)


17-17: Hard tabs
Column: 1

(MD010, no-hard-tabs)


21-21: Heading style
Expected: setext; Actual: atx

(MD003, heading-style)


26-26: Hard tabs
Column: 1

(MD010, no-hard-tabs)


32-32: Hard tabs
Column: 1

(MD010, no-hard-tabs)


37-37: Hard tabs
Column: 1

(MD010, no-hard-tabs)


43-43: Hard tabs
Column: 1

(MD010, no-hard-tabs)


49-49: Hard tabs
Column: 1

(MD010, no-hard-tabs)

⏰ 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). (17)
  • GitHub Check: build (windows-2022, 1.24)
  • GitHub Check: build (ubuntu-latest, 1.24)
  • GitHub Check: verify-devcontainer
  • GitHub Check: Run OKD bundle with crc (1.24)
  • GitHub Check: build-qe (darwin, arm64)
  • GitHub Check: build (macOS-13, 1.24)
  • GitHub Check: build (macOS-13, 1.24)
  • GitHub Check: build (ubuntu-22.04, 1.24)
  • GitHub Check: build (ubuntu-latest, 1.24)
  • GitHub Check: build (macOS-14, 1.24)
  • GitHub Check: build-installer (windows-2022, 1.24)
  • GitHub Check: build-qe (linux, arm64)
  • GitHub Check: build-qe (windows, amd64)
  • GitHub Check: build-qe (darwin, amd64)
  • GitHub Check: build (windows-2022, 1.24)
  • GitHub Check: build-qe (linux, amd64)
  • GitHub Check: Konflux kflux-prd-rh02 / crc-binary-on-pull-request
🔇 Additional comments (11)
vendor/github.com/DeedleFake/p9/internal/debug/debug.go (1)

10-12: LGTM: Simple and appropriate debug logger for vendored library.

The Log function provides conditional debug output to stderr when the p9debug build tag is set, complementing the no-op variant in nodebug.go. The implementation is minimal and appropriate for debug logging in a vendored 9P library.

vendor/github.com/DeedleFake/p9/README.md (1)

1-51: Vendored documentation looks appropriate.

This is documentation from the upstream DeedleFake/p9 library. The examples are clear and demonstrate basic server/client usage. The static analysis warnings (hard tabs, heading style) are from the upstream library and should not be modified here.

vendor/github.com/DeedleFake/p9/addr.go (1)

1-78: Vendored address parsing utilities look appropriate.

This is part of the upstream DeedleFake/p9 library. The address parsing logic correctly handles multiple 9P address formats (namespace, Unix socket, TCP with various delimiters). The code integrates with platform-specific namespace directory utilities (NamespaceDir() from addr_unix.go and addr_other.go).

As vendored code, this should remain unchanged from upstream. Any improvements or concerns should be addressed in the upstream repository.

vendor/modules.txt (1)

13-18: Verify p9 fork declaration and documentation

  • Check go.mod contains require github.com/DeedleFake/p9 v0.6.12 (within a require block) and no replace from hugelgupf/p9.
  • Ensure no remaining imports of github.com/hugelgupf/p9 outside vendor.
  • Confirm vendor/github.com/DeedleFake/p9/LICENSE is included.
  • Add a brief note in go.mod comments or a deps.md explaining the fork’s intent, license (MIT), and update process for future maintainers.
pkg/fileserver/fs9p/server.go (2)

15-24: LGTM! Server structure is well-defined.

The Server struct encapsulates all necessary components for a 9P file server: the network listener, filesystem abstraction, exposed directory path, and error channel.


26-52: LGTM! Constructor validates inputs properly.

The New9pServer function correctly validates that the exposed directory is absolute, exists, and is a directory before creating the filesystem wrapper.

cmd/crc/cmd/daemon.go (2)

14-14: LGTM! Runtime import added for OS detection.

The runtime package is appropriately imported to support Windows-specific 9P sharing logic.


28-28: LGTM! fs9p package imported for 9P server.

The fs9p import is correctly added to support the new 9P file sharing functionality.

vendor/github.com/DeedleFake/p9/dir_windows.go (1)

9-27: LGTM! Windows metadata extraction is appropriate.

The infoToEntry function correctly:

  • Attempts to extract Windows-specific attributes via type assertion (line 10)
  • Provides a sensible fallback when Windows attributes are unavailable (lines 12-18)
  • Populates ATime from LastAccessTime when available (line 22)
  • Uses standard FileInfo methods for cross-platform fields

The conversion from Filetime.Nanoseconds() to time.Unix(0, ...) (line 22) is correct for translating Windows time to Go time.

vendor/github.com/DeedleFake/p9/internal/util/util.go (1)

8-17: LGTM! LimitedReader structure is clear.

The LimitedReader type provides a bounded reader with custom error support. Using uint32 for N allows larger sizes on 32-bit systems, as documented.

pkg/crc/machine/start.go (1)

262-269: Use VSockGateway constant and validate 9pfs usage

  • Replace the literal "192.168.127.1" with constants.VSockGateway in pkg/crc/machine/start.go.
  • Verify that the 9pfs binary is bundled and that the mount command syntax (9pfs <gateway-IP> <target>) is correct.
  • Annotate the privileged chown core:core (line 264) to document its security implications.

Comment on lines +61 to +69
version := rsp.(*Rversion)
if version.Version != Version {
return 0, ErrUnsupportedVersion
}

c.SetMsize(version.Msize)

return version.Msize, nil
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Guard the Rversion type assertion to avoid panic.

rsp.(*Rversion) can panic if server replies with error or unexpected type. Use two-value assertion and return a clear error.

-    version := rsp.(*Rversion)
+    version, ok := rsp.(*Rversion)
+    if !ok {
+        return 0, fmt.Errorf("unexpected response type for version: %T", rsp)
+    }

Also consider accepting compatible variants like "9P2000.u" if the server negotiates an extension; strict equality may reduce interoperability.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
version := rsp.(*Rversion)
if version.Version != Version {
return 0, ErrUnsupportedVersion
}
c.SetMsize(version.Msize)
return version.Msize, nil
}
version, ok := rsp.(*Rversion)
if !ok {
return 0, fmt.Errorf("unexpected response type for version: %T", rsp)
}
if version.Version != Version {
return 0, ErrUnsupportedVersion
}
c.SetMsize(version.Msize)
return version.Msize, nil
}
🤖 Prompt for AI Agents
In vendor/github.com/DeedleFake/p9/client.go around lines 61 to 69, the code
uses a single-value type assertion rsp.(*Rversion) which can panic if rsp is an
error or another unexpected type; replace it with the two-value assertion (v,
ok) and if ok is false inspect whether rsp is an *Rerror and return that error
or otherwise return a clear ErrUnexpectedResponse; after safely asserting, use
v.Msize; also relax the version equality check to accept compatible variants
(e.g., use a prefix or explicit allowed list such as
strings.HasPrefix(version.Version, "9P2000") or allow "9P2000.u") so
server-negotiated extensions don’t cause ErrUnsupportedVersion.

Comment on lines +35 to +43
return DirEntry{
FileMode: ModeFromOS(fi.Mode()),
ATime: time.Unix(sys.Atimespec.Unix()),
MTime: fi.ModTime(),
Length: uint64(fi.Size()),
EntryName: fi.Name(),
UID: uname,
GID: gname,
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

ATime conversion uses non-existent Timespec.Unix(); fix to Sec/Nsec fields.

syscall.Timespec on Darwin has Sec/Nsec; there’s no Unix() method. This won’t compile. Use time.Unix(sec, nsec).

-        ATime:     time.Unix(sys.Atimespec.Unix()),
+        ATime:     time.Unix(sys.Atimespec.Sec, sys.Atimespec.Nsec),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return DirEntry{
FileMode: ModeFromOS(fi.Mode()),
ATime: time.Unix(sys.Atimespec.Unix()),
MTime: fi.ModTime(),
Length: uint64(fi.Size()),
EntryName: fi.Name(),
UID: uname,
GID: gname,
}
return DirEntry{
FileMode: ModeFromOS(fi.Mode()),
ATime: time.Unix(sys.Atimespec.Sec, sys.Atimespec.Nsec),
MTime: fi.ModTime(),
Length: uint64(fi.Size()),
EntryName: fi.Name(),
UID: uname,
GID: gname,
}
🤖 Prompt for AI Agents
In vendor/github.com/DeedleFake/p9/dir_darwin.go around lines 35 to 43, the code
calls sys.Atimespec.Unix() which doesn't exist on Darwin's syscall.Timespec;
replace the ATime construction to use time.Unix(sys.Atimespec.Sec,
sys.Atimespec.Nsec) (i.e. pass Sec and Nsec fields to time.Unix) so the access
time is built from the Timespec Sec/Nsec fields.

Comment on lines +35 to +43
return DirEntry{
FileMode: ModeFromOS(fi.Mode()),
ATime: time.Unix(sys.Atim.Unix()),
MTime: fi.ModTime(),
Length: uint64(fi.Size()),
EntryName: fi.Name(),
UID: uname,
GID: gname,
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

ATime conversion uses non-existent Timespec.Unix(); fix to Sec/Nsec fields.

syscall.Timespec on Linux has Sec/Nsec; there’s no Unix() method. Use time.Unix(sec, nsec).

-        ATime:     time.Unix(sys.Atim.Unix()),
+        ATime:     time.Unix(sys.Atim.Sec, sys.Atim.Nsec),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return DirEntry{
FileMode: ModeFromOS(fi.Mode()),
ATime: time.Unix(sys.Atim.Unix()),
MTime: fi.ModTime(),
Length: uint64(fi.Size()),
EntryName: fi.Name(),
UID: uname,
GID: gname,
}
return DirEntry{
FileMode: ModeFromOS(fi.Mode()),
ATime: time.Unix(sys.Atim.Sec, sys.Atim.Nsec),
MTime: fi.ModTime(),
Length: uint64(fi.Size()),
EntryName: fi.Name(),
UID: uname,
GID: gname,
}
🤖 Prompt for AI Agents
In vendor/github.com/DeedleFake/p9/dir_linux.go around lines 35 to 43, the ATime
conversion calls a non-existent Timespec.Unix() method; replace that call with
time.Unix using the Sec and Nsec fields (e.g., time.Unix(sys.Atim.Sec,
sys.Atim.Nsec)) so the access time is constructed from the correct Timespec
fields.

Comment on lines +138 to +145
case c.nextTag <- nextTag:
for {
nextTag++
if _, ok := tags[nextTag]; !ok {
break
}
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Never allocate the reserved NoTag value

Ensure the allocator never returns NoTag, even on wrap-around.

-        case c.nextTag <- nextTag:
-            for {
-                nextTag++
-                if _, ok := tags[nextTag]; !ok {
-                    break
-                }
-            }
+        case c.nextTag <- func() uint16 {
+            // Produce a tag to return now (skip NoTag).
+            if nextTag == NoTag {
+                nextTag++
+            }
+            t := nextTag
+            // Prepare the next available tag, skipping NoTag and in-use tags.
+            for {
+                nextTag++
+                if nextTag == NoTag {
+                    nextTag++
+                }
+                if _, ok := tags[nextTag]; !ok {
+                    break
+                }
+            }
+            return t
+        }():
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
case c.nextTag <- nextTag:
for {
nextTag++
if _, ok := tags[nextTag]; !ok {
break
}
}
}
case c.nextTag <- func() uint16 {
// Produce a tag to return now (skip NoTag).
if nextTag == NoTag {
nextTag++
}
t := nextTag
// Prepare the next available tag, skipping NoTag and in-use tags.
for {
nextTag++
if nextTag == NoTag {
nextTag++
}
if _, ok := tags[nextTag]; !ok {
break
}
}
return t
}():
🤖 Prompt for AI Agents
In vendor/github.com/DeedleFake/p9/proto/client.go around lines 138 to 145, the
tag allocator can return the reserved NoTag when wrapping; change the selection
loop so it never yields NoTag by advancing nextTag again if it equals NoTag (and
continue skipping any tags present in the map). Concretely, after incrementing
nextTag check both that tags[nextTag] is false and nextTag != NoTag; if nextTag
== NoTag, increment/wrap again and keep looping until you find a tag that is not
in the map and not NoTag, then send that value on c.nextTag.

Comment on lines +79 to +99
mode(func() {
rmsg := handler.HandleMessage(tmsg)
if rmsg, ok := rmsg.(Msizer); ok {
if msize > 0 {
log.Println("Warning: Attempted to set msize twice.")
}

setter.Do(func() {
msize = rmsg.P9Msize()
mode = func(f func()) {
go f()
}
})
}

err := p.Send(c, tag, rmsg)
if err != nil {
log.Printf("Error writing message: %v", err)
}
})
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Data race/logic bug: goroutine closes over loop vars tag/tmsg

The closure may observe mutated tag/tmsg. Copy them into locals before launching.

-        mode(func() {
-            rmsg := handler.HandleMessage(tmsg)
+        ctag := tag
+        ctmsg := tmsg
+        mode(func() {
+            rmsg := handler.HandleMessage(ctmsg)
             if rmsg, ok := rmsg.(Msizer); ok {
                 if msize > 0 {
                     log.Println("Warning: Attempted to set msize twice.")
                 }
 
                 setter.Do(func() {
                     msize = rmsg.P9Msize()
                     mode = func(f func()) {
                         go f()
                     }
                 })
             }
 
-            err := p.Send(c, tag, rmsg)
+            err := p.Send(c, ctag, rmsg)
             if err != nil {
                 log.Printf("Error writing message: %v", err)
             }
         })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
mode(func() {
rmsg := handler.HandleMessage(tmsg)
if rmsg, ok := rmsg.(Msizer); ok {
if msize > 0 {
log.Println("Warning: Attempted to set msize twice.")
}
setter.Do(func() {
msize = rmsg.P9Msize()
mode = func(f func()) {
go f()
}
})
}
err := p.Send(c, tag, rmsg)
if err != nil {
log.Printf("Error writing message: %v", err)
}
})
}
ctag := tag
ctmsg := tmsg
mode(func() {
rmsg := handler.HandleMessage(ctmsg)
if rmsg, ok := rmsg.(Msizer); ok {
if msize > 0 {
log.Println("Warning: Attempted to set msize twice.")
}
setter.Do(func() {
msize = rmsg.P9Msize()
mode = func(f func()) {
go f()
}
})
}
err := p.Send(c, ctag, rmsg)
if err != nil {
log.Printf("Error writing message: %v", err)
}
})
🤖 Prompt for AI Agents
In vendor/github.com/DeedleFake/p9/proto/server.go around lines 79 to 99, the
anonymous function passed to mode closes over the loop variables tag and tmsg
which can be mutated by the loop and cause a data race; fix it by copying tag
and tmsg into new local variables immediately before calling mode (e.g.,
localTag, localTmsg) and use those locals inside the closure so each goroutine
sees the intended values, leaving the rest of the logic unchanged.

@redbeam redbeam force-pushed the 9p-deedlefake branch 3 times, most recently from 9d23c41 to f176d25 Compare October 1, 2025 18:11
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: 5

♻️ Duplicate comments (27)
pkg/fileserver/fs9p/server.go (3)

56-68: Wait briefly for startup errors to avoid “false success” log.
Non‑blocking select can miss immediate Accept errors; add a short timeout.

 func (s *Server) Start() error {
 	go func() {
 		s.ErrChan <- proto.Serve(s.Listener, p9.Proto(), p9.FSConnHandler(s.Filesystem, constants.Plan9Msize))
 		close(s.ErrChan)
 	}()
 
-	// Just before returning, check to see if we got an error off server startup.
-	select {
-	case err := <-s.ErrChan:
-		return fmt.Errorf("starting 9p server: %w", err)
-	default:
-		logrus.Infof("Successfully started 9p server on %s for directory %s", s.Listener.Addr().String(), s.ExposedDir)
-		return nil
-	}
+	// Wait briefly for potential startup errors.
+	select {
+	case err := <-s.ErrChan:
+		return fmt.Errorf("starting 9p server: %w", err)
+	case <-time.After(100 * time.Millisecond):
+		logrus.WithFields(logrus.Fields{
+			"addr": s.Listener.Addr().String(),
+			"dir":  s.ExposedDir,
+		}).Info("Successfully started 9p server")
+		return nil
+	}

71-81: Clarify shutdown semantics and wrap errors for operators.
Warn about client impact and wrap Close error.

-// Stop a running server.
-// Please note that this does *BAD THINGS* to clients if they are still running
-// when the server stops. Processes get stuck in I/O deep sleep and zombify, and
-// nothing I do other than restarting the VM can remove the zombies.
+// Stop a running server.
+// WARNING: Stopping while clients are active may leave client processes
+// unresponsive; a VM restart might be required. Ensure clients disconnect first.
 func (s *Server) Stop() error {
 	if err := s.Listener.Close(); err != nil {
-		return err
+		return fmt.Errorf("failed to close 9p server listener: %w", err)
 	}
-	logrus.Infof("Successfully stopped 9p server for directory %s", s.ExposedDir)
+	logrus.WithField("dir", s.ExposedDir).Info("Successfully stopped 9p server")
 	return nil
 }

83-87: Provide context‑aware wait and handle net.ErrClosed as normal shutdown.
Avoid indefinite block; treat graceful Close as non‑error; keep back‑compat by delegating the old method.

-// WaitForError from a running server.
-func (s *Server) WaitForError() error {
-	err := <-s.ErrChan
-	return err
-}
+// WaitForError from a running server (back‑compat).
+func (s *Server) WaitForError() error {
+	return s.WaitForErrorCtx(context.Background())
+}
+
+// WaitForErrorCtx waits for a server error or context cancellation.
+func (s *Server) WaitForErrorCtx(ctx context.Context) error {
+	select {
+	case err, ok := <-s.ErrChan:
+		if !ok {
+			return nil
+		}
+		// Treat listener closure as graceful shutdown.
+		if errors.Is(err, net.ErrClosed) || errors.Is(err, os.ErrClosed) {
+			return nil
+		}
+		return err
+	case <-ctx.Done():
+		return ctx.Err()
+	}
+}

To ensure this change won’t break callers, please verify usages:

#!/bin/bash
# Find uses of WaitForError and ErrChan across the repo
rg -nP --glob '!**/vendor/**' '\bWaitForError\s*\(' -C2
rg -nP --glob '!**/vendor/**' '\.ErrChan\b' -C2
vendor/github.com/DeedleFake/p9/dir_darwin.go (1)

35-43: Critical: ATime conversion uses non-existent method.

Line 37 calls sys.Atimespec.Unix(), which doesn't exist on Darwin's syscall.Timespec. The struct has Sec and Nsec fields instead. This will cause a compilation error.

Apply this diff:

-		ATime:     time.Unix(sys.Atimespec.Unix()),
+		ATime:     time.Unix(sys.Atimespec.Sec, sys.Atimespec.Nsec),
vendor/github.com/DeedleFake/p9/p9.go (1)

25-39: Duplicate: Open-mode flag values still don't match 9P2000 spec.

The past review comment on lines 25-39 correctly identified that the flag bit patterns are offset by one position. This is a critical protocol compatibility issue that remains unresolved. Since this is vendored code, consider:

  1. Opening an issue with the upstream library maintainer.
  2. Patching the vendored code locally with the correct values.
  3. Verifying whether the servers and clients in use are tolerant of these non-standard values.
vendor/github.com/DeedleFake/p9/client.go (4)

45-47: Duplicate: FID generation vulnerable to overflow.

The past review comment correctly identified that the atomic uint32 counter will overflow after 2^32 operations, potentially causing FID conflicts in long-running clients. Since this is vendored code:

  1. Consider reporting to upstream maintainer.
  2. Evaluate whether the expected usage pattern (client lifespan and FID allocation rate) makes overflow likely in practice.
  3. If overflow is a realistic concern, patch locally to handle wraparound or use a larger counter type.

49-69: Duplicate: Unsafe type assertion in Handshake can panic.

The past review comments correctly identified that the type assertion rsp.(*Rversion) at line 61 lacks safety checks and will panic if the server returns an error response or unexpected type. This is a critical reliability issue. Since this is vendored code, consider patching locally or reporting upstream.


71-91: Duplicate: Unsafe type assertion in Auth can panic.

The past review comment correctly identified that the type assertion rsp.(*Rauth) at line 84 will panic if the response is not of the expected type. This is a critical issue that should be addressed either by patching the vendored code locally or reporting to the upstream maintainer.


93-120: Duplicate: Unsafe type assertion in Attach can panic.

The past review comment correctly identified that the type assertion rsp.(*Rattach) at line 113 will panic if the server returns an unexpected response. This is a critical reliability issue that affects the robustness of the client. Consider patching the vendored code to use safe type assertions with error handling.

vendor/github.com/DeedleFake/p9/proto/client.go (2)

90-97: Break on non-EOF read errors to avoid tight loop

Continuing on non-EOF/non-cancel errors can loop indefinitely on a broken conn. Return (or backoff) instead.

-        if (ctx.Err() != nil) || (err == io.EOF) {
-            return
-        }
-
-        continue
+        if (ctx.Err() != nil) || (err == io.EOF) {
+            return
+        }
+        log.Printf("client reader: receive error: %v", err)
+        return

138-145: Never allocate reserved NoTag (0xFFFF)

Allocator can yield NoTag on wrap-around. Skip it both when returning and when advancing.

-        case c.nextTag <- nextTag:
-            for {
-                nextTag++
-                if _, ok := tags[nextTag]; !ok {
-                    break
-                }
-            }
+        case c.nextTag <- func() uint16 {
+            if nextTag == NoTag {
+                nextTag++
+            }
+            t := nextTag
+            for {
+                nextTag++
+                if nextTag == NoTag {
+                    nextTag++
+                }
+                if _, ok := tags[nextTag]; !ok {
+                    break
+                }
+            }
+            return t
+        }():
vendor/github.com/DeedleFake/p9/proto/encoding.go (2)

150-153: Unsafe uint16 read into uint32 is endianness-unsafe

Avoid unsafe.Pointer; read into a uint16 and widen.

-        default:
-            d.read((*uint16)(unsafe.Pointer(&length)))
+        default:
+            var len16 uint16
+            d.read(&len16)
+            length = uint32(len16)

168-171: Avoid unsafe []byte→string conversion

Use a safe conversion; preserves Go’s string immutability.

-        v.SetString(*(*string)(unsafe.Pointer(&buf)))
+        v.SetString(string(buf))
vendor/github.com/DeedleFake/p9/proto/proto.go (2)

85-94: Frame bound should exclude the 4‑byte size header; add short-size guard

Set limit to size-4 and reject frames smaller than header to prevent over/under‑reads.

-    if (msize > 0) && (size > msize) {
+    if (msize > 0) && (size > msize) {
         return nil, NoTag, util.Errorf("receive: %w", ErrLargeMessage)
     }
-
-    lr := &util.LimitedReader{
+    if size < 4 {
+        return nil, NoTag, util.Errorf("receive: short message size: %d", size)
+    }
+    lr := &util.LimitedReader{
         R: r,
-        N: size,
+        N: size - 4,
         E: ErrLargeMessage,
     }

101-108: Remove unreachable error check in unknown-type path

The inner err check is dead code here.

-    t := p.TypeFromID(msgType)
-    if t == nil {
-        if err != nil {
-            return nil, NoTag, err
-        }
-
-        return nil, NoTag, util.Errorf("receive: invalid message type: %v", msgType)
-    }
+    t := p.TypeFromID(msgType)
+    if t == nil {
+        return nil, NoTag, util.Errorf("receive: invalid message type: %v", msgType)
+    }
vendor/github.com/DeedleFake/p9/fs.go (6)

499-512: Directory read offset handling deviates from 9P; validate offsets or document.

Spec requires offset 0 or previous offset+len. Current code ignores offset. Track and validate per-FID dir read offsets.

I can draft a minimal per-file lastOff/lastLen in fsFile and enforce the rule if desired.


179-180: Remove unsafe string→[]byte; use safe conversion.

unsafe.Pointer here violates memory safety for strings.

-  sum := sha256.Sum256(*(*[]byte)(unsafe.Pointer(&p)))
+  sum := sha256.Sum256([]byte(p))

Also drop the unused unsafe import.


244-249: Tflush: don’t return Rerror; minimally return Rflush (document limitation).

Returning an error breaks clients that send flush. If true cancellation isn’t implemented, reply with &Rflush{} and document semantics.

 func (h *fsHandler) flush(msg *Tflush) interface{} {
-  // TODO: Implement this.
-  return &Rerror{
-    Ename: "flush is not supported",
-  }
+  // Note: does not guarantee cancellation of in-flight ops.
+  return &Rflush{}
 }

536-537: Use exclusive lock for writes; RLock risks races.

Writes mutate file state; RLock allows concurrent writers.

-  file.RLock()         // Somewhat ironic that this doesn't require a
-  defer file.RUnlock() // full lock like read() does.
+  file.Lock()
+  defer file.Unlock()

593-598: Fix error check after clunk in Remove.

rsp is a protocol message, not error. Check *Rerror.

-  if _, ok := rsp.(error); ok {
+  if _, ok := rsp.(*Rerror); ok {
     return rsp
   }

719-729: Close(): lock per file during close to avoid races with in‑flight ops.

Synchronize close with other operations.

 func (h *fsHandler) Close() error {
   h.fids.Range(func(k, v interface{}) bool {
     file := v.(*fsFile)
-    if file.file != nil {
-      file.file.Close()
-    }
+    file.Lock()
+    if file.file != nil {
+      _ = file.file.Close()
+      file.file = nil
+    }
+    file.Unlock()
     return true
   })
 
   return nil
 }
vendor/github.com/DeedleFake/p9/stat.go (1)

135-153: Avoid unsafe conversion in FileMode.String().

Use string(buf); remove unsafe import if now unused.

 func (m FileMode) String() string {
   buf := []byte("----------")
   ...
-  return *(*string)(unsafe.Pointer(&buf))
+  return string(buf)
 }
vendor/github.com/DeedleFake/p9/dir.go (3)

18-20: Fix path join to prevent share escape on Windows and absolute paths.

Leading separators make p absolute, causing Join(base, p) to ignore the base. Strip leading separators before join.

Apply:

 func (d Dir) path(p string) string {
-	return filepath.Join(string(d), filepath.FromSlash(p))
+	ps := filepath.FromSlash(p)
+	// Force relative join by trimming leading separators
+	for len(ps) > 0 && (ps[0] == os.PathSeparator) {
+		ps = ps[1:]
+	}
+	return filepath.Join(string(d), ps)
 }

105-122: Create(dir): don’t open directories with file flags; open after Mkdir using os.Open and return.

Opening a directory via os.OpenFile(..., O_CREATE, ...) is invalid and will fail on most platforms. Return a handle after Mkdir.

 func (d Dir) Create(p string, perm FileMode, mode uint8) (File, error) {
   p = d.path(p)
 
   if perm&ModeDir != 0 {
     err := os.Mkdir(p, os.FileMode(perm.Perm()))
     if err != nil {
       return nil, err
     }
+    f, err := os.Open(p)
+    return &dirFile{File: f}, err
   }
 
   flag := toOSFlags(mode)
 
   file, err := os.OpenFile(p, flag|os.O_CREATE, os.FileMode(perm.Perm()))
   return &dirFile{
     File: file,
   }, err
 }

239-260: Make OREAD/OWRITE/ORDWR/OEXEC mutually exclusive; treat them as mode values, not bitflags.

Current bitwise checks can yield wrong flags (e.g., OEXEC matching OWRITE). Use low 2-bit mask and a switch.

-func toOSFlags(mode uint8) (flag int) {
-  if mode&OREAD != 0 {
-    flag |= os.O_RDONLY
-  }
-  if mode&OWRITE != 0 {
-    flag |= os.O_WRONLY
-  }
-  if mode&ORDWR != 0 {
-    flag |= os.O_RDWR
-  }
+func toOSFlags(mode uint8) (flag int) {
+  switch mode & 0x3 { // OREAD=0, OWRITE=1, ORDWR=2, OEXEC=3
+  case OWRITE:
+    flag |= os.O_WRONLY
+  case ORDWR:
+    flag |= os.O_RDWR
+  default:
+    // OREAD or OEXEC -> read-only (O_RDONLY is 0)
+  }
   if mode&OTRUNC != 0 {
     flag |= os.O_TRUNC
   }
   //if mode&OEXCL != 0 {
   //	flag |= os.O_EXCL
   //}
   //if mode&OAPPEND != 0 {
   //	flag |= os.O_APPEND
   //}
 
   return flag
 }
vendor/github.com/DeedleFake/p9/msg.go (1)

207-217: Error handling issue in vendored code.

The past review comment correctly identified that P9Encode methods don't check errors after each proto.Write call, which could lead to returning corrupted data if an intermediate write fails. The same issue exists in Twstat.P9Encode (lines 240-255).

Since this is vendored third-party code from github.com/DeedleFake/p9, consider:

  1. Checking if a newer version of the library addresses this issue
  2. Reporting the issue upstream to the library maintainer
  3. Documenting this known limitation if the risk is acceptable

Check if a newer version of the p9 library exists:

#!/bin/bash
# Check the vendored version and compare with upstream
echo "=== Vendored version information ==="
if [ -f "go.mod" ]; then
  grep "github.com/DeedleFake/p9" go.mod
fi

echo ""
echo "=== Latest releases from GitHub ==="
gh api repos/DeedleFake/p9/releases --jq '.[0:3] | .[] | {tag_name, published_at, name}'
vendor/github.com/DeedleFake/p9/remote.go (1)

37-43: Path splitting issue in vendored code.

The past review comment correctly identified that strings.Split on an absolute path like "/a/b" produces ["", "a", "b"], where the leading empty string is invalid for Twalk. The current logic doesn't filter out this empty component.

Since this is vendored third-party code from github.com/DeedleFake/p9:

  1. Verify if a newer version addresses this issue
  2. Consider reporting upstream if not fixed
  3. Test that the 9P use cases in this PR don't trigger this path with leading slashes

Search the codebase for calls to Open, Create, Remove, and Stat methods that might pass absolute paths:

#!/bin/bash
# Search for Remote method calls that might pass absolute paths
echo "=== Searching for Remote.Open/Create/Remove/Stat calls ==="
rg -n -C2 -tgo '\.(Open|Create|Remove|Stat)\s*\(\s*"/' --glob '!vendor/**'

echo ""
echo "=== Check if paths are always relative in fs9p usage ==="
rg -n -C3 -tgo 'Remote\.(Open|Create|Remove|Stat)' pkg/fileserver/fs9p/
🧹 Nitpick comments (9)
pkg/fileserver/fs9p/server.go (3)

3-13: Add missing imports for timeout/context/error handling.
Required by the startup timeout and ctx-aware wait refactor below.

 import (
+	"context"
+	"errors"
 	"fmt"
 	"net"
 	"os"
 	"path/filepath"
+	"time"
 
 	"github.com/DeedleFake/p9"
 	"github.com/DeedleFake/p9/proto"
 	"github.com/crc-org/crc/v2/pkg/crc/constants"
 	"github.com/sirupsen/logrus"
 )

45-51: Minor: Initialize Server with a struct literal.
Clearer and less error‑prone.

-toReturn := new(Server)
-toReturn.Listener = listener
-toReturn.Filesystem = fs
-toReturn.ExposedDir = exposeDir
-toReturn.ErrChan = errChan
-
-return toReturn, nil
+return &Server{
+	Listener:   listener,
+	Filesystem: fs,
+	ExposedDir: exposeDir,
+	ErrChan:    errChan,
+}, nil

15-24: Consider making fields unexported to tighten API surface.
Expose methods, not internals (e.g., errChan), to prevent external sends/close.

 type Server struct {
 	// Listener this server is bound to
 	Listener net.Listener
 	// Plan9 Filesystem type that holds the exposed directory
 	Filesystem p9.FileSystem
 	// Directory this server exposes
 	ExposedDir string
 	// Errors from the server being started will come out here
-	ErrChan chan error
+	ErrChan chan error // consider: make this unexported (errChan) and use WaitForError/WaitForErrorCtx
 }
vendor/github.com/DeedleFake/p9/README.md (1)

12-49: Fix markdown formatting issues.

Static analysis flags hard tabs and heading style inconsistencies. Replace hard tabs with spaces and use consistent heading styles (e.g., setext throughout or atx throughout).

vendor/github.com/DeedleFake/p9/proto/client.go (1)

69-74: Close should wait for goroutines to exit

Ensure reader/coord exit to avoid races/leaks; wait briefly for c.done.

 func (c *Client) Close() error {
     c.cancel()
-    return c.c.Close()
+    _ = c.c.Close()
+    select {
+    case <-c.done:
+    case <-time.After(1 * time.Second):
+    }
+    return nil
 }
vendor/github.com/DeedleFake/p9/proto/encoding.go (1)

85-96: Speed up []byte encoding path

For slices of bytes, write the payload in one shot instead of byte-by-byte.

-    case reflect.Array, reflect.Slice:
-        switch v.Type().Elem().Kind() {
-        case reflect.Uint8:
-            e.mode(uint32(v.Len()))
-        default:
-            e.mode(uint16(v.Len()))
-        }
-
-        for i := 0; i < v.Len(); i++ {
-            e.encode(v.Index(i))
-        }
+    case reflect.Array, reflect.Slice:
+        switch v.Type().Elem().Kind() {
+        case reflect.Uint8:
+            e.mode(uint32(v.Len()))
+            // Fast path for []byte
+            e.mode(v.Bytes())
+        default:
+            e.mode(uint16(v.Len()))
+            for i := 0; i < v.Len(); i++ {
+                e.encode(v.Index(i))
+            }
+        }
vendor/github.com/DeedleFake/p9/dir.go (1)

190-196: Consider allowing OEXEC on read‑only FS.

Denying OEXEC blocks execute-only opens that don’t mutate state. If safe for your use case, remove OEXEC from the rejection mask.

- if mode&(OWRITE|ORDWR|OEXEC|OTRUNC|OCEXEC|ORCLOSE) != 0 {
+ if mode&(OWRITE|ORDWR|OTRUNC|OCEXEC|ORCLOSE) != 0 {
vendor/github.com/DeedleFake/p9/fs.go (1)

205-220: Be more lenient in version negotiation.

Many clients send “9P2000.u/L”. Consider replying Rversion with Version (9P2000) and adjusted msize instead of hard‑erroring.

- if msg.Version != Version {
-   return &Rerror{ Ename: ErrUnsupportedVersion.Error() }
- }
+ // Accept unknown variants by responding with our Version.
+ // If strict negotiation is desired, gate behind a flag.
pkg/crc/machine/start.go (1)

267-269: Make the 9pfs invocation explicit (use -p or use mount -t 9p).

Current code calls sshRunner.Run("9pfs 192.168.127.1", mount.Target) which yields 9pfs 192.168.127.1 <mountpoint> — that can work (9pfs accepts server then mountpoint and will default to TCP port 564), but typical/robust usage is to specify the port/options explicitly (e.g. 9pfs 192.168.127.1 -p 564 <mountpoint>) or use mount -t 9p -o trans=tcp,port=564 .... Confirm whether you want to rely on the default port here or change the invocation to include -p constants.Plan9TcpPort (or switch to a mount(9p) form) for clarity and future-proofing. (pmikkelsen.com)

Location: pkg/crc/machine/start.go:267-269.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9d23c41 and f176d25.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (33)
  • cmd/crc/cmd/daemon.go (5 hunks)
  • go.mod (1 hunks)
  • pkg/crc/constants/constants.go (1 hunks)
  • pkg/crc/machine/libhvee/driver_windows.go (1 hunks)
  • pkg/crc/machine/start.go (2 hunks)
  • pkg/fileserver/fs9p/server.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/LICENSE (1 hunks)
  • vendor/github.com/DeedleFake/p9/README.md (1 hunks)
  • vendor/github.com/DeedleFake/p9/addr.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/addr_other.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/addr_unix.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/client.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_darwin.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_linux.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_other.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_plan9.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_windows.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/doc.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/encoding.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/fs.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/internal/debug/debug.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/internal/debug/nodebug.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/internal/util/util.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/msg.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/p9.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/proto/client.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/proto/encoding.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/proto/proto.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/proto/server.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/remote.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/stat.go (1 hunks)
  • vendor/modules.txt (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (12)
  • vendor/github.com/DeedleFake/p9/LICENSE
  • go.mod
  • vendor/github.com/DeedleFake/p9/addr.go
  • vendor/github.com/DeedleFake/p9/dir_linux.go
  • pkg/crc/constants/constants.go
  • vendor/github.com/DeedleFake/p9/addr_unix.go
  • vendor/github.com/DeedleFake/p9/proto/server.go
  • vendor/modules.txt
  • vendor/github.com/DeedleFake/p9/addr_other.go
  • vendor/github.com/DeedleFake/p9/internal/debug/nodebug.go
  • cmd/crc/cmd/daemon.go
  • pkg/crc/machine/libhvee/driver_windows.go
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: redbeam
PR: crc-org/crc#4866
File: pkg/fileserver/fs9p/shares.go:0-0
Timestamp: 2025-08-06T09:48:10.441Z
Learning: The vsock implementation in pkg/fileserver/fs9p/shares.go was temporarily removed because 9pfs doesn't fully support it yet. It will be added back when 9pfs provides proper vsock support.
🧬 Code graph analysis (19)
vendor/github.com/DeedleFake/p9/internal/debug/debug.go (1)
vendor/github.com/DeedleFake/p9/internal/debug/nodebug.go (1)
  • Log (5-5)
vendor/github.com/DeedleFake/p9/dir_windows.go (1)
vendor/github.com/DeedleFake/p9/stat.go (3)
  • DirEntry (255-267)
  • FileMode (23-23)
  • ModeFromOS (43-78)
vendor/github.com/DeedleFake/p9/dir_other.go (1)
vendor/github.com/DeedleFake/p9/stat.go (3)
  • DirEntry (255-267)
  • FileMode (23-23)
  • ModeFromOS (43-78)
pkg/fileserver/fs9p/server.go (3)
vendor/github.com/DeedleFake/p9/fs.go (2)
  • FileSystem (21-34)
  • FSConnHandler (162-167)
vendor/github.com/DeedleFake/p9/proto/server.go (1)
  • Serve (17-42)
pkg/crc/constants/constants.go (1)
  • Plan9Msize (59-59)
vendor/github.com/DeedleFake/p9/proto/encoding.go (1)
vendor/github.com/DeedleFake/p9/internal/util/util.go (1)
  • Errorf (44-52)
pkg/crc/machine/start.go (1)
pkg/os/exec.go (1)
  • RunPrivileged (48-59)
vendor/github.com/DeedleFake/p9/internal/util/util.go (1)
vendor/github.com/DeedleFake/p9/proto/encoding.go (1)
  • Read (34-38)
vendor/github.com/DeedleFake/p9/fs.go (7)
vendor/github.com/DeedleFake/p9/stat.go (4)
  • Stat (156-168)
  • DirEntry (255-267)
  • StatChanges (316-318)
  • FileMode (23-23)
vendor/github.com/DeedleFake/p9/p9.go (7)
  • QID (42-46)
  • QIDType (49-49)
  • IOHeaderSize (70-70)
  • Version (10-10)
  • QTAuth (58-58)
  • NoFID (18-18)
  • QTDir (54-54)
vendor/github.com/DeedleFake/p9/proto/server.go (3)
  • MessageHandler (135-139)
  • ConnHandler (112-114)
  • ConnHandlerFunc (125-125)
vendor/github.com/DeedleFake/p9/internal/debug/debug.go (1)
  • Log (10-12)
vendor/github.com/DeedleFake/p9/internal/debug/nodebug.go (1)
  • Log (5-5)
vendor/github.com/DeedleFake/p9/msg.go (27)
  • Tversion (79-82)
  • Tversion (84-84)
  • Rerror (118-120)
  • Rversion (86-89)
  • Tauth (95-99)
  • Rauth (101-103)
  • Tflush (126-128)
  • Tattach (105-110)
  • Rattach (112-114)
  • Twalk (133-137)
  • Rwalk (139-141)
  • Topen (143-146)
  • Ropen (148-151)
  • Tcreate (153-158)
  • Rcreate (160-163)
  • Tread (165-169)
  • Rread (171-173)
  • Twrite (175-179)
  • Rwrite (181-183)
  • Tclunk (185-187)
  • Rclunk (189-190)
  • Tremove (192-194)
  • Rremove (196-197)
  • Tstat (199-201)
  • Rstat (203-205)
  • Twstat (235-238)
  • Rwstat (278-279)
vendor/github.com/DeedleFake/p9/encoding.go (1)
  • WriteDir (33-42)
vendor/github.com/DeedleFake/p9/dir_plan9.go (3)
vendor/github.com/DeedleFake/p9/stat.go (4)
  • DirEntry (255-267)
  • FileMode (23-23)
  • ModeFromOS (43-78)
  • Stat (156-168)
vendor/github.com/DeedleFake/p9/dir.go (1)
  • Dir (16-16)
vendor/github.com/DeedleFake/p9/p9.go (2)
  • QID (42-46)
  • Version (10-10)
vendor/github.com/DeedleFake/p9/proto/client.go (5)
vendor/github.com/DeedleFake/p9/msg.go (1)
  • Proto (75-77)
vendor/github.com/DeedleFake/p9/proto/proto.go (3)
  • Proto (38-41)
  • NoTag (27-27)
  • ErrClientClosed (22-22)
vendor/github.com/DeedleFake/p9/client.go (3)
  • NewClient (30-32)
  • Client (22-26)
  • Dial (36-43)
vendor/github.com/DeedleFake/p9/internal/debug/debug.go (1)
  • Log (10-12)
vendor/github.com/DeedleFake/p9/internal/debug/nodebug.go (1)
  • Log (5-5)
vendor/github.com/DeedleFake/p9/dir_darwin.go (3)
vendor/github.com/DeedleFake/p9/stat.go (4)
  • DirEntry (255-267)
  • FileMode (23-23)
  • ModeFromOS (43-78)
  • Stat (156-168)
vendor/github.com/DeedleFake/p9/dir.go (1)
  • Dir (16-16)
vendor/github.com/DeedleFake/p9/p9.go (3)
  • QID (42-46)
  • QIDType (49-49)
  • Version (10-10)
vendor/github.com/DeedleFake/p9/stat.go (3)
vendor/github.com/DeedleFake/p9/p9.go (3)
  • QIDType (49-49)
  • QID (42-46)
  • Version (10-10)
vendor/github.com/DeedleFake/p9/proto/encoding.go (3)
  • Write (25-31)
  • Read (34-38)
  • Size (14-20)
vendor/github.com/DeedleFake/p9/internal/util/util.go (1)
  • LimitedReader (13-17)
vendor/github.com/DeedleFake/p9/proto/proto.go (4)
vendor/github.com/DeedleFake/p9/msg.go (1)
  • Proto (75-77)
vendor/github.com/DeedleFake/p9/proto/encoding.go (3)
  • Read (34-38)
  • Write (25-31)
  • Size (14-20)
vendor/github.com/DeedleFake/p9/p9.go (1)
  • NoTag (15-15)
vendor/github.com/DeedleFake/p9/internal/util/util.go (2)
  • Errorf (44-52)
  • LimitedReader (13-17)
vendor/github.com/DeedleFake/p9/dir.go (3)
vendor/github.com/DeedleFake/p9/stat.go (5)
  • Stat (156-168)
  • DirEntry (255-267)
  • StatChanges (316-318)
  • FileMode (23-23)
  • ModeDir (27-27)
vendor/github.com/DeedleFake/p9/fs.go (4)
  • File (100-119)
  • Attachment (44-74)
  • FileSystem (21-34)
  • QIDFS (89-91)
vendor/github.com/DeedleFake/p9/p9.go (7)
  • OWRITE (27-27)
  • ORDWR (28-28)
  • OEXEC (29-29)
  • OTRUNC (31-31)
  • OCEXEC (32-32)
  • ORCLOSE (33-33)
  • OREAD (26-26)
vendor/github.com/DeedleFake/p9/msg.go (6)
vendor/github.com/DeedleFake/p9/proto/proto.go (2)
  • NewProto (44-60)
  • Proto (38-41)
vendor/github.com/DeedleFake/p9/p9.go (2)
  • Version (10-10)
  • QID (42-46)
vendor/github.com/DeedleFake/p9/proto/client.go (1)
  • P9NoTag (221-223)
vendor/github.com/DeedleFake/p9/stat.go (3)
  • FileMode (23-23)
  • Stat (156-168)
  • ErrLargeStat (18-18)
vendor/github.com/DeedleFake/p9/proto/encoding.go (2)
  • Write (25-31)
  • Read (34-38)
vendor/github.com/DeedleFake/p9/internal/util/util.go (1)
  • LimitedReader (13-17)
vendor/github.com/DeedleFake/p9/encoding.go (2)
vendor/github.com/DeedleFake/p9/stat.go (2)
  • DirEntry (255-267)
  • Stat (156-168)
vendor/github.com/DeedleFake/p9/proto/encoding.go (2)
  • Read (34-38)
  • Write (25-31)
vendor/github.com/DeedleFake/p9/p9.go (1)
vendor/github.com/DeedleFake/p9/stat.go (1)
  • FileMode (23-23)
vendor/github.com/DeedleFake/p9/client.go (4)
vendor/github.com/DeedleFake/p9/proto/client.go (3)
  • Client (18-31)
  • NewClient (35-56)
  • Dial (60-67)
vendor/github.com/DeedleFake/p9/msg.go (8)
  • Proto (75-77)
  • Tversion (79-82)
  • Tversion (84-84)
  • Rversion (86-89)
  • Tauth (95-99)
  • Rauth (101-103)
  • Tattach (105-110)
  • Rattach (112-114)
vendor/github.com/DeedleFake/p9/p9.go (3)
  • Version (10-10)
  • NoFID (18-18)
  • QID (42-46)
vendor/github.com/DeedleFake/p9/remote.go (1)
  • Remote (19-27)
vendor/github.com/DeedleFake/p9/remote.go (7)
vendor/github.com/DeedleFake/p9/client.go (1)
  • Client (22-26)
vendor/github.com/DeedleFake/p9/proto/client.go (1)
  • Client (18-31)
vendor/github.com/DeedleFake/p9/p9.go (4)
  • QID (42-46)
  • QIDType (49-49)
  • Version (10-10)
  • IOHeaderSize (70-70)
vendor/github.com/DeedleFake/p9/msg.go (14)
  • Twalk (133-137)
  • Rwalk (139-141)
  • Topen (143-146)
  • Ropen (148-151)
  • Tcreate (153-158)
  • Rcreate (160-163)
  • Tremove (192-194)
  • Tread (165-169)
  • Rread (171-173)
  • Twrite (175-179)
  • Rwrite (181-183)
  • Tclunk (185-187)
  • Tstat (199-201)
  • Rstat (203-205)
vendor/github.com/DeedleFake/p9/stat.go (3)
  • FileMode (23-23)
  • Stat (156-168)
  • DirEntry (255-267)
vendor/github.com/DeedleFake/p9/internal/util/util.go (1)
  • Errorf (44-52)
vendor/github.com/DeedleFake/p9/encoding.go (1)
  • ReadDir (16-30)
🪛 markdownlint-cli2 (0.18.1)
vendor/github.com/DeedleFake/p9/README.md

12-12: Heading style
Expected: setext; Actual: atx

(MD003, heading-style)


17-17: Hard tabs
Column: 1

(MD010, no-hard-tabs)


21-21: Heading style
Expected: setext; Actual: atx

(MD003, heading-style)


26-26: Hard tabs
Column: 1

(MD010, no-hard-tabs)


32-32: Hard tabs
Column: 1

(MD010, no-hard-tabs)


37-37: Hard tabs
Column: 1

(MD010, no-hard-tabs)


43-43: Hard tabs
Column: 1

(MD010, no-hard-tabs)


49-49: Hard tabs
Column: 1

(MD010, no-hard-tabs)

⏰ 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). (14)
  • GitHub Check: build (ubuntu-22.04, 1.24)
  • GitHub Check: build (ubuntu-latest, 1.24)
  • GitHub Check: verify-devcontainer
  • GitHub Check: build (macOS-14, 1.24)
  • GitHub Check: build (macOS-13, 1.24)
  • GitHub Check: build (ubuntu-latest, 1.24)
  • GitHub Check: build-qe (linux, arm64)
  • GitHub Check: build-qe (darwin, arm64)
  • GitHub Check: Run OKD bundle with crc (1.24)
  • GitHub Check: build-qe (darwin, amd64)
  • GitHub Check: build-qe (linux, amd64)
  • GitHub Check: build-qe (windows, amd64)
  • GitHub Check: build-installer (windows-2022, 1.24)
  • GitHub Check: Konflux kflux-prd-rh02 / crc-binary-on-pull-request
🔇 Additional comments (16)
vendor/github.com/DeedleFake/p9/internal/debug/debug.go (1)

1-12: Vendored debug implementation looks correct.

This is vendored code from the DeedleFake/p9 library. The conditional compilation approach (p9debug build tag enables logging, !p9debug provides no-op) is standard practice for optional debug instrumentation. The implementation is straightforward and appropriate for debug logging.

pkg/fileserver/fs9p/server.go (1)

31-40: Path validation looks solid.
Absolute path + existence + directory check is correct and cross‑platform.

vendor/github.com/DeedleFake/p9/dir_other.go (1)

1-14: LGTM: Fallback implementation is correct.

This provides a reasonable fallback for OSes without platform-specific syscall data. The minimal DirEntry population is appropriate.

vendor/github.com/DeedleFake/p9/dir_windows.go (1)

1-27: LGTM: Windows-specific implementation is correct.

The use of LastAccessTime.Nanoseconds() is valid for Windows syscall.Filetime. Fallback handling is appropriate.

vendor/github.com/DeedleFake/p9/doc.go (1)

1-63: LGTM: Package documentation is comprehensive and clear.

The documentation provides a solid overview of the 9P protocol handling, client/server abstractions, and usage patterns.

vendor/github.com/DeedleFake/p9/dir_darwin.go (1)

46-62: LGTM: GetQID implementation is correct.

The QID retrieval using os.Stat and inode extraction from syscall.Stat_t is correct for Darwin. Error handling is appropriate.

vendor/github.com/DeedleFake/p9/encoding.go (2)

16-30: LGTM – ReadDir implementation is sound.

The function correctly handles EOF as normal termination and accumulates directory entries. The warning about buffering is a helpful guard against protocol issues.


33-42: LGTM – WriteDir implementation is correct.

The function correctly serializes directory entries and fails fast on the first write error, which is appropriate for this protocol layer.

vendor/github.com/DeedleFake/p9/internal/util/util.go (2)

27-39: LimitedReader.Read logic is correct.

The implementation correctly enforces the byte limit and returns the custom error when exhausted. The check lr.N <= 0 at line 28 is technically redundant (since N is uint32 and cannot be negative), but it doesn't affect correctness and may be defensive coding for clarity.


44-52: Errorf correctly preserves sentinel errors.

The function correctly returns io.EOF directly when found in the arguments, avoiding wrapping it. The type assertion at line 47 is safe since io.EOF is of type error.

vendor/github.com/DeedleFake/p9/dir_plan9.go (2)

10-31: LGTM – infoToEntry safely extracts Plan 9 metadata.

The function correctly uses a two-value type assertion to safely extract Plan 9-specific metadata when available, falling back to basic metadata otherwise.


33-49: LGTM – GetQID safely extracts QID on Plan 9.

The method correctly uses a safe type assertion and returns a clear error if the platform-specific metadata is unavailable. The QID construction from syscall.Dir is appropriate for Plan 9.

vendor/github.com/DeedleFake/p9/p9.go (2)

7-19: LGTM – Core 9P constants are correct.

The version string and sentinel values are properly defined and match the 9P2000 protocol specification.


41-71: LGTM – QID and related constants are correct.

The QID structure, QIDType constants, and FileMode conversion are correctly implemented according to the 9P2000 specification. The bit patterns for QIDType properly use descending bit positions.

vendor/github.com/DeedleFake/p9/client.go (1)

11-43: LGTM – Client structure and constructors are well-designed.

The Client type appropriately embeds the proto.Client and maintains per-client FID state. The constructor and Dial function are correctly implemented.

vendor/github.com/DeedleFake/p9/proto/client.go (1)

50-51: Default msize seems low; confirm Tversion negotiation adjusts it

1024B limits throughput. Verify the caller negotiates a larger msize (e.g., 8–64 KiB) via Tversion before regular traffic.

Comment on lines +52 to +60
atime, ok1 := changes.ATime()
mtime, ok2 := changes.MTime()
if ok1 || ok2 {
err := os.Chtimes(p, atime, mtime)
if err != nil {
return err
}
}

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Avoid clobbering unspecified times in WriteStat; handle partial ATime/MTime updates safely.

Calling os.Chtimes(p, atime, mtime) when only one is set will overwrite the other with zero time or error. Preserve the unspecified value.

-  atime, ok1 := changes.ATime()
-  mtime, ok2 := changes.MTime()
-  if ok1 || ok2 {
-    err := os.Chtimes(p, atime, mtime)
+  atime, ok1 := changes.ATime()
+  mtime, ok2 := changes.MTime()
+  if ok1 || ok2 {
+    // Preserve unspecified field by probing current times (best-effort).
+    fi, err := os.Stat(p)
+    if err != nil {
+      return err
+    }
+    if !ok2 {
+      mtime = fi.ModTime()
+    }
+    if !ok1 {
+      // Best-effort: fall back to current mtime if atime not available.
+      // Consider adding OS-specific atime retrieval for accuracy.
+      atime = fi.ModTime()
+    }
+    err = os.Chtimes(p, atime, mtime)
     if err != nil {
       return err
     }
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
atime, ok1 := changes.ATime()
mtime, ok2 := changes.MTime()
if ok1 || ok2 {
err := os.Chtimes(p, atime, mtime)
if err != nil {
return err
}
}
atime, ok1 := changes.ATime()
mtime, ok2 := changes.MTime()
if ok1 || ok2 {
// Preserve unspecified field by probing current times (best-effort).
fi, err := os.Stat(p)
if err != nil {
return err
}
if !ok2 {
mtime = fi.ModTime()
}
if !ok1 {
// Best-effort: fall back to current mtime if atime not available.
// Consider adding OS-specific atime retrieval for accuracy.
atime = fi.ModTime()
}
err = os.Chtimes(p, atime, mtime)
if err != nil {
return err
}
}
🤖 Prompt for AI Agents
In vendor/github.com/DeedleFake/p9/dir.go around lines 52 to 60, the current
logic calls os.Chtimes(p, atime, mtime) even when only one of ATime/MTime is
provided which can clobber the unspecified time; change it to first stat the
file (os.Stat) to read the existing atime/mtime, then if ok1 is false reuse the
existing atime, and if ok2 is false reuse the existing mtime, and finally call
os.Chtimes with the preserved values (keeping the existing modification/access
times when not supplied) and return any error from that call.

Comment on lines +61 to +67
length, ok := changes.Length()
if ok {
err := os.Truncate(p, int64(length))
if err != nil {
return err
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Guard against uint64→int64 overflow in Truncate.

Large length can wrap negative when cast, corrupting data or erroring.

-  length, ok := changes.Length()
+  length, ok := changes.Length()
   if ok {
-    err := os.Truncate(p, int64(length))
+    if length > math.MaxInt64 {
+      return fmt.Errorf("truncate length too large: %d", length)
+    }
+    err := os.Truncate(p, int64(length))
     if err != nil {
       return err
     }
   }

Add imports:

+  "fmt"
+  "math"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
length, ok := changes.Length()
if ok {
err := os.Truncate(p, int64(length))
if err != nil {
return err
}
}
// At the top of the file, in the import block, add:
import (
// ... other imports ...
"fmt"
"math"
)
// Then, in the function around lines 61–67, replace the existing snippet with:
length, ok := changes.Length()
if ok {
if length > math.MaxInt64 {
return fmt.Errorf("truncate length too large: %d", length)
}
err := os.Truncate(p, int64(length))
if err != nil {
return err
}
}
🤖 Prompt for AI Agents
In vendor/github.com/DeedleFake/p9/dir.go around lines 61–67, guard against
uint64→int64 overflow before calling os.Truncate: if changes.Length() returns a
length greater than int64 max, return a descriptive error instead of casting and
calling Truncate; otherwise safely cast to int64 and call os.Truncate. Add the
necessary imports (math and fmt or errors) to construct the error.

Comment on lines +69 to +75
name, ok := changes.Name()
if ok {
err := os.Rename(p, filepath.Join(base, filepath.FromSlash(name)))
if err != nil {
return err
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Sanitize rename target to a base name to prevent path traversal.

filepath.Join(base, FromSlash(name)) allows ../.. to escape the directory. Restrict to basename.

-  name, ok := changes.Name()
+  name, ok := changes.Name()
   if ok {
-    err := os.Rename(p, filepath.Join(base, filepath.FromSlash(name)))
+    newName := filepath.Base(filepath.FromSlash(name))
+    if newName == "." || newName == "" {
+      return errors.New("invalid new name")
+    }
+    err := os.Rename(p, filepath.Join(base, newName))
     if err != nil {
       return err
     }
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
name, ok := changes.Name()
if ok {
err := os.Rename(p, filepath.Join(base, filepath.FromSlash(name)))
if err != nil {
return err
}
}
name, ok := changes.Name()
if ok {
newName := filepath.Base(filepath.FromSlash(name))
if newName == "." || newName == "" {
return errors.New("invalid new name")
}
err := os.Rename(p, filepath.Join(base, newName))
if err != nil {
return err
}
}

@redbeam redbeam force-pushed the 9p-deedlefake branch 2 times, most recently from 6362817 to 3093c61 Compare October 4, 2025 01:10
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: 5

♻️ Duplicate comments (11)
vendor/github.com/DeedleFake/p9/dir_darwin.go (1)

37-37: ATime conversion uses non-existent Timespec.Unix(); fix to Sec/Nsec fields.

syscall.Timespec on Darwin has Sec and Nsec fields; there's no Unix() method. This will cause a compilation error.

Apply this diff to fix the issue:

-		ATime:     time.Unix(sys.Atimespec.Unix()),
+		ATime:     time.Unix(sys.Atimespec.Sec, sys.Atimespec.Nsec),
vendor/github.com/DeedleFake/p9/dir_linux.go (1)

37-37: ATime conversion uses non-existent Timespec.Unix(); fix to Sec/Nsec fields.

syscall.Timespec on Linux has Sec and Nsec fields; there's no Unix() method. This will cause a compilation error.

Apply this diff to fix the issue:

-		ATime:     time.Unix(sys.Atim.Unix()),
+		ATime:     time.Unix(sys.Atim.Sec, sys.Atim.Nsec),
vendor/github.com/DeedleFake/p9/msg.go (2)

207-217: Acknowledge prior error handling concern in vendored code.

A previous review identified that this method returns buffer bytes even when the final proto.Write fails (line 215), which could produce corrupted output. Since this is vendored code from github.com/DeedleFake/p9 v0.6.12, modifications should be submitted upstream rather than made locally.

Verify whether this concern has been reported to the upstream repository. If not, consider opening an issue at https://github.com/DeedleFake/p9.


240-255: Same error handling concern as Rstat.P9Encode.

This method exhibits the same pattern as Rstat.P9Encode: it returns buffer bytes even when the final proto.Write fails (line 253). As vendored code from github.com/DeedleFake/p9 v0.6.12, this should be addressed upstream.

cmd/crc/cmd/daemon.go (1)

259-259: Security: Entire home directory exposed via 9p.

This concern was already raised in previous reviews. The current implementation exposes constants.GetHomeDir() (the entire user home directory) via 9p without access controls or read-only enforcement. This creates security risks as noted in the existing review comments.

pkg/fileserver/fs9p/server.go (1)

56-70: Race condition: startup error detection is still unreliable.

The non-blocking select (lines 63-69) checks for immediate errors but proto.Serve may not encounter errors until after it starts accepting connections. For example, if the first client triggers a protocol error, that error arrives in ErrChan milliseconds later, well after Start() has returned success.

Apply this diff to wait briefly for startup errors:

 // Start a server created by New9pServer.
 func (s *Server) Start() error {
 	go func() {
 		s.ErrChan <- proto.Serve(s.Listener, p9.Proto(), p9.FSConnHandler(s.Filesystem, constants.Plan9Msize))
 		close(s.ErrChan)
 	}()
 
-	// Just before returning, check to see if we got an error off server startup.
+	// Wait briefly for potential startup errors
 	select {
 	case err := <-s.ErrChan:
 		return fmt.Errorf("starting 9p server: %w", err)
-	default:
+	case <-time.After(100 * time.Millisecond):
 		logrus.Infof("Successfully started 9p server on %s for directory %s", s.Listener.Addr().String(), s.ExposedDir)
 		return nil
 	}
 }

You'll need to import the time package.

vendor/github.com/DeedleFake/p9/proto/client.go (2)

90-99: Stop spinning forever on persistent read errors

This re-raises the unresolved point: when Receive returns a non-EOF error, the loop just continues, so a dead TCP connection causes a tight spin. Please propagate the error (and cancel waiting senders) instead of looping forever.


138-145: Never hand out the reserved NoTag

Reiterating prior feedback: when nextTag reaches 0xFFFF, this allocator returns the reserved NoTag, breaking the protocol on the very next request. Ensure you skip NoTag both when handing out a tag and while searching for the next free value.

vendor/github.com/DeedleFake/p9/proto/encoding.go (1)

146-154: Big-endian decode interprets 16-bit lengths as 32-bit gigabytes

Line [152] writes a 16-bit value into a uint32 via unsafe.Pointer, which only works on little-end machines. On big-endian targets the bytes land in the high half, so a length of 5 turns into 0x00050000, allocating hundreds of KB (worst case >4 GB). Decode into a real uint16 first and widen safely.

-		default:
-			d.read((*uint16)(unsafe.Pointer(&length)))
+		default:
+			var len16 uint16
+			d.read(&len16)
+			length = uint32(len16)
vendor/github.com/DeedleFake/p9/client.go (2)

74-120: Auth/Attach can panic on server errors

Lines [84] and [113] also use unchecked assertions (rsp.(*Rauth), rsp.(*Rattach)). When the server replies with Rerror—which is normal for auth/attach failures—the client panics instead of returning the error. Guard these assertions and surface a clear error.

-	rauth := rsp.(*Rauth)
+	rauth, ok := rsp.(*Rauth)
+	if !ok {
+		return nil, fmt.Errorf("unexpected response type for auth: %T", rsp)
+	}
@@
-	attach := rsp.(*Rattach)
+	attach, ok := rsp.(*Rattach)
+	if !ok {
+		return nil, fmt.Errorf("unexpected response type for attach: %T", rsp)
+	}

53-68: Handshake rejects standard server replies and can panic

Line [61] blindly asserts rsp.(*Rversion), so a legitimate Rerror response (e.g., auth failure) would panic. Even if it succeeds, the strict equality on Line [62] rejects common replies like "9P2000.L" from the Linux kernel, so the handshake always fails against standard servers. Please guard the type assertion and accept Version prefixes so "9P2000" family replies work.

-import (
-	"errors"
-	"net"
-	"sync/atomic"
-
-	"github.com/DeedleFake/p9/proto"
-)
+import (
+	"errors"
+	"fmt"
+	"net"
+	"strings"
+	"sync/atomic"
+
+	"github.com/DeedleFake/p9/proto"
+)
@@
-	version := rsp.(*Rversion)
-	if version.Version != Version {
-		return 0, ErrUnsupportedVersion
-	}
+	version, ok := rsp.(*Rversion)
+	if !ok {
+		return 0, fmt.Errorf("unexpected response type for version: %T", rsp)
+	}
+	if !strings.HasPrefix(version.Version, Version) {
+		return 0, fmt.Errorf("%w: server replied %q", ErrUnsupportedVersion, version.Version)
+	}
🧹 Nitpick comments (4)
vendor/github.com/DeedleFake/p9/addr_unix.go (1)

28-28: Consider username sanitization for edge cases.

The path construction concatenates u.Username without sanitization (line 28). While Unix usernames are typically restricted (alphanumeric + underscore/hyphen), edge cases with special characters (spaces, backslashes) could lead to unexpected paths.

If you want to add extra robustness, consider sanitizing the username:

-	return filepath.Join("/", "tmp", "ns."+u.Username+"."+display)
+	sanitizedUsername := strings.Map(func(r rune) rune {
+		if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '_' || r == '-' {
+			return r
+		}
+		return '_'
+	}, u.Username)
+	return filepath.Join("/", "tmp", "ns."+sanitizedUsername+"."+display)

Note: This is an optional refinement since vendored library code typically assumes standard Unix username constraints.

pkg/fileserver/fs9p/server.go (3)

72-82: Document zombie process risk more clearly.

The comment warns about "BAD THINGS" and zombie processes but doesn't provide actionable guidance for operators. Consider clarifying when it's safe to call Stop() and what the consequences are.

Apply this diff to improve the warning:

 // Stop a running server.
-// Please note that this does *BAD THINGS* to clients if they are still running
-// when the server stops. Processes get stuck in I/O deep sleep and zombify, and
-// nothing I do other than restarting the VM can remove the zombies.
+// WARNING: Stopping the server while clients are actively mounted will cause
+// client processes to hang in uninterruptible I/O sleep, requiring VM restart
+// to recover. Ensure all 9p mounts are unmounted before calling Stop().
 func (s *Server) Stop() error {
 	if err := s.Listener.Close(); err != nil {
-		return err
+		return fmt.Errorf("failed to close 9p server listener: %w", err)
 	}
 	logrus.Infof("Successfully stopped 9p server for directory %s", s.ExposedDir)
 	return nil
 }

84-88: WaitForError blocks indefinitely without timeout.

WaitForError() blocks until an error arrives on ErrChan. If proto.Serve runs without errors (normal operation), this method never returns. Callers cannot implement timeouts or graceful shutdown.

Apply this diff to add context support:

+import "context"
+
 // WaitForError from a running server.
-func (s *Server) WaitForError() error {
-	err := <-s.ErrChan
-	return err
+func (s *Server) WaitForError(ctx context.Context) error {
+	select {
+	case err := <-s.ErrChan:
+		return err
+	case <-ctx.Done():
+		return ctx.Err()
+	}
 }

Update callers (e.g., in daemon.go) to pass a context:

go func() {
	if err := server9p.WaitForError(context.Background()); err != nil {
		errCh <- errors.Wrap(err, "9p server failed")
	}
}()

59-59: Channel closed after first error; subsequent errors lost.

Line 59 closes ErrChan after proto.Serve returns. If Stop() is called and then another error occurs (unlikely but possible in edge cases), the second send panics. More importantly, closing immediately after the first error means WaitForError() will return a zero value on subsequent calls rather than blocking.

Consider removing the close(s.ErrChan) line if you want WaitForError() to block indefinitely when no error has occurred, or document that WaitForError() should only be called once. Alternatively, keep the close but document that the channel is single-use.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f176d25 and 3093c61.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (33)
  • cmd/crc/cmd/daemon.go (5 hunks)
  • go.mod (1 hunks)
  • pkg/crc/constants/constants.go (1 hunks)
  • pkg/crc/machine/libhvee/driver_windows.go (1 hunks)
  • pkg/crc/machine/start.go (2 hunks)
  • pkg/fileserver/fs9p/server.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/LICENSE (1 hunks)
  • vendor/github.com/DeedleFake/p9/README.md (1 hunks)
  • vendor/github.com/DeedleFake/p9/addr.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/addr_other.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/addr_unix.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/client.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_darwin.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_linux.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_other.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_plan9.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_windows.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/doc.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/encoding.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/fs.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/internal/debug/debug.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/internal/debug/nodebug.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/internal/util/util.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/msg.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/p9.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/proto/client.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/proto/encoding.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/proto/proto.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/proto/server.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/remote.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/stat.go (1 hunks)
  • vendor/modules.txt (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (21)
  • vendor/github.com/DeedleFake/p9/internal/debug/debug.go
  • pkg/crc/machine/start.go
  • go.mod
  • vendor/github.com/DeedleFake/p9/addr.go
  • vendor/github.com/DeedleFake/p9/LICENSE
  • vendor/github.com/DeedleFake/p9/internal/util/util.go
  • pkg/crc/constants/constants.go
  • vendor/github.com/DeedleFake/p9/proto/server.go
  • vendor/modules.txt
  • pkg/crc/machine/libhvee/driver_windows.go
  • vendor/github.com/DeedleFake/p9/dir_plan9.go
  • vendor/github.com/DeedleFake/p9/doc.go
  • vendor/github.com/DeedleFake/p9/internal/debug/nodebug.go
  • vendor/github.com/DeedleFake/p9/stat.go
  • vendor/github.com/DeedleFake/p9/dir_windows.go
  • vendor/github.com/DeedleFake/p9/p9.go
  • vendor/github.com/DeedleFake/p9/dir_other.go
  • vendor/github.com/DeedleFake/p9/proto/proto.go
  • vendor/github.com/DeedleFake/p9/dir.go
  • vendor/github.com/DeedleFake/p9/fs.go
  • vendor/github.com/DeedleFake/p9/addr_other.go
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: redbeam
PR: crc-org/crc#4866
File: pkg/fileserver/fs9p/shares.go:0-0
Timestamp: 2025-08-06T09:48:10.441Z
Learning: The vsock implementation in pkg/fileserver/fs9p/shares.go was temporarily removed because 9pfs doesn't fully support it yet. It will be added back when 9pfs provides proper vsock support.
🧬 Code graph analysis (11)
vendor/github.com/DeedleFake/p9/dir_linux.go (3)
vendor/github.com/DeedleFake/p9/stat.go (4)
  • DirEntry (255-267)
  • FileMode (23-23)
  • ModeFromOS (43-78)
  • Stat (156-168)
vendor/github.com/DeedleFake/p9/dir.go (1)
  • Dir (16-16)
vendor/github.com/DeedleFake/p9/p9.go (3)
  • QID (42-46)
  • QIDType (49-49)
  • Version (10-10)
vendor/github.com/DeedleFake/p9/addr_unix.go (1)
vendor/github.com/DeedleFake/p9/addr_other.go (1)
  • NamespaceDir (17-29)
vendor/github.com/DeedleFake/p9/proto/encoding.go (1)
vendor/github.com/DeedleFake/p9/internal/util/util.go (1)
  • Errorf (44-52)
vendor/github.com/DeedleFake/p9/proto/client.go (4)
vendor/github.com/DeedleFake/p9/proto/proto.go (3)
  • Proto (38-41)
  • NoTag (27-27)
  • ErrClientClosed (22-22)
vendor/github.com/DeedleFake/p9/client.go (3)
  • NewClient (30-32)
  • Client (22-26)
  • Dial (36-43)
vendor/github.com/DeedleFake/p9/internal/debug/debug.go (1)
  • Log (10-12)
vendor/github.com/DeedleFake/p9/internal/debug/nodebug.go (1)
  • Log (5-5)
vendor/github.com/DeedleFake/p9/dir_darwin.go (3)
vendor/github.com/DeedleFake/p9/stat.go (4)
  • DirEntry (255-267)
  • FileMode (23-23)
  • ModeFromOS (43-78)
  • Stat (156-168)
vendor/github.com/DeedleFake/p9/dir.go (1)
  • Dir (16-16)
vendor/github.com/DeedleFake/p9/p9.go (3)
  • QID (42-46)
  • QIDType (49-49)
  • Version (10-10)
vendor/github.com/DeedleFake/p9/client.go (5)
vendor/github.com/DeedleFake/p9/proto/client.go (3)
  • Client (18-31)
  • NewClient (35-56)
  • Dial (60-67)
vendor/github.com/DeedleFake/p9/msg.go (8)
  • Proto (75-77)
  • Tversion (79-82)
  • Tversion (84-84)
  • Rversion (86-89)
  • Tauth (95-99)
  • Rauth (101-103)
  • Tattach (105-110)
  • Rattach (112-114)
vendor/github.com/DeedleFake/p9/proto/proto.go (1)
  • Proto (38-41)
vendor/github.com/DeedleFake/p9/p9.go (3)
  • Version (10-10)
  • NoFID (18-18)
  • QID (42-46)
vendor/github.com/DeedleFake/p9/remote.go (1)
  • Remote (19-27)
cmd/crc/cmd/daemon.go (2)
pkg/crc/constants/constants.go (2)
  • Plan9TcpPort (60-60)
  • GetHomeDir (164-170)
pkg/fileserver/fs9p/server.go (1)
  • New9pServer (29-53)
vendor/github.com/DeedleFake/p9/encoding.go (2)
vendor/github.com/DeedleFake/p9/stat.go (2)
  • DirEntry (255-267)
  • Stat (156-168)
vendor/github.com/DeedleFake/p9/proto/encoding.go (2)
  • Read (34-38)
  • Write (25-31)
vendor/github.com/DeedleFake/p9/msg.go (5)
vendor/github.com/DeedleFake/p9/proto/proto.go (2)
  • NewProto (44-60)
  • Proto (38-41)
vendor/github.com/DeedleFake/p9/proto/client.go (1)
  • P9NoTag (221-223)
vendor/github.com/DeedleFake/p9/stat.go (3)
  • FileMode (23-23)
  • Stat (156-168)
  • ErrLargeStat (18-18)
vendor/github.com/DeedleFake/p9/proto/encoding.go (2)
  • Write (25-31)
  • Read (34-38)
vendor/github.com/DeedleFake/p9/internal/util/util.go (1)
  • LimitedReader (13-17)
pkg/fileserver/fs9p/server.go (3)
vendor/github.com/DeedleFake/p9/fs.go (2)
  • FileSystem (21-34)
  • FSConnHandler (162-167)
vendor/github.com/DeedleFake/p9/proto/server.go (1)
  • Serve (17-42)
pkg/crc/constants/constants.go (1)
  • Plan9Msize (59-59)
vendor/github.com/DeedleFake/p9/remote.go (6)
vendor/github.com/DeedleFake/p9/client.go (1)
  • Client (22-26)
vendor/github.com/DeedleFake/p9/p9.go (4)
  • QID (42-46)
  • QIDType (49-49)
  • Version (10-10)
  • IOHeaderSize (70-70)
vendor/github.com/DeedleFake/p9/msg.go (14)
  • Twalk (133-137)
  • Rwalk (139-141)
  • Topen (143-146)
  • Ropen (148-151)
  • Tcreate (153-158)
  • Rcreate (160-163)
  • Tremove (192-194)
  • Tread (165-169)
  • Rread (171-173)
  • Twrite (175-179)
  • Rwrite (181-183)
  • Tclunk (185-187)
  • Tstat (199-201)
  • Rstat (203-205)
vendor/github.com/DeedleFake/p9/stat.go (3)
  • FileMode (23-23)
  • Stat (156-168)
  • DirEntry (255-267)
vendor/github.com/DeedleFake/p9/internal/util/util.go (1)
  • Errorf (44-52)
vendor/github.com/DeedleFake/p9/encoding.go (1)
  • ReadDir (16-30)
🪛 markdownlint-cli2 (0.18.1)
vendor/github.com/DeedleFake/p9/README.md

12-12: Heading style
Expected: setext; Actual: atx

(MD003, heading-style)


17-17: Hard tabs
Column: 1

(MD010, no-hard-tabs)


21-21: Heading style
Expected: setext; Actual: atx

(MD003, heading-style)


26-26: Hard tabs
Column: 1

(MD010, no-hard-tabs)


32-32: Hard tabs
Column: 1

(MD010, no-hard-tabs)


37-37: Hard tabs
Column: 1

(MD010, no-hard-tabs)


43-43: Hard tabs
Column: 1

(MD010, no-hard-tabs)


49-49: Hard tabs
Column: 1

(MD010, no-hard-tabs)

⏰ 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). (4)
  • GitHub Check: verify-devcontainer
  • GitHub Check: build-qe (linux, arm64)
  • GitHub Check: Run OKD bundle with crc (1.24)
  • GitHub Check: Konflux kflux-prd-rh02 / crc-binary-on-pull-request
🔇 Additional comments (15)
vendor/github.com/DeedleFake/p9/msg.go (6)

1-10: LGTM!

Package declaration and imports are appropriate for the 9P protocol implementation.


12-42: LGTM!

Message type identifiers correctly implement the 9P2000 protocol specification, including the appropriate placeholder for the unused Terror type.


44-72: LGTM!

Protocol registry correctly maps all message type identifiers to their corresponding Go types.


74-77: LGTM!

Proto() function provides appropriate access to the protocol implementation.


79-201: LGTM!

Message struct definitions correctly implement the 9P2000 protocol. The special interface methods (P9NoTag, P9Msize, Error) are appropriately implemented.


278-279: LGTM!

Empty Rwstat struct correctly represents the 9P protocol's Rwstat response, which carries no data.

vendor/github.com/DeedleFake/p9/addr_unix.go (5)

1-1: LGTM: Build constraint is appropriate.

The // +build linux darwin constraint correctly limits this Unix-specific implementation to Linux and macOS, complementing the addr_other.go fallback for other platforms.


3-9: LGTM: Imports are appropriate.

The standard library imports (os, os/user, path/filepath) are sufficient for the NamespaceDir() implementation.


18-21: LGTM: Panic behavior is documented and appropriate.

The panic on user.Current() failure (lines 19-20) is acceptable for a vendored library where user lookup is a critical prerequisite. The behavior is clearly documented in the function comments (lines 15-16).


23-26: LGTM: DISPLAY fallback is reasonable.

The fallback to :0 when DISPLAY is not set (lines 24-25) is appropriate for Unix systems where X11 display :0 is the default.


17-29: Remove suggestion to use os.TempDir(): p9’s NamespaceDir deliberately hardcodes “/tmp” to follow Plan 9 conventions; changing it would break compatibility with other 9P tools.

Likely an incorrect or invalid review comment.

cmd/crc/cmd/daemon.go (3)

14-14: LGTM!

The runtime import is appropriately added to support the Windows-specific conditional logic for 9P file sharing.


28-28: LGTM!

The fs9p package import is correctly added to support the new 9P server instantiation.


182-182: LGTM!

Refactoring to use net.JoinHostPort improves clarity and handles IPv6 addresses correctly.

Also applies to: 198-198

pkg/fileserver/fs9p/server.go (1)

29-53: LGTM!

The constructor properly validates the exposed directory (absolute path, exists, is directory) before creating the server. Error messages are clear and the validation logic is sound.

Comment on lines +80 to +97
next, err := file.walk(p)
if err != nil {
return nil, err
}

rsp, err := file.client.Send(&Topen{
FID: next.fid,
Mode: mode,
})
if err != nil {
return nil, err
}
open := rsp.(*Ropen)

next.qid = open.QID

return next, nil
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Release the new fid when Topen fails

If Send(Topen) returns an error, Line [90] bails without clunking the fid we just allocated in walk, leaking server-side resources. Call next.Close() (ignoring its error if necessary) before returning the original error.

	rsp, err := file.client.Send(&Topen{
		FID:  next.fid,
		Mode: mode,
	})
	if err != nil {
-		return nil, err
+		_ = next.Close()
+		return nil, err
	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
next, err := file.walk(p)
if err != nil {
return nil, err
}
rsp, err := file.client.Send(&Topen{
FID: next.fid,
Mode: mode,
})
if err != nil {
return nil, err
}
open := rsp.(*Ropen)
next.qid = open.QID
return next, nil
}
next, err := file.walk(p)
if err != nil {
return nil, err
}
rsp, err := file.client.Send(&Topen{
FID: next.fid,
Mode: mode,
})
if err != nil {
_ = next.Close()
return nil, err
}
open := rsp.(*Ropen)
next.qid = open.QID
return next, nil
}
🤖 Prompt for AI Agents
In vendor/github.com/DeedleFake/p9/remote.go around lines 80 to 97, if
file.client.Send(&Topen{...}) returns an error the function returns without
clunking the fid allocated by file.walk, leaking server resources; call
next.Close() (or next.Close(contextless) as appropriate) and ignore any error
from that Close before returning the original Send error so the newly allocated
fid is released, then return nil, err.

Comment on lines +108 to +122
rsp, err := file.client.Send(&Tcreate{
FID: next.fid,
Name: name,
Perm: perm,
Mode: mode,
})
if err != nil {
return nil, err
}
create := rsp.(*Rcreate)

next.qid = create.QID

return next, nil
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Also clunk the directory fid when Tcreate fails

Same leak in Create: if the server rejects the create, we never close the directory fid we just walked. Clunk it via next.Close() before returning.

	rsp, err := file.client.Send(&Tcreate{
		FID:  next.fid,
		Name: name,
		Perm: perm,
		Mode: mode,
	})
	if err != nil {
-		return nil, err
+		_ = next.Close()
+		return nil, err
	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
rsp, err := file.client.Send(&Tcreate{
FID: next.fid,
Name: name,
Perm: perm,
Mode: mode,
})
if err != nil {
return nil, err
}
create := rsp.(*Rcreate)
next.qid = create.QID
return next, nil
}
rsp, err := file.client.Send(&Tcreate{
FID: next.fid,
Name: name,
Perm: perm,
Mode: mode,
})
if err != nil {
_ = next.Close()
return nil, err
}
create := rsp.(*Rcreate)
next.qid = create.QID
return next, nil
}
🤖 Prompt for AI Agents
In vendor/github.com/DeedleFake/p9/remote.go around lines 108 to 122, when the
Tcreate RPC fails we currently return without closing the directory fid returned
by walk (next), leaking the fid; call next.Close() (and ignore or log its error)
before returning the RPC error so the fid is clunked, i.e. invoke next.Close()
in the err != nil branch right before returning nil, err.

@redbeam redbeam force-pushed the 9p-deedlefake branch 3 times, most recently from 654e75d to 1e6dbd0 Compare October 7, 2025 23:02
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: 2

♻️ Duplicate comments (1)
pkg/crc/machine/start.go (1)

264-264: Fix the chown command syntax.

This issue was previously flagged. The command "chown core:core" is passed as a single argument, but RunPrivileged expects the command name followed by separate arguments.

Apply this diff:

-			if _, _, err := sshRunner.RunPrivileged("Changing owner of mount directory", "chown core:core", mount.Target); err != nil {
+			if _, _, err := sshRunner.RunPrivileged("Changing owner of mount directory", "chown", "core:core", mount.Target); err != nil {
🧹 Nitpick comments (3)
pkg/fileserver/fs9p/server.go (3)

56-70: Consider adding a brief timeout to catch early startup errors.

The non-blocking select at lines 63-69 may miss errors that occur immediately after the goroutine starts but before the select executes. While the buffered channel mitigates this and most errors occur later, adding a small timeout (e.g., 100ms) would provide more reliable startup error detection.

 	// Just before returning, check to see if we got an error off server startup.
 	select {
 	case err := <-s.ErrChan:
 		return fmt.Errorf("starting 9p server: %w", err)
-	default:
+	case <-time.After(100 * time.Millisecond):
 		logrus.Infof("Successfully started 9p server on %s for directory %s", s.Listener.Addr().String(), s.ExposedDir)
 		return nil
 	}

You'll need to import time.


72-82: Improve the zombie process warning documentation.

The comment about "BAD THINGS" is concerning for production use. Consider rephrasing more professionally to help operators understand the implications and recommended precautions.

 // Stop a running server.
-// Please note that this does *BAD THINGS* to clients if they are still running
-// when the server stops. Processes get stuck in I/O deep sleep and zombify, and
-// nothing I do other than restarting the VM can remove the zombies.
+// Stop a running server.
+// WARNING: Stopping the server while clients are actively connected may cause
+// client processes to become unresponsive due to 9P protocol limitations.
+// VM restart may be required to recover. Ensure all clients have disconnected
+// before calling Stop() when possible.
 func (s *Server) Stop() error {

84-88: Consider adding context support to WaitForError.

The method blocks indefinitely. Adding a context parameter would allow callers to set timeouts or cancel the wait, improving robustness.

-// WaitForError from a running server.
-func (s *Server) WaitForError() error {
-	err := <-s.ErrChan
-	return err
-}
+// WaitForError from a running server with context support.
+func (s *Server) WaitForError(ctx context.Context) error {
+	select {
+	case err := <-s.ErrChan:
+		return err
+	case <-ctx.Done():
+		return ctx.Err()
+	}
+}

You'll need to import context and update the caller in cmd/crc/cmd/daemon.go.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 61555b4 and 1e6dbd0.

📒 Files selected for processing (5)
  • cmd/crc/cmd/daemon.go (5 hunks)
  • pkg/crc/constants/constants.go (1 hunks)
  • pkg/crc/machine/libhvee/driver_windows.go (1 hunks)
  • pkg/crc/machine/start.go (2 hunks)
  • pkg/fileserver/fs9p/server.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/crc/constants/constants.go
  • pkg/crc/machine/libhvee/driver_windows.go
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: redbeam
PR: crc-org/crc#4866
File: pkg/fileserver/fs9p/shares.go:0-0
Timestamp: 2025-08-06T09:48:10.441Z
Learning: The vsock implementation in pkg/fileserver/fs9p/shares.go was temporarily removed because 9pfs doesn't fully support it yet. It will be added back when 9pfs provides proper vsock support.
🧬 Code graph analysis (3)
cmd/crc/cmd/daemon.go (3)
pkg/crc/constants/constants.go (2)
  • Plan9TcpPort (60-60)
  • GetHomeDir (164-170)
pkg/fileserver/fs9p/server.go (1)
  • New9pServer (29-53)
pkg/crc/logging/logging.go (1)
  • Warnf (100-102)
pkg/crc/machine/start.go (1)
pkg/os/exec.go (1)
  • RunPrivileged (48-59)
pkg/fileserver/fs9p/server.go (3)
vendor/github.com/DeedleFake/p9/fs.go (2)
  • FileSystem (21-34)
  • FSConnHandler (162-167)
vendor/github.com/DeedleFake/p9/proto/server.go (1)
  • Serve (17-42)
pkg/crc/constants/constants.go (1)
  • Plan9Msize (59-59)
⏰ 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). (2)
  • GitHub Check: build (ubuntu-22.04, 1.24)
  • GitHub Check: Konflux kflux-prd-rh02 / crc-binary-on-pull-request
🔇 Additional comments (2)
cmd/crc/cmd/daemon.go (1)

182-182: LGTM: Improved host:port construction.

Using net.JoinHostPort instead of fmt.Sprintf is more idiomatic and properly handles edge cases like IPv6 addresses.

Also applies to: 198-198

pkg/fileserver/fs9p/server.go (1)

29-53: LGTM: Constructor properly validates and initializes.

The constructor correctly validates the directory path, ensures it exists and is a directory, and initializes the server with a buffered error channel (addressing previous concerns about goroutine blocking).

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: 3

♻️ Duplicate comments (24)
pkg/fileserver/fs9p/server.go (3)

60-75: Startup race: wait briefly for early errors before returning from Start()

The non-blocking select can miss immediate errors from proto.Serve/Accept. Wait a short time window.

 func (s *Server) Start() error {
   go func() {
     s.ErrChan <- proto.Serve(s.Listener, p9.Proto(), p9.FSConnHandler(s.Filesystem, constants.Plan9Msize))
     close(s.ErrChan)
   }()
 
-  // Just before returning, check to see if we got an error off server startup.
-  select {
-  case err := <-s.ErrChan:
-    return fmt.Errorf("starting 9p server: %w", err)
-  default:
-    logrus.Infof("started 9p server on %s for directory %s", s.Listener.Addr().String(), s.ExposedDir)
-    return nil
-  }
+  // Wait briefly for potential startup errors.
+  select {
+  case err := <-s.ErrChan:
+    return fmt.Errorf("starting 9p server: %w", err)
+  case <-time.After(150 * time.Millisecond):
+    logrus.Infof("started 9p server on %s for directory %s", s.Listener.Addr().String(), s.ExposedDir)
+    return nil
+  }
 }

Add: import "time".


77-87: Clarify Stop() behavior and wrap errors for operators

Make the comment actionable and wrap the error.

-// Stop a running server.
-// Please note that this does *BAD THINGS* to clients if they are still running
-// when the server stops. Processes get stuck in I/O deep sleep and zombify, and
-// nothing I do other than restarting the VM can remove the zombies.
+// Stop a running server.
+// WARNING: Stopping while clients are active may leave client processes
+// unresponsive. Ensure clients disconnect before stopping the server.
 func (s *Server) Stop() error {
   if err := s.Listener.Close(); err != nil {
-    return err
+    return fmt.Errorf("failed to close 9p server listener: %w", err)
   }
   logrus.Infof("stopped 9p server for directory %s", s.ExposedDir)
   return nil
 }

89-98: Avoid indefinite blocking: add context to WaitForError

Let callers bound the wait and handle shutdown uniformly.

-// WaitForError from a running server.
-func (s *Server) WaitForError() error {
-  err := <-s.ErrChan
+// WaitForError from a running server with context support.
+func (s *Server) WaitForError(ctx context.Context) error {
+  var err error
+  select {
+  case err = <-s.ErrChan:
+  case <-ctx.Done():
+    return ctx.Err()
+  }
   // captures "accept tcp: endpoint is in invalid state" errors on exit
   var opErr *net.OpError
   if errors.As(err, &opErr) && strings.Contains(opErr.Error(), "endpoint is in invalid state") {
     return nil
   }
   return err
 }

Add: import "context". Update callers accordingly.

vendor/github.com/DeedleFake/p9/proto/encoding.go (3)

65-71: Fix Size() for custom Encoder: count payload bytes, not slice header

 case Encoder:
     buf, err := v.P9Encode()
     e.err = err
-    e.mode(buf)
+    if e.mode == e.size {
+        e.n += uint32(len(buf))
+    } else {
+        e.mode(buf)
+    }
     return

146-153: Remove unsafe pointer cast; read uint16 length safely

   default:
-    d.read((*uint16)(unsafe.Pointer(&length)))
+    var len16 uint16
+    d.read(&len16)
+    length = uint32(len16)

164-172: Avoid unsafe []byte -> string conversion; copy for safety

   buf := make([]byte, int(length))
   d.read(buf)
-
-  v.SetString(*(*string)(unsafe.Pointer(&buf)))
+  v.SetString(string(buf))
vendor/github.com/DeedleFake/p9/dir_darwin.go (1)

35-43: Fix ATime conversion on Darwin: Timespec has Sec/Nsec, no Unix()

 return DirEntry{
   FileMode:  ModeFromOS(fi.Mode()),
-  ATime:     time.Unix(sys.Atimespec.Unix()),
+  ATime:     time.Unix(sys.Atimespec.Sec, sys.Atimespec.Nsec),
   MTime:     fi.ModTime(),
   Length:    uint64(fi.Size()),
   EntryName: fi.Name(),
   UID:       uname,
   GID:       gname,
 }
vendor/github.com/DeedleFake/p9/dir_linux.go (1)

35-43: Fix ATime conversion on Linux: Timespec has Sec/Nsec, no Unix()

 return DirEntry{
   FileMode:  ModeFromOS(fi.Mode()),
-  ATime:     time.Unix(sys.Atim.Unix()),
+  ATime:     time.Unix(sys.Atim.Sec, sys.Atim.Nsec),
   MTime:     fi.ModTime(),
   Length:    uint64(fi.Size()),
   EntryName: fi.Name(),
   UID:       uname,
   GID:       gname,
 }
vendor/github.com/DeedleFake/p9/remote.go (4)

37-43: Normalize path components to avoid empty Wname entries.

Trim leading slash and drop empties; handle "." and "/" as root (nil).

-	w := []string{path.Clean(p)}
-	if w[0] != "/" {
-		w = strings.Split(w[0], "/")
-	}
-	if (len(w) == 1) && (w[0] == ".") {
-		w = nil
-	}
+	cleaned := path.Clean(p)
+	var w []string
+	if cleaned != "." && cleaned != "/" {
+		cleaned = strings.TrimPrefix(cleaned, "/")
+		if cleaned != "" {
+			for _, part := range strings.Split(cleaned, "/") {
+				if part != "" {
+					w = append(w, part)
+				}
+			}
+		}
+	}

58-66: Clunk new fid and error on incomplete walk.

Avoid returning a Remote with bogus QID and leaking fid.

-	if len(walk.WQID) != len(w) {
-		qid = QID{
-			Type:    0xFF,
-			Version: 0xFFFFFFFF,
-			Path:    0xFFFFFFFFFFFFFFFF,
-		}
-	}
+	if len(walk.WQID) != len(w) {
+		_, _ = file.client.Send(&Tclunk{FID: fid})
+		return nil, util.Errorf("walk %q incomplete", p)
+	}

85-92: Release fid if Topen fails.

Prevents server-side fid leak.

 	rsp, err := file.client.Send(&Topen{
 		FID:  next.fid,
 		Mode: mode,
 	})
 	if err != nil {
-		return nil, err
+		_ = next.Close()
+		return nil, err
 	}

108-116: Also clunk directory fid if Tcreate fails.

Prevents fid leak on error.

 	rsp, err := file.client.Send(&Tcreate{
 		FID:  next.fid,
 		Name: name,
 		Perm: perm,
 		Mode: mode,
 	})
 	if err != nil {
-		return nil, err
+		_ = next.Close()
+		return nil, err
 	}
vendor/github.com/DeedleFake/p9/proto/client.go (2)

90-97: Avoid tight/infinite read-error loop. Return or back off.

Continuing on non-EOF read errors can spin forever if the conn stays bad.

-		if err != nil {
-			if (ctx.Err() != nil) || (err == io.EOF) {
-				return
-			}
-			continue
-		}
+		if err != nil {
+			if (ctx.Err() != nil) || (err == io.EOF) {
+				return
+			}
+			log.Printf("Read error: %v", err)
+			return
+		}

138-145: Never allocate reserved NoTag from tag allocator.

Skip NoTag and any in-use tag, including on wrap-around.

-		case c.nextTag <- nextTag:
-			for {
-				nextTag++
-				if _, ok := tags[nextTag]; !ok {
-					break
-				}
-			}
+		case c.nextTag <- func() uint16 {
+			if nextTag == NoTag {
+				nextTag++
+			}
+			t := nextTag
+			for {
+				nextTag++
+				if nextTag == NoTag {
+					nextTag++
+				}
+				if _, ok := tags[nextTag]; !ok {
+					break
+				}
+			}
+			return t
+		}():
vendor/github.com/DeedleFake/p9/proto/proto.go (2)

85-94: Bound reader to payload (size-4) and guard short frames.

Current N=size risks over-read into the next frame and lacks short-size guard.

-	if (msize > 0) && (size > msize) {
+	if (msize > 0) && (size > msize) {
 		return nil, NoTag, util.Errorf("receive: %w", ErrLargeMessage)
 	}
-
-	lr := &util.LimitedReader{
+	if size < 4 {
+		return nil, NoTag, util.Errorf("receive: short message size: %d", size)
+	}
+	lr := &util.LimitedReader{
 		R: r,
-		N: size,
+		N: size - 4,
 		E: ErrLargeMessage,
 	}

101-108: Remove unreachable error check inside t==nil.

err isn’t set by TypeFromID; this block is dead code.

 	t := p.TypeFromID(msgType)
 	if t == nil {
-		if err != nil {
-			return nil, NoTag, err
-		}
-
 		return nil, NoTag, util.Errorf("receive: invalid message type: %v", msgType)
 	}
vendor/github.com/DeedleFake/p9/proto/server.go (2)

70-78: Add continue after logging read error.

Prevents processing invalid tmsg/tag.

 		if err != nil {
 			if err == io.EOF {
 				return
 			}
 
 			log.Printf("Error reading message: %v", err)
+			continue
 		}

79-99: Avoid closure over loop vars tag/tmsg when switching to concurrent mode.

Copy to locals before invoking mode() to prevent data races.

-		mode(func() {
-			rmsg := handler.HandleMessage(tmsg)
+		ctag := tag
+		ctmsg := tmsg
+		mode(func() {
+			rmsg := handler.HandleMessage(ctmsg)
 			if rmsg, ok := rmsg.(Msizer); ok {
@@
-			err := p.Send(c, tag, rmsg)
+			err := p.Send(c, ctag, rmsg)
vendor/github.com/DeedleFake/p9/stat.go (1)

152-153: Avoid unsafe conversion; use string(buf).

Safer and idiomatic; negligible overhead here.

-	return *(*string)(unsafe.Pointer(&buf))
+	return string(buf)
vendor/github.com/DeedleFake/p9/msg.go (2)

207-217: Return immediately on write error to prevent partial/corrupt buffers.

Stop after each failed proto.Write.

 func (stat *Rstat) P9Encode() ([]byte, error) {
 	var buf bytes.Buffer
 
 	err := proto.Write(&buf, stat.Stat.size()+2)
 	if err != nil {
 		return nil, err
 	}
 
 	err = proto.Write(&buf, stat.Stat)
-	return buf.Bytes(), err
+	if err != nil {
+		return nil, err
+	}
+	return buf.Bytes(), nil
 }

240-255: Same early-return fix for Twstat.P9Encode.

 	err := proto.Write(&buf, stat.FID)
 	if err != nil {
 		return nil, err
 	}
 
 	err = proto.Write(&buf, stat.Stat.size()+2)
 	if err != nil {
 		return nil, err
 	}
 
 	err = proto.Write(&buf, stat.Stat)
-	return buf.Bytes(), err
+	if err != nil {
+		return nil, err
+	}
+	return buf.Bytes(), nil
vendor/github.com/DeedleFake/p9/fs.go (3)

179-181: Remove unsafe string-to-bytes conversion.

Unsafe violates Go’s guarantees; use safe conversion.

-	sum := sha256.Sum256(*(*[]byte)(unsafe.Pointer(&p)))
+	sum := sha256.Sum256([]byte(p))

536-537: Writes must take exclusive lock (RLock is unsafe here).

Concurrent writes under RLock can corrupt state.

-	file.RLock()         // Somewhat ironic that this doesn't require a
-	defer file.RUnlock() // full lock like read() does.
+	file.Lock()
+	defer file.Unlock()

719-724: Close(): lock per file before closing to avoid races.

Ensure no concurrent ops touch file during Close().

 func (h *fsHandler) Close() error {
   h.fids.Range(func(k, v interface{}) bool {
     file := v.(*fsFile)
-    if file.file != nil {
-      file.file.Close()
-    }
+    file.Lock()
+    if file.file != nil {
+      _ = file.file.Close()
+      file.file = nil
+    }
+    file.Unlock()
     return true
   })
 
   return nil
 }
🧹 Nitpick comments (14)
vendor/github.com/DeedleFake/p9/README.md (2)

12-12: Fix markdown heading style inconsistency.

Lines 12 and 21 use ATX-style headings (###) but should use setext-style headings (underlined with === or ---) to match the title format on lines 1-2.

Apply this diff:

-### Server
+Server
+------
-### Client
+Client
+------

Also applies to: 21-21


17-17: Replace hard tabs with spaces in code examples.

The code examples contain hard tabs (lines 17, 26, 32, 37, 43, 49) which should be replaced with spaces for consistent indentation across different editors and tools.

Apply this diff to the code blocks:

 err := p9.ListenAndServe("tcp", "localhost:5640", p9.FSConnHandler(fsImplementation))
 if err != nil {
-	log.Fatalf("Failed to start server: %v", err)
+    log.Fatalf("Failed to start server: %v", err)
 }
 _, err := c.Handshake(2048)
 if err != nil {
-	log.Fatalf("Failed to perform handshake: %v", err)
+    log.Fatalf("Failed to perform handshake: %v", err)
 }

(Apply the same fix to all other hard-tabbed lines in both code examples.)

Also applies to: 26-26, 32-32, 37-37, 43-43, 49-49

vendor/github.com/DeedleFake/p9/internal/debug/nodebug.go (1)

1-1: Informational: Old-style build constraint in vendored code.

The build constraint uses the deprecated // +build syntax. Modern Go prefers //go:build !p9debug. However, since this is vendored code from an external dependency, it should not be modified here. If needed, the change should be submitted upstream to github.com/DeedleFake/p9.

vendor/github.com/DeedleFake/p9/dir_windows.go (1)

9-27: Windows ATime: optional fallback when access time is disabled

On some setups LastAccessTime can be zero. Fallback to ModTime to avoid zero epoch timestamps in listings.

 func infoToEntry(fi os.FileInfo) DirEntry {
   sys, _ := fi.Sys().(*syscall.Win32FileAttributeData)
   if sys == nil {
     return DirEntry{
       FileMode:  ModeFromOS(fi.Mode()),
       MTime:     fi.ModTime(),
       Length:    uint64(fi.Size()),
       EntryName: fi.Name(),
     }
   }
-
-  return DirEntry{
-    FileMode:  ModeFromOS(fi.Mode()),
-    ATime:     time.Unix(0, sys.LastAccessTime.Nanoseconds()),
-    MTime:     fi.ModTime(),
-    Length:    uint64(fi.Size()),
-    EntryName: fi.Name(),
-  }
+  at := fi.ModTime()
+  if ns := sys.LastAccessTime.Nanoseconds(); ns > 0 {
+    at = time.Unix(0, ns)
+  }
+  return DirEntry{
+    FileMode:  ModeFromOS(fi.Mode()),
+    ATime:     at,
+    MTime:     fi.ModTime(),
+    Length:    uint64(fi.Size()),
+    EntryName: fi.Name(),
+  }
 }
pkg/fileserver/fs9p/server.go (1)

31-58: Normalize and store a cleaned path

Minor: clean the exposed path to avoid surprises with trailing separators/.. segments.

 func New9pServer(listener net.Listener, exposeDir string) (*Server, error) {
   // verify that exposeDir makes sense
   if !filepath.IsAbs(exposeDir) {
     return nil, fmt.Errorf("path to expose to machine must be absolute: %s", exposeDir)
   }
-  stat, err := os.Stat(exposeDir)
+  exposeDir = filepath.Clean(exposeDir)
+  stat, err := os.Stat(exposeDir)
   if err != nil {
     return nil, fmt.Errorf("cannot stat path to expose to machine: %w", err)
   }
vendor/github.com/DeedleFake/p9/dir_darwin.go (1)

23-33: Optional: cache UID/GID lookups to avoid per-entry NSS calls

User/group lookups per file can be expensive. Consider a small LRU or map cache keyed by uid/gid strings during a directory walk.

vendor/github.com/DeedleFake/p9/dir_linux.go (1)

23-33: Optional: cache UID/GID lookups during directory scans

Same as Darwin: memoize user.LookupId/group lookups to reduce NSS overhead.

vendor/github.com/DeedleFake/p9/dir_plan9.go (1)

39-42: Tighten error message for diagnostics.

Clarify actual type received to ease debugging.

-		return QID{}, errors.New("failed to get QID: FileInfo was not Dir")
+		return QID{}, errors.New("failed to get QID: FileInfo.Sys() was not *syscall.Dir")
vendor/github.com/DeedleFake/p9/fs.go (2)

244-249: Handle Tflush gracefully (return Rflush or implement cancellation).

Returning Rerror breaks clients relying on flush semantics.

 func (h *fsHandler) flush(msg *Tflush) interface{} {
-	// TODO: Implement this.
-	return &Rerror{
-		Ename: "flush is not supported",
-	}
+	// Best-effort acknowledge; actual cancellation not guaranteed.
+	return &Rflush{}
 }

499-512: Directory read offset handling deviates from spec — confirm or align.

Spec requires dir reads at 0 or previous offset+len. Current code ignores non‑zero offsets.

Would you like a patch to track and validate offsets per FID?

vendor/github.com/DeedleFake/p9/remote.go (4)

198-200: Guard against pathological msize values.

Ensure non-negative buffer size.

-func (file *Remote) maxBufSize() int {
-	return int(file.client.Msize() - IOHeaderSize)
-}
+func (file *Remote) maxBufSize() int {
+	ms := int(file.client.Msize())
+	if ms <= IOHeaderSize {
+		return 0
+	}
+	return ms - IOHeaderSize
+}

273-275: Use io.ErrShortWrite for partial write, not EOF.

EOF is for reads; short writes should return ErrShortWrite.

-	if write.Count < uint32(len(data)) {
-		return int(write.Count), io.EOF
-	}
+	if write.Count < uint32(len(data)) {
+		return int(write.Count), io.ErrShortWrite
+	}

184-185: Return an error instead of panic on invalid whence.

Avoid crashing caller; align with io.Seeker semantics.

-	panic(util.Errorf("Invalid whence: %v", whence))
+	return int64(file.pos), util.Errorf("invalid whence: %v", whence)

127-135: Minor: avoid shadowing receiver in Remove().

Improves readability.

-		file, err := file.walk(p)
+		next, err := file.walk(p)
 		if err != nil {
 			return err
 		}
 		// Close is not necessary. Remove is also a clunk.
 
-		return file.Remove("")
+		return next.Remove("")
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1e6dbd0 and dd593f8.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (33)
  • cmd/crc/cmd/daemon.go (5 hunks)
  • go.mod (1 hunks)
  • pkg/crc/constants/constants.go (1 hunks)
  • pkg/crc/machine/libhvee/driver_windows.go (1 hunks)
  • pkg/crc/machine/start.go (2 hunks)
  • pkg/fileserver/fs9p/server.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/LICENSE (1 hunks)
  • vendor/github.com/DeedleFake/p9/README.md (1 hunks)
  • vendor/github.com/DeedleFake/p9/addr.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/addr_other.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/addr_unix.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/client.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_darwin.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_linux.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_other.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_plan9.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_windows.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/doc.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/encoding.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/fs.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/internal/debug/debug.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/internal/debug/nodebug.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/internal/util/util.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/msg.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/p9.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/proto/client.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/proto/encoding.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/proto/proto.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/proto/server.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/remote.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/stat.go (1 hunks)
  • vendor/modules.txt (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (16)
  • vendor/github.com/DeedleFake/p9/internal/debug/debug.go
  • vendor/github.com/DeedleFake/p9/addr_other.go
  • pkg/crc/machine/libhvee/driver_windows.go
  • vendor/modules.txt
  • pkg/crc/machine/start.go
  • vendor/github.com/DeedleFake/p9/p9.go
  • vendor/github.com/DeedleFake/p9/dir_other.go
  • vendor/github.com/DeedleFake/p9/doc.go
  • go.mod
  • vendor/github.com/DeedleFake/p9/client.go
  • pkg/crc/constants/constants.go
  • cmd/crc/cmd/daemon.go
  • vendor/github.com/DeedleFake/p9/dir.go
  • vendor/github.com/DeedleFake/p9/LICENSE
  • vendor/github.com/DeedleFake/p9/encoding.go
  • vendor/github.com/DeedleFake/p9/internal/util/util.go
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: redbeam
PR: crc-org/crc#4866
File: pkg/fileserver/fs9p/shares.go:0-0
Timestamp: 2025-08-06T09:48:10.441Z
Learning: The vsock implementation in pkg/fileserver/fs9p/shares.go was temporarily removed because 9pfs doesn't fully support it yet. It will be added back when 9pfs provides proper vsock support.
🧬 Code graph analysis (16)
vendor/github.com/DeedleFake/p9/addr.go (2)
vendor/github.com/DeedleFake/p9/addr_other.go (1)
  • NamespaceDir (17-29)
vendor/github.com/DeedleFake/p9/addr_unix.go (1)
  • NamespaceDir (17-29)
vendor/github.com/DeedleFake/p9/proto/client.go (6)
vendor/github.com/DeedleFake/p9/msg.go (1)
  • Proto (75-77)
vendor/github.com/DeedleFake/p9/proto/proto.go (3)
  • Proto (38-41)
  • NoTag (27-27)
  • ErrClientClosed (22-22)
vendor/github.com/DeedleFake/p9/client.go (3)
  • NewClient (30-32)
  • Client (22-26)
  • Dial (36-43)
vendor/github.com/DeedleFake/p9/internal/debug/debug.go (1)
  • Log (10-12)
vendor/github.com/DeedleFake/p9/internal/debug/nodebug.go (1)
  • Log (5-5)
vendor/github.com/DeedleFake/p9/p9.go (1)
  • NoTag (15-15)
vendor/github.com/DeedleFake/p9/addr_unix.go (1)
vendor/github.com/DeedleFake/p9/addr_other.go (1)
  • NamespaceDir (17-29)
vendor/github.com/DeedleFake/p9/dir_darwin.go (3)
vendor/github.com/DeedleFake/p9/stat.go (3)
  • DirEntry (255-267)
  • FileMode (23-23)
  • ModeFromOS (43-78)
vendor/github.com/DeedleFake/p9/dir.go (1)
  • Dir (16-16)
vendor/github.com/DeedleFake/p9/p9.go (3)
  • QID (42-46)
  • QIDType (49-49)
  • Version (10-10)
vendor/github.com/DeedleFake/p9/proto/proto.go (2)
vendor/github.com/DeedleFake/p9/proto/encoding.go (3)
  • Read (34-38)
  • Write (25-31)
  • Size (14-20)
vendor/github.com/DeedleFake/p9/internal/util/util.go (2)
  • Errorf (44-52)
  • LimitedReader (13-17)
vendor/github.com/DeedleFake/p9/dir_plan9.go (3)
vendor/github.com/DeedleFake/p9/stat.go (4)
  • DirEntry (255-267)
  • FileMode (23-23)
  • ModeFromOS (43-78)
  • Stat (156-168)
vendor/github.com/DeedleFake/p9/dir.go (1)
  • Dir (16-16)
vendor/github.com/DeedleFake/p9/p9.go (2)
  • QID (42-46)
  • Version (10-10)
vendor/github.com/DeedleFake/p9/fs.go (8)
vendor/github.com/DeedleFake/p9/stat.go (4)
  • Stat (156-168)
  • DirEntry (255-267)
  • StatChanges (316-318)
  • FileMode (23-23)
vendor/github.com/DeedleFake/p9/p9.go (7)
  • QID (42-46)
  • QIDType (49-49)
  • IOHeaderSize (70-70)
  • Version (10-10)
  • QTAuth (58-58)
  • NoFID (18-18)
  • QTDir (54-54)
vendor/github.com/DeedleFake/p9/proto/server.go (3)
  • MessageHandler (135-139)
  • ConnHandler (112-114)
  • ConnHandlerFunc (125-125)
vendor/github.com/DeedleFake/p9/internal/debug/debug.go (1)
  • Log (10-12)
vendor/github.com/DeedleFake/p9/internal/debug/nodebug.go (1)
  • Log (5-5)
vendor/github.com/DeedleFake/p9/msg.go (26)
  • Tversion (79-82)
  • Tversion (84-84)
  • Rversion (86-89)
  • Tauth (95-99)
  • Rauth (101-103)
  • Tflush (126-128)
  • Tattach (105-110)
  • Rattach (112-114)
  • Twalk (133-137)
  • Rwalk (139-141)
  • Topen (143-146)
  • Ropen (148-151)
  • Tcreate (153-158)
  • Rcreate (160-163)
  • Tread (165-169)
  • Rread (171-173)
  • Twrite (175-179)
  • Rwrite (181-183)
  • Tclunk (185-187)
  • Rclunk (189-190)
  • Tremove (192-194)
  • Rremove (196-197)
  • Tstat (199-201)
  • Rstat (203-205)
  • Twstat (235-238)
  • Rwstat (278-279)
vendor/github.com/DeedleFake/p9/client.go (1)
  • ErrUnsupportedVersion (14-14)
vendor/github.com/DeedleFake/p9/encoding.go (1)
  • WriteDir (33-42)
vendor/github.com/DeedleFake/p9/dir_windows.go (1)
vendor/github.com/DeedleFake/p9/stat.go (3)
  • DirEntry (255-267)
  • FileMode (23-23)
  • ModeFromOS (43-78)
vendor/github.com/DeedleFake/p9/stat.go (3)
vendor/github.com/DeedleFake/p9/p9.go (3)
  • QIDType (49-49)
  • QID (42-46)
  • Version (10-10)
vendor/github.com/DeedleFake/p9/proto/encoding.go (3)
  • Write (25-31)
  • Read (34-38)
  • Size (14-20)
vendor/github.com/DeedleFake/p9/internal/util/util.go (1)
  • LimitedReader (13-17)
vendor/github.com/DeedleFake/p9/internal/debug/nodebug.go (1)
vendor/github.com/DeedleFake/p9/internal/debug/debug.go (1)
  • Log (10-12)
vendor/github.com/DeedleFake/p9/proto/encoding.go (1)
vendor/github.com/DeedleFake/p9/internal/util/util.go (1)
  • Errorf (44-52)
vendor/github.com/DeedleFake/p9/msg.go (6)
vendor/github.com/DeedleFake/p9/proto/proto.go (2)
  • NewProto (44-60)
  • Proto (38-41)
vendor/github.com/DeedleFake/p9/p9.go (2)
  • Version (10-10)
  • QID (42-46)
vendor/github.com/DeedleFake/p9/proto/client.go (1)
  • P9NoTag (221-223)
vendor/github.com/DeedleFake/p9/stat.go (3)
  • FileMode (23-23)
  • Stat (156-168)
  • ErrLargeStat (18-18)
vendor/github.com/DeedleFake/p9/proto/encoding.go (2)
  • Write (25-31)
  • Read (34-38)
vendor/github.com/DeedleFake/p9/internal/util/util.go (1)
  • LimitedReader (13-17)
vendor/github.com/DeedleFake/p9/proto/server.go (1)
vendor/github.com/DeedleFake/p9/proto/proto.go (1)
  • Proto (38-41)
pkg/fileserver/fs9p/server.go (3)
vendor/github.com/DeedleFake/p9/fs.go (2)
  • FileSystem (21-34)
  • FSConnHandler (162-167)
vendor/github.com/DeedleFake/p9/proto/server.go (1)
  • Serve (17-42)
pkg/crc/constants/constants.go (1)
  • Plan9Msize (59-59)
vendor/github.com/DeedleFake/p9/remote.go (7)
vendor/github.com/DeedleFake/p9/client.go (1)
  • Client (22-26)
vendor/github.com/DeedleFake/p9/proto/client.go (1)
  • Client (18-31)
vendor/github.com/DeedleFake/p9/p9.go (4)
  • QID (42-46)
  • QIDType (49-49)
  • Version (10-10)
  • IOHeaderSize (70-70)
vendor/github.com/DeedleFake/p9/msg.go (14)
  • Twalk (133-137)
  • Rwalk (139-141)
  • Topen (143-146)
  • Ropen (148-151)
  • Tcreate (153-158)
  • Rcreate (160-163)
  • Tremove (192-194)
  • Tread (165-169)
  • Rread (171-173)
  • Twrite (175-179)
  • Rwrite (181-183)
  • Tclunk (185-187)
  • Tstat (199-201)
  • Rstat (203-205)
vendor/github.com/DeedleFake/p9/stat.go (3)
  • FileMode (23-23)
  • Stat (156-168)
  • DirEntry (255-267)
vendor/github.com/DeedleFake/p9/internal/util/util.go (1)
  • Errorf (44-52)
vendor/github.com/DeedleFake/p9/encoding.go (1)
  • ReadDir (16-30)
vendor/github.com/DeedleFake/p9/dir_linux.go (3)
vendor/github.com/DeedleFake/p9/stat.go (3)
  • DirEntry (255-267)
  • FileMode (23-23)
  • ModeFromOS (43-78)
vendor/github.com/DeedleFake/p9/dir.go (1)
  • Dir (16-16)
vendor/github.com/DeedleFake/p9/p9.go (3)
  • QID (42-46)
  • QIDType (49-49)
  • Version (10-10)
🪛 markdownlint-cli2 (0.18.1)
vendor/github.com/DeedleFake/p9/README.md

12-12: Heading style
Expected: setext; Actual: atx

(MD003, heading-style)


17-17: Hard tabs
Column: 1

(MD010, no-hard-tabs)


21-21: Heading style
Expected: setext; Actual: atx

(MD003, heading-style)


26-26: Hard tabs
Column: 1

(MD010, no-hard-tabs)


32-32: Hard tabs
Column: 1

(MD010, no-hard-tabs)


37-37: Hard tabs
Column: 1

(MD010, no-hard-tabs)


43-43: Hard tabs
Column: 1

(MD010, no-hard-tabs)


49-49: Hard tabs
Column: 1

(MD010, no-hard-tabs)

⏰ 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). (4)
  • GitHub Check: build (macOS-13, 1.24)
  • GitHub Check: build-qe (darwin, amd64)
  • GitHub Check: build (ubuntu-latest, 1.24)
  • GitHub Check: Konflux kflux-prd-rh02 / crc-binary-on-pull-request
🔇 Additional comments (8)
vendor/github.com/DeedleFake/p9/README.md (1)

1-51: Good documentation structure for the 9P library.

The README provides clear examples and explains the package's purpose well. The content is well-organized with practical server and client usage examples.

vendor/github.com/DeedleFake/p9/internal/debug/nodebug.go (1)

3-5: LGTM! Correct no-op debug implementation.

The no-op Log function correctly implements the conditional compilation pattern. When the p9debug build tag is not set, this version compiles instead of the actual logging implementation in debug.go, eliminating debug overhead at compile time. The function signature matches the active implementation, ensuring API consistency.

vendor/github.com/DeedleFake/p9/addr_unix.go (3)

28-28: Path construction differs between Unix and non-Unix platforms.

The Unix version hardcodes /tmp using filepath.Join("/", "tmp", ...), while the non-Unix version (addr_other.go) uses os.TempDir(). This creates a platform-specific discrepancy:

  • Unix/Darwin: /tmp/ns.$USER.$DISPLAY
  • Windows/Other: %TEMP%\ns.$USER.$DISPLAY

This is likely intentional (following Plan 9/Unix conventions), but verify that:

  1. Windows 9P clients/servers expect a different namespace directory path
  2. The hardcoded /tmp path is appropriate for all Unix-like CRC environments (containers, custom setups)
  3. The namespace directory is created if it doesn't exist (check calling code)

1-29: Vendored p9 library is current but upstream project is unmaintained.

The vendored version v0.6.12 is documented in go.mod and is the latest available release. However, the upstream DeedleFake/p9 project has not been actively maintained since March 2021 (3.5+ years with no releases). No security advisories were detected for this library. Verify that using an unmaintained dependency is acceptable for your use case.


18-20: This review comment is incorrect and should be dismissed.

The code under review is in the vendored github.com/DeedleFake/p9 library, not part of CRC's codebase. The panic in NamespaceDir() is only triggered if the code calls ParseAddr() with a namespace-formatted address (like /program_name), which then calls GetNamespace(), which calls NamespaceDir().

However, the CRC 9P server implementation in pkg/fileserver/fs9p/server.go does not use ParseAddr(), GetNamespace(), or NamespaceDir() at all. It creates a filesystem directly with p9.Dir() and serves it via proto.Serve() with p9.FSConnHandler(). This panic code path is unreachable in the CRC application. Additionally, the panic behavior is already documented in the upstream library's function comment (lines 15-16 of addr_unix.go), so it's an intentional design choice by the library maintainers.

Likely an incorrect or invalid review comment.

vendor/github.com/DeedleFake/p9/addr.go (3)

8-8: LGTM: Standard 9P port is correctly defined.

Port 564 is the IANA-assigned standard port for 9P2000, correctly used here for address parsing fallbacks and pseudo-port mapping.


70-72: GetNamespace inherits panic behavior from NamespaceDir().

This function calls NamespaceDir(), which panics if user.Current() fails (see addr_unix.go review). Any code calling GetNamespace() (or ParseAddr() with namespace addresses) must be prepared for potential panics.

Ensure that 9P server initialization code accounts for this behavior, especially in containerized or non-standard user environments.


32-66: I need to verify if the p9 library (and specifically ParseAddr) is actually used in the codebase. The initial search didn't find direct calls to ParseAddr from the DeedleFake/p9 package.<!-- [scratchpad_update]
[observations]

  • Web search confirms ParseAddr is a utility function in the p9 library that parses address strings
  • The p9 library appears to be used in the codebase (server.go has p9.FSConnHandler, proto.Serve references)
  • ParseAddr converts a string into network and address pair for establishing connections
  • The review comment concerns input validation for the function

[pending]

  • Still need to confirm if ParseAddr is specifically called in the codebase
  • Search didn't explicitly show if ParseAddr is invoked by the application code
  • Need to refocus: Is this a vendored file that shouldn't be modified?

[actions]

  • Run focused search on the p9 library usage in codebase
  • Check if vendored code is expected to be modified or just a dependency
    [scratchpad_update] -->

Let me search more specifically for how the p9 library is used in the codebase:<!-- [scratchpad_update]
[task verify_review_comment_1/1]

[observations]

  • Web search results clarify vendoring practices in Go
  • Vendored code is a "copy" of dependencies in the project directory
  • Convention: vendored code should NOT be modified because changes get wiped out on updates
  • Modifying vendored dependencies is generally discouraged; better to fork and use replace directive
  • The review comment is about vendored code: vendor/github.com/DeedleFake/p9/addr.go

[key_insight]

  • Reviewing and requesting improvements to vendored code is fundamentally problematic
  • Vendored files are not part of the project's codebase to be modified
  • The review comment's requests cannot/should not be acted upon since it's vendored

[conclusion]

  • The original review comment is attempting to request changes to vendored code
  • This is outside the scope of code review for a repository
  • The file is from an external library (DeedleFake/p9) and shouldn't be modified
  • The review comment itself is incorrect in context

[done]
The review comment is flagged as because it attempts to suggest improvements to vendored third-party code, which is not part of the project's modifiable codebase.
[scratchpad_update] -->

Based on my verification, I can now provide the final assessment:


The file vendor/github.com/DeedleFake/p9/addr.go is vendored third-party code from the DeedleFake/p9 library. Vendored dependencies should not be modified in a repository—they represent external libraries with their own maintenance and versioning. If improvements are needed in the upstream library, the proper approach is to contribute directly to the DeedleFake/p9 project or use a fork with a go.mod replace directive, not to modify the vendored copy (which would be overwritten on dependency updates). The review comment's request for verification and changes does not apply to vendored code in this context.

Likely an incorrect or invalid review comment.

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: 1

♻️ Duplicate comments (1)
pkg/crc/machine/start.go (1)

264-264: Fix the chown command argument passing.

The command arguments are not properly split. RunPrivileged expects the command name followed by individual arguments, but "chown core:core" is passed as a single string.

Apply this diff:

-			if _, _, err := sshRunner.RunPrivileged("Changing owner of mount directory", "chown core:core", mount.Target); err != nil {
+			if _, _, err := sshRunner.RunPrivileged("Changing owner of mount directory", "chown", "core:core", mount.Target); err != nil {
🧹 Nitpick comments (2)
pkg/fileserver/fs9p/server.go (2)

61-74: Start method correctly prevents early error detection race.

The non-blocking select allows the server to start asynchronously while catching immediate startup failures. For most use cases, this is sufficient. If more robust startup synchronization is needed in the future, consider adding a small timeout (e.g., 100ms) to the select statement.


89-98: WaitForError handles shutdown gracefully.

The special handling for "endpoint is in invalid state" errors during normal shutdown is appropriate. The method correctly blocks until an error occurs or the channel is closed.

For enhanced robustness, consider adding context support to prevent indefinite blocking, but this is not critical for the current use case.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dd593f8 and d56fb1a.

📒 Files selected for processing (5)
  • cmd/crc/cmd/daemon.go (5 hunks)
  • pkg/crc/constants/constants.go (1 hunks)
  • pkg/crc/machine/libhvee/driver_windows.go (1 hunks)
  • pkg/crc/machine/start.go (2 hunks)
  • pkg/fileserver/fs9p/server.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/crc/machine/libhvee/driver_windows.go
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: redbeam
PR: crc-org/crc#4866
File: pkg/fileserver/fs9p/shares.go:0-0
Timestamp: 2025-08-06T09:48:10.441Z
Learning: The vsock implementation in pkg/fileserver/fs9p/shares.go was temporarily removed because 9pfs doesn't fully support it yet. It will be added back when 9pfs provides proper vsock support.
🧬 Code graph analysis (3)
cmd/crc/cmd/daemon.go (4)
pkg/crc/config/settings.go (1)
  • EnableSharedDirs (34-34)
pkg/crc/constants/constants.go (2)
  • Plan9TcpPort (60-60)
  • GetHomeDir (164-170)
pkg/fileserver/fs9p/server.go (1)
  • New9pServer (34-58)
pkg/crc/logging/logging.go (1)
  • Warnf (100-102)
pkg/crc/machine/start.go (2)
pkg/os/exec.go (1)
  • RunPrivileged (48-59)
pkg/crc/constants/constants.go (1)
  • VSockGateway (42-42)
pkg/fileserver/fs9p/server.go (3)
vendor/github.com/DeedleFake/p9/fs.go (2)
  • FileSystem (21-34)
  • FSConnHandler (162-167)
vendor/github.com/DeedleFake/p9/proto/server.go (1)
  • Serve (17-42)
pkg/crc/constants/constants.go (1)
  • Plan9Msize (59-59)
⏰ 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). (18)
  • GitHub Check: build (ubuntu-latest, 1.24)
  • GitHub Check: build (windows-2022, 1.24)
  • GitHub Check: build (ubuntu-22.04, 1.24)
  • GitHub Check: build (macOS-13, 1.24)
  • GitHub Check: build (ubuntu-latest, 1.24)
  • GitHub Check: build (macOS-14, 1.24)
  • GitHub Check: build-qe (darwin, amd64)
  • GitHub Check: Run OKD bundle with crc (1.24)
  • GitHub Check: build-qe (linux, arm64)
  • GitHub Check: build-qe (windows, amd64)
  • GitHub Check: build-qe (darwin, arm64)
  • GitHub Check: build-qe (linux, amd64)
  • GitHub Check: verify-devcontainer
  • GitHub Check: build-installer (windows-2022, 1.24)
  • GitHub Check: build (macOS-13, 1.24)
  • GitHub Check: build (windows-2022, 1.24)
  • GitHub Check: build (macOS-14, 1.24)
  • GitHub Check: Konflux kflux-prd-rh02 / crc-binary-on-pull-request
🔇 Additional comments (9)
pkg/crc/constants/constants.go (1)

59-60: LGTM!

The constants are well-defined and appropriate for 9P protocol support. Port 564 is the standard IANA-assigned port for Plan 9, and the 1 MB message size is a reasonable choice for 9P operations.

pkg/crc/machine/start.go (2)

255-259: Good security practice for masking credentials.

The error handling properly masks the password in error messages using MaskedSecretError, preventing credential leakage in logs or error output.


267-267: Use VSockGateway constant appropriately.

Good use of constants.VSockGateway instead of a hardcoded IP. However, verify that the Run method correctly handles these as separate arguments (command, IP, target).

Based on the pattern at lines 248 and 254, verify the arguments are passed correctly to the Run method.

cmd/crc/cmd/daemon.go (5)

14-14: LGTM!

Appropriate import of runtime package for OS detection in the Windows-specific 9P sharing logic.


28-28: LGTM!

Appropriate import of the new fs9p package for 9P server functionality.


182-182: Good use of net.JoinHostPort for proper address formatting.

Using net.JoinHostPort ensures correct IPv6 and IPv4 address handling when constructing the listener address.


198-198: Consistent use of net.JoinHostPort.

Good consistency with the gateway listener setup.


253-276: Well-structured 9P server lifecycle management.

The Windows-specific 9P sharing implementation correctly:

  • Checks both OS and configuration settings
  • Uses appropriate constants for port configuration
  • Handles errors at each initialization step
  • Manages server lifecycle with proper cleanup via defer
  • Monitors server errors in a background goroutine without busy-waiting
pkg/fileserver/fs9p/server.go (1)

34-58: Solid validation and initialization logic.

The constructor properly validates:

  • Path is absolute
  • Directory exists and is accessible
  • All required fields are initialized

The buffered channel prevents goroutine leaks on shutdown.

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: 0

🧹 Nitpick comments (1)
pkg/fileserver/fs9p/server.go (1)

77-87: Consider runtime warning for shutdown risks.

The comment correctly notes that stopping the server with active clients causes process zombification in the VM. While the Stop method is properly integrated with defer in the daemon, the operational risk isn't visible at runtime.

Consider adding a structured warning log when Stop() is called to make this risk visible to operators:

 func (s *Server) Stop() error {
+	logrus.Warnf("stopping 9p server for %s - active client processes may zombify until VM restart", s.ExposedDir)
 	if err := s.Listener.Close(); err != nil {
 		return err
 	}
-	logrus.Infof("stopped 9p server for directory %s", s.ExposedDir)
 	return nil
 }

This makes the limitation discoverable in logs during troubleshooting.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d56fb1a and a12218e.

📒 Files selected for processing (5)
  • cmd/crc/cmd/daemon.go (5 hunks)
  • pkg/crc/constants/constants.go (1 hunks)
  • pkg/crc/machine/libhvee/driver_windows.go (1 hunks)
  • pkg/crc/machine/start.go (2 hunks)
  • pkg/fileserver/fs9p/server.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • pkg/crc/machine/start.go
  • pkg/crc/machine/libhvee/driver_windows.go
  • pkg/crc/constants/constants.go
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: redbeam
PR: crc-org/crc#4866
File: pkg/fileserver/fs9p/shares.go:0-0
Timestamp: 2025-08-06T09:48:10.441Z
Learning: The vsock implementation in pkg/fileserver/fs9p/shares.go was temporarily removed because 9pfs doesn't fully support it yet. It will be added back when 9pfs provides proper vsock support.
🧬 Code graph analysis (2)
cmd/crc/cmd/daemon.go (4)
pkg/crc/config/settings.go (1)
  • EnableSharedDirs (34-34)
pkg/crc/constants/constants.go (2)
  • Plan9TcpPort (60-60)
  • GetHomeDir (164-170)
pkg/fileserver/fs9p/server.go (1)
  • New9pServer (34-58)
pkg/crc/logging/logging.go (1)
  • Warnf (100-102)
pkg/fileserver/fs9p/server.go (3)
vendor/github.com/DeedleFake/p9/fs.go (2)
  • FileSystem (21-34)
  • FSConnHandler (162-167)
vendor/github.com/DeedleFake/p9/proto/server.go (1)
  • Serve (17-42)
pkg/crc/constants/constants.go (1)
  • Plan9Msize (59-59)
⏰ 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). (18)
  • GitHub Check: build-qe (windows, amd64)
  • GitHub Check: build-qe (linux, amd64)
  • GitHub Check: build-qe (linux, arm64)
  • GitHub Check: build-qe (darwin, arm64)
  • GitHub Check: build-qe (darwin, amd64)
  • GitHub Check: build-installer (windows-2022, 1.24)
  • GitHub Check: build (ubuntu-latest, 1.24)
  • GitHub Check: verify-devcontainer
  • GitHub Check: build (macOS-13, 1.24)
  • GitHub Check: build (macOS-14, 1.24)
  • GitHub Check: build (ubuntu-22.04, 1.24)
  • GitHub Check: build (macOS-13, 1.24)
  • GitHub Check: build (macOS-14, 1.24)
  • GitHub Check: build (ubuntu-latest, 1.24)
  • GitHub Check: Run OKD bundle with crc (1.24)
  • GitHub Check: build (windows-2022, 1.24)
  • GitHub Check: build (windows-2022, 1.24)
  • GitHub Check: Konflux kflux-prd-rh02 / crc-binary-on-pull-request
🔇 Additional comments (6)
cmd/crc/cmd/daemon.go (3)

14-14: LGTM: Import additions support 9p integration.

The runtime import enables OS detection for Windows-specific 9p sharing, and the fs9p import provides the new server implementation.

Also applies to: 28-28


182-182: LGTM: Improved host:port construction.

Using net.JoinHostPort is more robust than string concatenation and properly handles both IPv4 and IPv6 addresses. Good consistency with the 9p listener initialization.

Also applies to: 198-198


253-276: LGTM: Well-structured 9p sharing lifecycle.

The Windows-specific 9p home directory sharing implementation properly handles:

  • Conditional execution (OS + config check)
  • Listener creation on the virtual network
  • Server initialization and startup with error propagation
  • Cleanup via defer to ensure Stop() runs on exit
  • Background error monitoring with single WaitForError() call

The use of constants.GetHomeDir() can panic if the home directory is unavailable, but this is acceptable since sharing cannot proceed without it, and the failure occurs during startup (making it immediately visible).

pkg/fileserver/fs9p/server.go (3)

34-58: LGTM: Robust constructor validation.

The constructor performs thorough validation:

  • Requires absolute paths (prevents ambiguity)
  • Verifies directory existence via os.Stat
  • Confirms path is a directory, not a file
  • Creates buffered ErrChan (size 1) to prevent goroutine blocking on shutdown

Error messages are clear and actionable. Well-designed initialization.


60-75: LGTM: Server startup with immediate error detection.

The Start method launches proto.Serve in a goroutine and uses a non-blocking select to detect immediate startup failures (e.g., invalid listener). The design is sound: proto.Serve loops on Accept(), so listener errors surface immediately, while operational errors during serving are caught by WaitForError().

A past review suggested adding a 100ms timeout to the select for more defensive error detection. While the current approach works in practice, adding a timeout would eliminate the theoretical race window. Given the narrow scope of the race, this is an optional improvement.


89-98: LGTM: Error monitoring with graceful shutdown handling.

WaitForError blocks on the error channel and includes special handling for the Windows-specific "endpoint is in invalid state" error during graceful shutdown. The method works correctly with the current usage pattern in daemon.go (single call in a goroutine), where the channel is guaranteed to close after proto.Serve exits.

A past review suggested adding context.Context timeout support for more defensive programming. The current implementation is functionally correct for the usage pattern, so context support would be an optional improvement if this method is reused in scenarios requiring timeout control.

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: 2

♻️ Duplicate comments (21)
pkg/fileserver/fs9p/server.go (3)

89-98: WaitForError blocks indefinitely without timeout.

The method blocks forever waiting on ErrChan. If the server never produces an error, callers will hang indefinitely with no way to cancel.

Add context support for cancellation:

-func (s *Server) WaitForError() error {
+func (s *Server) WaitForError(ctx context.Context) error {
+	select {
+	case err := <-s.ErrChan:
-	err := <-s.ErrChan
 		// captures "accept tcp: endpoint is in invalid state" errors on exit
 		var opErr *net.OpError
 		if errors.As(err, &opErr) && strings.Contains(opErr.Error(), "endpoint is in invalid state") {
 			return nil
 		}
 		return err
+	case <-ctx.Done():
+		return ctx.Err()
+	}
-	return err
 }

Update callers to pass a context (e.g., from daemon lifecycle).


60-75: Startup error detection may miss early failures.

The non-blocking select with default at lines 68-74 may return success before the server goroutine encounters an error. If proto.Serve fails immediately after the goroutine starts, the error might not be caught by the select.

Consider adding a brief timeout to improve reliability:

 	// Just before returning, check to see if we got an error off server startup.
 	select {
 	case err := <-s.ErrChan:
 		return fmt.Errorf("starting 9p server: %w", err)
+	case <-time.After(100 * time.Millisecond):
+		logrus.Infof("started 9p server on %s for directory %s", s.Listener.Addr().String(), s.ExposedDir)
+		return nil
 	default:
 		logrus.Infof("started 9p server on %s for directory %s", s.Listener.Addr().String(), s.ExposedDir)
 		return nil
 	}

77-87: Document zombie process risk more prominently for operators.

The comment about zombie processes is critical for production operations but easy to miss. When clients are forcefully disconnected, their processes enter uninterruptible sleep.

Enhance operational visibility:

 func (s *Server) Stop() error {
+	logrus.Warnf("stopping 9p server for %s - active client connections may zombify", s.ExposedDir)
 	if err := s.Listener.Close(); err != nil {
 		return err
 	}
 	logrus.Infof("stopped 9p server for directory %s", s.ExposedDir)
 	return nil
 }

Additionally, document this limitation in user-facing documentation with recommended shutdown procedures.

vendor/github.com/DeedleFake/p9/client.go (1)

1-120: Vendored library contains critical issues flagged in previous reviews.

This is a vendored dependency from github.com/DeedleFake/p9. The past review comments identified several critical issues that remain unaddressed:

  1. Lines 45-47: FID generation can overflow causing conflicts
  2. Lines 61-62: Unsafe type assertion on rsp.(*Rversion) can panic
  3. Lines 84: Unsafe type assertion on rsp.(*Rauth) can panic
  4. Line 113: Unsafe type assertion on rsp.(*Rattach) can panic

Since this is vendored code, these issues should be reported and fixed upstream in the github.com/DeedleFake/p9 repository rather than patched locally.

Consider opening an issue upstream or contributing fixes to the DeedleFake/p9 project for these type safety concerns.

vendor/github.com/DeedleFake/p9/proto/encoding.go (1)

1-195: Vendored encoding library has critical correctness and portability bugs.

This vendored file from github.com/DeedleFake/p9 contains three unresolved issues from previous reviews:

  1. Lines 65-71: Custom Encoder handling incorrectly calculates size (measures slice header instead of data length)
  2. Lines 150-155: Unsafe pointer cast may cause endianness issues on big-endian systems
  3. Line 173: Unsafe string conversion using unsafe.String violates Go memory safety

These bugs affect correctness and portability of the 9P protocol implementation. Since this is vendored code, report these issues upstream to github.com/DeedleFake/p9.

Verify the version of the vendored library and check if newer versions address these issues.

#!/bin/bash
# Check the vendored library version
grep "github.com/DeedleFake/p9" go.mod
vendor/github.com/DeedleFake/p9/msg.go (1)

207-217: Error handling allows partial writes in P9Encode.

In the vendored Rstat.P9Encode method, if the first proto.Write succeeds but the second fails, the method returns partial data in buf.Bytes(). This violates atomicity and could corrupt the protocol stream.

The same issue exists in Twstat.P9Encode (lines 240-255).

Since this is vendored from github.com/DeedleFake/p9, report this upstream.

vendor/github.com/DeedleFake/p9/dir_linux.go (1)

37-37: Critical: Timespec.Unix() method doesn't exist on Linux.

Line 37 calls sys.Atim.Unix(), but syscall.Timespec on Linux only has Sec and Nsec fields, no Unix() method. This will cause a compilation error.

This is vendored code from github.com/DeedleFake/p9 and must be fixed upstream.

Verify this compiles on Linux:

#!/bin/bash
# Check if this file compiles on Linux
GOOS=linux go build -o /dev/null ./vendor/github.com/DeedleFake/p9/dir_linux.go 2>&1 || echo "Compilation failed as expected"
vendor/github.com/DeedleFake/p9/proto/client.go (2)

90-97: Read error loop doesn't exit on persistent failures.

In the vendored reader goroutine, when c.p.Receive returns an error that's neither EOF nor context cancellation, the code continues, potentially looping forever on persistent connection errors.

This is vendored from github.com/DeedleFake/p9 and should be fixed upstream.


138-145: Tag allocator can return reserved NoTag value.

The tag allocation logic doesn't prevent returning NoTag (0xFFFF) when the counter wraps around. This violates the protocol's reserved tag semantics and could cause message routing failures.

This is vendored code from github.com/DeedleFake/p9 and must be fixed upstream.

vendor/github.com/DeedleFake/p9/proto/proto.go (2)

101-108: Dead code: unreachable error check.

Lines 103-105 check if err != nil inside a t == nil block, but err is never assigned in that path. This is unreachable dead code.

This is vendored from github.com/DeedleFake/p9 and should be cleaned up upstream.


85-94: LimitedReader may allow over-reads.

The LimitedReader is set to read size bytes (line 91), but size includes the 4-byte length header already consumed. The reader should limit to size - 4 to prevent reading beyond the message payload.

Additionally, no guard against size < 4 exists, which could cause underflow.

This is vendored from github.com/DeedleFake/p9 and must be fixed upstream.

vendor/github.com/DeedleFake/p9/fs.go (7)

499-512: Directory read offset deviates from 9P spec

Spec requires offset 0 or previous offset + previous read length. Current code ignores non‑zero offsets.

Suggestion:

  • Track lastDirOff and lastDirCount per FID; validate msg.Offset.
  • Serve data slices using the offset into the serialized dir buffer rather than ignoring it.

179-181: Avoid unsafe string-to-bytes in QID hashing

Use a safe conversion; unsafe here isn’t needed.

-	sum := sha256.Sum256(unsafe.Slice(unsafe.StringData(p), len(p)))
+	sum := sha256.Sum256([]byte(p))
 	path := binary.LittleEndian.Uint64(sum[:])

Also remove the now-unused unsafe import at the top:

-	"unsafe"

244-249: Tflush handling

Returning Rerror breaks clients that send flush. Either return Rflush or implement cancellation; minimally, acknowledge flush.

 func (h *fsHandler) flush(msg *Tflush) any {
-	// TODO: Implement this.
-	return &Rerror{
-		Ename: "flush is not supported",
-	}
+	// Best-effort acknowledgement; cancellation not guaranteed.
+	return &Rflush{}
 }

536-537: Writes must use exclusive lock, not RLock

RLock during WriteAt can race with other ops and corrupt state.

-	file.RLock()         // Somewhat ironic that this doesn't require a
-	defer file.RUnlock() // full lock like read() does.
+	file.Lock()
+	defer file.Unlock()

566-574: Clunk uses RLock while mutating/closing

Close mutates state; use exclusive lock.

-	file.RLock()
-	defer file.RUnlock()
+	file.Lock()
+	defer file.Unlock()
@@
-	err := file.file.Close()
+	err := file.file.Close()

583-607: remove: don’t hold file lock across clunk; fix error check; snapshot before remove

Call clunk without the file lock; check for protocol Rerror; then remove via snapshot of path/attachment.

 func (h *fsHandler) remove(msg *Tremove) any {
 	file, ok := h.getFile(msg.FID, false)
 	if !ok {
 		return &Rerror{
 			Ename: "unknown FID",
 		}
 	}
-	file.RLock()
-	defer file.RUnlock()
-
-	rsp := h.clunk(&Tclunk{
-		FID: msg.FID,
-	})
-	if _, ok := rsp.(error); ok {
-		return rsp
-	}
-
-	err := file.a.Remove(file.path)
+	// Snapshot without holding lock across clunk/remove.
+	file.RLock()
+	p := file.path
+	a := file.a
+	file.RUnlock()
+
+	rsp := h.clunk(&Tclunk{FID: msg.FID})
+	if rer, ok := rsp.(*Rerror); ok {
+		return rer
+	}
+
+	err := a.Remove(p)
 	if err != nil {
 		return &Rerror{
 			Ename: err.Error(),
 		}
 	}
 
 	return &Rremove{}
 }

719-729: Close should lock per-file and null the handle

Prevent races with in-flight ops and double-closes.

 func (h *fsHandler) Close() error {
 	h.fids.Range(func(k, v any) bool {
 		file := v.(*fsFile)
-		if file.file != nil {
-			file.file.Close()
-		}
+		file.Lock()
+		if file.file != nil {
+			_ = file.file.Close()
+			file.file = nil
+		}
+		file.Unlock()
 		return true
 	})
 
 	return nil
 }
vendor/github.com/DeedleFake/p9/stat.go (1)

135-153: Avoid unsafe.String; use string(buf)

This conversion doesn’t need unsafe and may surprise maintainers/tools.

 func (m FileMode) String() string {
@@
-	return unsafe.String(unsafe.SliceData(buf), len(buf))
+	return string(buf)
 }

Then drop the unused unsafe import in this file.

vendor/github.com/DeedleFake/p9/proto/server.go (2)

70-77: Missing continue after read error

On read error (non-EOF), you log but still process tmsg/tag, which may be invalid. Skip the iteration.

 		tmsg, tag, err := p.Receive(c, msize)
 		if err != nil {
 			if err == io.EOF {
 				return
 			}
 
 			log.Printf("Error reading message: %v", err)
+			continue
 		}

79-99: Goroutine closes over loop vars tag/tmsg

Copy tag and tmsg into locals before launching; otherwise values can race/mismatch when mode becomes async.

-		mode(func() {
-			rmsg := handler.HandleMessage(tmsg)
+		ctag := tag
+		ctmsg := tmsg
+		mode(func() {
+			rmsg := handler.HandleMessage(ctmsg)
 			if rmsg, ok := rmsg.(Msizer); ok {
 				if msize > 0 {
 					log.Println("Warning: Attempted to set msize twice.")
 				}
 
 				setter.Do(func() {
 					msize = rmsg.P9Msize()
 					mode = func(f func()) {
 						go f()
 					}
 				})
 			}
 
-			err := p.Send(c, tag, rmsg)
+			err := p.Send(c, ctag, rmsg)
 			if err != nil {
 				log.Printf("Error writing message: %v", err)
 			}
 		})
🧹 Nitpick comments (1)
vendor/github.com/DeedleFake/p9/fs.go (1)

529-555: Optional: enforce write size vs msize for symmetry with read

You check read counts against msize but not writes. Clients shouldn’t be able to send oversized writes, but validating count improves robustness and error messages.

 func (h *fsHandler) write(msg *Twrite) any {
+	if h.largeCount(uint32(len(msg.Data))) {
+		return &Rerror{Ename: "write too large"}
+	}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a12218e and 3d3599c.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (33)
  • cmd/crc/cmd/daemon.go (5 hunks)
  • go.mod (1 hunks)
  • pkg/crc/constants/constants.go (1 hunks)
  • pkg/crc/machine/libhvee/driver_windows.go (1 hunks)
  • pkg/crc/machine/start.go (2 hunks)
  • pkg/fileserver/fs9p/server.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/LICENSE (1 hunks)
  • vendor/github.com/DeedleFake/p9/README.md (1 hunks)
  • vendor/github.com/DeedleFake/p9/addr.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/addr_other.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/addr_unix.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/client.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_darwin.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_linux.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_other.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_plan9.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/dir_windows.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/doc.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/encoding.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/fs.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/internal/debug/debug.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/internal/debug/nodebug.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/internal/util/util.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/msg.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/p9.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/proto/client.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/proto/encoding.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/proto/proto.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/proto/server.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/remote.go (1 hunks)
  • vendor/github.com/DeedleFake/p9/stat.go (1 hunks)
  • vendor/modules.txt (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • vendor/github.com/DeedleFake/p9/doc.go
🚧 Files skipped from review as they are similar to previous changes (19)
  • pkg/crc/constants/constants.go
  • vendor/github.com/DeedleFake/p9/LICENSE
  • vendor/github.com/DeedleFake/p9/dir_darwin.go
  • go.mod
  • vendor/github.com/DeedleFake/p9/addr_unix.go
  • vendor/github.com/DeedleFake/p9/dir_plan9.go
  • cmd/crc/cmd/daemon.go
  • vendor/github.com/DeedleFake/p9/dir_other.go
  • vendor/github.com/DeedleFake/p9/encoding.go
  • pkg/crc/machine/libhvee/driver_windows.go
  • vendor/modules.txt
  • pkg/crc/machine/start.go
  • vendor/github.com/DeedleFake/p9/addr.go
  • vendor/github.com/DeedleFake/p9/dir_windows.go
  • vendor/github.com/DeedleFake/p9/dir.go
  • vendor/github.com/DeedleFake/p9/p9.go
  • vendor/github.com/DeedleFake/p9/addr_other.go
  • vendor/github.com/DeedleFake/p9/internal/util/util.go
  • vendor/github.com/DeedleFake/p9/remote.go
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: redbeam
PR: crc-org/crc#4866
File: pkg/fileserver/fs9p/shares.go:0-0
Timestamp: 2025-08-06T09:48:10.441Z
Learning: The vsock implementation in pkg/fileserver/fs9p/shares.go was temporarily removed because 9pfs doesn't fully support it yet. It will be added back when 9pfs provides proper vsock support.
🧬 Code graph analysis (12)
vendor/github.com/DeedleFake/p9/internal/debug/debug.go (1)
vendor/github.com/DeedleFake/p9/internal/debug/nodebug.go (1)
  • Log (6-6)
vendor/github.com/DeedleFake/p9/internal/debug/nodebug.go (1)
vendor/github.com/DeedleFake/p9/internal/debug/debug.go (1)
  • Log (11-13)
vendor/github.com/DeedleFake/p9/client.go (5)
vendor/github.com/DeedleFake/p9/proto/client.go (3)
  • Client (18-31)
  • NewClient (35-56)
  • Dial (60-67)
vendor/github.com/DeedleFake/p9/msg.go (8)
  • Proto (75-77)
  • Tversion (79-82)
  • Tversion (84-84)
  • Rversion (86-89)
  • Tauth (95-99)
  • Rauth (101-103)
  • Tattach (105-110)
  • Rattach (112-114)
vendor/github.com/DeedleFake/p9/proto/proto.go (1)
  • Proto (38-41)
vendor/github.com/DeedleFake/p9/p9.go (3)
  • Version (10-10)
  • NoFID (18-18)
  • QID (42-46)
vendor/github.com/DeedleFake/p9/remote.go (1)
  • Remote (19-27)
vendor/github.com/DeedleFake/p9/proto/server.go (2)
vendor/github.com/DeedleFake/p9/msg.go (1)
  • Proto (75-77)
vendor/github.com/DeedleFake/p9/proto/proto.go (1)
  • Proto (38-41)
vendor/github.com/DeedleFake/p9/proto/client.go (4)
vendor/github.com/DeedleFake/p9/msg.go (1)
  • Proto (75-77)
vendor/github.com/DeedleFake/p9/proto/proto.go (3)
  • Proto (38-41)
  • NoTag (27-27)
  • ErrClientClosed (22-22)
vendor/github.com/DeedleFake/p9/client.go (3)
  • NewClient (30-32)
  • Client (22-26)
  • Dial (36-43)
vendor/github.com/DeedleFake/p9/internal/debug/debug.go (1)
  • Log (11-13)
vendor/github.com/DeedleFake/p9/proto/encoding.go (1)
vendor/github.com/DeedleFake/p9/internal/util/util.go (1)
  • Errorf (44-52)
vendor/github.com/DeedleFake/p9/fs.go (7)
vendor/github.com/DeedleFake/p9/stat.go (4)
  • Stat (156-168)
  • DirEntry (255-267)
  • StatChanges (316-318)
  • FileMode (23-23)
vendor/github.com/DeedleFake/p9/p9.go (7)
  • QID (42-46)
  • QIDType (49-49)
  • IOHeaderSize (70-70)
  • Version (10-10)
  • QTAuth (58-58)
  • NoFID (18-18)
  • QTDir (54-54)
vendor/github.com/DeedleFake/p9/proto/server.go (1)
  • MessageHandler (135-139)
vendor/github.com/DeedleFake/p9/internal/debug/debug.go (1)
  • Log (11-13)
vendor/github.com/DeedleFake/p9/internal/debug/nodebug.go (1)
  • Log (6-6)
vendor/github.com/DeedleFake/p9/msg.go (26)
  • Tversion (79-82)
  • Tversion (84-84)
  • Rversion (86-89)
  • Tauth (95-99)
  • Rauth (101-103)
  • Tflush (126-128)
  • Tattach (105-110)
  • Rattach (112-114)
  • Twalk (133-137)
  • Rwalk (139-141)
  • Topen (143-146)
  • Ropen (148-151)
  • Tcreate (153-158)
  • Rcreate (160-163)
  • Tread (165-169)
  • Rread (171-173)
  • Twrite (175-179)
  • Rwrite (181-183)
  • Tclunk (185-187)
  • Rclunk (189-190)
  • Tremove (192-194)
  • Rremove (196-197)
  • Tstat (199-201)
  • Rstat (203-205)
  • Twstat (235-238)
  • Rwstat (278-279)
vendor/github.com/DeedleFake/p9/encoding.go (1)
  • WriteDir (33-42)
vendor/github.com/DeedleFake/p9/proto/proto.go (3)
vendor/github.com/DeedleFake/p9/proto/encoding.go (3)
  • Read (34-38)
  • Write (25-31)
  • Size (14-20)
vendor/github.com/DeedleFake/p9/p9.go (1)
  • NoTag (15-15)
vendor/github.com/DeedleFake/p9/internal/util/util.go (2)
  • Errorf (44-52)
  • LimitedReader (13-17)
pkg/fileserver/fs9p/server.go (3)
vendor/github.com/DeedleFake/p9/fs.go (2)
  • FileSystem (21-34)
  • FSConnHandler (162-167)
vendor/github.com/DeedleFake/p9/proto/server.go (1)
  • Serve (17-42)
pkg/crc/constants/constants.go (1)
  • Plan9Msize (59-59)
vendor/github.com/DeedleFake/p9/stat.go (3)
vendor/github.com/DeedleFake/p9/p9.go (3)
  • QIDType (49-49)
  • QID (42-46)
  • Version (10-10)
vendor/github.com/DeedleFake/p9/proto/encoding.go (3)
  • Write (25-31)
  • Read (34-38)
  • Size (14-20)
vendor/github.com/DeedleFake/p9/internal/util/util.go (1)
  • LimitedReader (13-17)
vendor/github.com/DeedleFake/p9/msg.go (6)
vendor/github.com/DeedleFake/p9/proto/proto.go (2)
  • NewProto (44-60)
  • Proto (38-41)
vendor/github.com/DeedleFake/p9/p9.go (2)
  • Version (10-10)
  • QID (42-46)
vendor/github.com/DeedleFake/p9/proto/client.go (1)
  • P9NoTag (221-223)
vendor/github.com/DeedleFake/p9/stat.go (3)
  • FileMode (23-23)
  • Stat (156-168)
  • ErrLargeStat (18-18)
vendor/github.com/DeedleFake/p9/proto/encoding.go (2)
  • Write (25-31)
  • Read (34-38)
vendor/github.com/DeedleFake/p9/internal/util/util.go (1)
  • LimitedReader (13-17)
vendor/github.com/DeedleFake/p9/dir_linux.go (3)
vendor/github.com/DeedleFake/p9/stat.go (3)
  • DirEntry (255-267)
  • FileMode (23-23)
  • ModeFromOS (43-78)
vendor/github.com/DeedleFake/p9/dir.go (1)
  • Dir (16-16)
vendor/github.com/DeedleFake/p9/p9.go (3)
  • QID (42-46)
  • QIDType (49-49)
  • Version (10-10)
🪛 markdownlint-cli2 (0.18.1)
vendor/github.com/DeedleFake/p9/README.md

12-12: Heading style
Expected: setext; Actual: atx

(MD003, heading-style)


17-17: Hard tabs
Column: 1

(MD010, no-hard-tabs)


21-21: Heading style
Expected: setext; Actual: atx

(MD003, heading-style)


26-26: Hard tabs
Column: 1

(MD010, no-hard-tabs)


32-32: Hard tabs
Column: 1

(MD010, no-hard-tabs)


37-37: Hard tabs
Column: 1

(MD010, no-hard-tabs)


43-43: Hard tabs
Column: 1

(MD010, no-hard-tabs)


49-49: Hard tabs
Column: 1

(MD010, no-hard-tabs)

⏰ 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). (18)
  • GitHub Check: build-qe (darwin, arm64)
  • GitHub Check: Run OKD bundle with crc (1.24)
  • GitHub Check: build-qe (linux, arm64)
  • GitHub Check: verify-devcontainer
  • GitHub Check: build-qe (linux, amd64)
  • GitHub Check: build-qe (darwin, amd64)
  • GitHub Check: build-qe (windows, amd64)
  • GitHub Check: build-installer (windows-2022, 1.24)
  • GitHub Check: build (macOS-13, 1.24)
  • GitHub Check: build (ubuntu-latest, 1.24)
  • GitHub Check: build (macOS-14, 1.24)
  • GitHub Check: build (ubuntu-latest, 1.24)
  • GitHub Check: build (windows-2022, 1.24)
  • GitHub Check: build (ubuntu-22.04, 1.24)
  • GitHub Check: build (windows-2022, 1.24)
  • GitHub Check: build (macOS-14, 1.24)
  • GitHub Check: build (macOS-13, 1.24)
  • GitHub Check: Konflux kflux-prd-rh02 / crc-binary-on-pull-request
🔇 Additional comments (4)
vendor/github.com/DeedleFake/p9/internal/debug/debug.go (1)

1-13: LGTM! Standard build-tag-gated debug logging.

This vendored debug implementation follows a common Go pattern: the p9debug build tag gates actual logging to stderr, while the no-op variant in nodebug.go is compiled by default. The implementation is clean and appropriate for internal debug utilities.

vendor/github.com/DeedleFake/p9/internal/debug/nodebug.go (1)

1-6: LGTM! Complementary no-op stub.

The !p9debug build tag ensures this no-op variant is compiled by default, providing zero runtime overhead when debug logging is disabled. The function signature correctly matches the active implementation in debug.go.

vendor/github.com/DeedleFake/p9/README.md (1)

1-51: LGTM - vendored documentation.

This is documentation from the vendored library. The markdown linting hints are minor formatting issues that should be addressed upstream if at all.

pkg/fileserver/fs9p/server.go (1)

34-58: LGTM - Constructor validates inputs correctly.

The New9pServer constructor properly validates that:

  • exposeDir is an absolute path
  • exposeDir exists and is a directory
  • Creates a buffered ErrChan to prevent goroutine blocking

The implementation is solid.

Comment on lines +641 to +663
func (h *fsHandler) wstat(msg *Twstat) any {
file, ok := h.getFile(msg.FID, false)
if !ok {
return &Rerror{
Ename: "unknown FID",
}
}
file.RLock()
defer file.RUnlock()

changes := StatChanges{
DirEntry: msg.Stat.DirEntry(),
}

err := file.a.WriteStat(file.path, changes)
if err != nil {
return &Rerror{
Ename: err.Error(),
}
}

return &Rwstat{}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

wstat rename: update in-memory path and use exclusive lock

If Name() changes, file.path must be updated to keep the FID consistent; also we’re mutating state, so use Lock.

 func (h *fsHandler) wstat(msg *Twstat) any {
 	file, ok := h.getFile(msg.FID, false)
 	if !ok {
 		return &Rerror{
 			Ename: "unknown FID",
 		}
 	}
-	file.RLock()
-	defer file.RUnlock()
+	file.Lock()
+	defer file.Unlock()
@@
 	err := file.a.WriteStat(file.path, changes)
 	if err != nil {
 		return &Rerror{
 			Ename: err.Error(),
 		}
 	}
+
+	// Update local path on rename.
+	if newName, ok := changes.Name(); ok {
+		file.path = path.Join(path.Dir(file.path), newName)
+	}
 
 	return &Rwstat{}
 }

Comment on lines +61 to +67
func handleMessages(c net.Conn, p Proto, handler MessageHandler) {
var setter sync.Once

var msize uint32
mode := func(f func()) {
f()
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Concurrent Send on a shared net.Conn — serialize or verify

Once mode switches to async, multiple goroutines may call p.Send on the same net.Conn, which can interleave writes unless p.Send is internally synchronized. Either guard sends with a per-connection mutex or confirm Proto.Send is concurrency-safe.

Optional serialization here:

 func handleMessages(c net.Conn, p Proto, handler MessageHandler) {
 	var setter sync.Once
+	var sendMu sync.Mutex
 
 	var msize uint32
 	mode := func(f func()) {
 		f()
 	}
@@
-			err := p.Send(c, ctag, rmsg)
+			sendMu.Lock()
+			err := p.Send(c, ctag, rmsg)
+			sendMu.Unlock()
 			if err != nil {
 				log.Printf("Error writing message: %v", err)
 			}

Run to verify Send’s internals:

Also applies to: 94-97


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -nP '(?m)^func\s*\(\s*\w+\s+\*?Proto\s*\)\s*Send\s*\(' vendor/github.com/DeedleFake/p9/proto || true
rg -nP '(?m)\btype\s+Proto\b' vendor/github.com/DeedleFake/p9/proto || true
rg -nP '(?m)\bsendMu\b|\bmutex\b' vendor/github.com/DeedleFake/p9/proto || true

Length of output: 188



🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Get Proto struct definition
echo "=== Proto struct definition ==="
sed -n '38,100p' vendor/github.com/DeedleFake/p9/proto/proto.go

echo ""
echo "=== Send method implementation ==="
sed -n '130,150p' vendor/github.com/DeedleFake/p9/proto/proto.go

echo ""
echo "=== handleMessages function around lines 61-67 ==="
sed -n '55,100p' vendor/github.com/DeedleFake/p9/proto/server.go

echo ""
echo "=== handleMessages function around lines 94-97 ==="
sed -n '85,110p' vendor/github.com/DeedleFake/p9/proto/server.go

Length of output: 3680


Concurrent sends on shared net.Conn require synchronization — add per-connection mutex

Verification confirms the race condition is real:

  • Proto struct has no mutex field and Send() method has no internal locking
  • After setter.Do() at lines 94-97 in server.go, mode switches from synchronous to async (go f())
  • Multiple goroutines then call p.Send(c, tag, rmsg) on the same connection c
  • Proto.Send() performs an unsynchronized io.Copy(w, buf) write to the connection, causing concurrent writes to interleave

Add var sendMu sync.Mutex guard around p.Send() calls as suggested:

 func handleMessages(c net.Conn, p Proto, handler MessageHandler) {
 	var setter sync.Once
+	var sendMu sync.Mutex
 
 	var msize uint32
 	mode := func(f func()) {
 		f()
 	}
@@
-			err := p.Send(c, tag, rmsg)
+			sendMu.Lock()
+			err := p.Send(c, tag, rmsg)
+			sendMu.Unlock()
 			if err != nil {
 				log.Printf("Error writing message: %v", err)
 			}

Also apply at lines 94-97 context (same p.Send() call within the async goroutine).

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In vendor/github.com/DeedleFake/p9/proto/server.go around lines 61-67,
concurrent goroutines call p.Send() on the same net.Conn causing unsynchronized
writes; add a per-connection mutex (e.g., declare var sendMu sync.Mutex near the
top of handleMessages) and wrap every p.Send(c, ...) invocation with
sendMu.Lock() / sendMu.Unlock(), including the p.Send() in the async goroutine
executed after setter.Do() at lines ~94-97, to ensure all writes to the shared
connection are serialized.

@openshift-ci
Copy link

openshift-ci bot commented Oct 23, 2025

@redbeam: The following tests failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/security 3d3599c link false /test security
ci/prow/e2e-crc 3d3599c link true /test e2e-crc

Full PR test history. Your PR dashboard.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Ready for review

Development

Successfully merging this pull request may close these issues.

4 participants