Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WASI: fs.FS as Preopen parameter with implementation of fd_prestat_get #362

Closed
wants to merge 6 commits into from

Conversation

nullpo-head
Copy link
Contributor

@nullpo-head nullpo-head commented Mar 11, 2022

This PR changes the input type of preopen from wasi.FS to fs.FS, which is more natural for users.
This PR also includes implementation of FdPrestatGet and PathOpen flags, which are necessary for multiple pre-open directories to work correctly.

@nullpo-head nullpo-head force-pushed the wasi_fdstat branch 5 times, most recently from b7e2e0e to ad2dc8a Compare March 16, 2022 18:39
@nullpo-head nullpo-head changed the title Wasi fdstat WASI: fs.FS as Preopen parameter with implementation of fd_prestat_get Mar 16, 2022
@nullpo-head nullpo-head marked this pull request as ready for review March 16, 2022 18:55
internal/wasi/fs.go Outdated Show resolved Hide resolved
internal/wasi/fs.go Outdated Show resolved Hide resolved
for name, entry := range dir.Entries {
entries = append(entries, &memFileInfo{name: path.Base(name), fsEntry: entry})
}
sort.Slice(entries, func(i, j int) bool { return entries[i].Name() < entries[j].Name() })
Copy link
Member

Choose a reason for hiding this comment

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

q: is sorting dirs necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, it's necessary. fs.ReadDir requires the result is sorted, and the `fstest.TestFS" becomes flakey if we don't sort.

internal/wasi/fs.go Outdated Show resolved Hide resolved
internal/wasi/wasi.go Outdated Show resolved Hide resolved
return wasi.ErrnoBadf
}
writer, ok := f.file.(io.Writer)
if !ok {
Copy link
Member

Choose a reason for hiding this comment

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

q: in what case !ok in real world?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Realworld examples are when the fily system of f.file is an embed.FS. f.file is fs.File, which doesn't require io.Writer, so it doesn't always implement io.Writer. Maybe it's better to comment that. let me do it

internal/wasi/wasi.go Outdated Show resolved Hide resolved
Copy link
Contributor

@codefromthecrypt codefromthecrypt left a comment

Choose a reason for hiding this comment

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

Thanks for getting this started. I think there will be a fair amount of work to finish this up, and we don't want to hold the supporting changes hostage through that. Can you extract the wasi api changes and tests into a separate PR from the fs.FS stuff?

README.md Show resolved Hide resolved
internal/wasi/fs.go Outdated Show resolved Hide resolved
internal/wasi/fs.go Outdated Show resolved Hide resolved
internal/wasi/fs.go Outdated Show resolved Hide resolved
internal/wasi/fs.go Show resolved Hide resolved
internal/wasi/fs.go Show resolved Hide resolved
internal/wasi/fs.go Outdated Show resolved Hide resolved
internal/wasi/fs.go Show resolved Hide resolved
"simple file": {Contents: []byte("simple cont")},
"directory": {
Entries: map[string]*memFSEntry{
"file1 inside directory": {Contents: []byte("simple cont file1 inside directory")},
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe use more realistic paths here? it would make it easier to read and know the intent.

ex would it be "directory/file1" because this includes the name of the key?

also the contents if dummy can be shorter like single character or single word

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've changed the names to more realistic paths, but let me know if those are not what you intended. THanks!

internal/wasi/fs_test.go Show resolved Hide resolved
@codefromthecrypt
Copy link
Contributor

I'm going to take over this PR for some several minutes, rebase it after extracting the WASI functions to another PR then force push a more isolated variant to proceed with. bear with me a bit

@codefromthecrypt
Copy link
Contributor

once #388 is merged I'll rebase this and hand it back over to @nullpo-head. hopefully next time we can keep fixing WASI APIs separate from new API work that uses them.

@codefromthecrypt
Copy link
Contributor

okie dokie over to you!

Signed-off-by: Takaya Saeki <[email protected]>
Signed-off-by: Takaya Saeki <[email protected]>
Signed-off-by: Takaya Saeki <[email protected]>
Signed-off-by: Takaya Saeki <[email protected]>
@nullpo-head
Copy link
Contributor Author

ptal @codefromthecrypt @mathetake thanks!

Copy link
Contributor

@codefromthecrypt codefromthecrypt left a comment

Choose a reason for hiding this comment

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

memFS looks suspiciously like fstest.MapFS Please delete it and use fstest.MapFS if you need a dummy filesystem besides embedFS. The main point of this change is to stop bespoke APIs and use fs.FS instead.

I would be very very surprised if there isn't an existing fs.FS library out there that supports writes.

If we couple OpenFile(path string, flag int, perm os.FileMode) (fs.File, error) with fs.FS we almost guarantee people can't bring their own impls as an existing impl wouldn't be using our interfaces. Instead, until we find an alternative to OpenFile (this is a TODO to find one!), make a separate Config option for a write path that accepts this interface.

ex. config.WithOpenFile(path, OpenFile) and put a TODO to look more into options for writes that don't imply completely custom implementations of fs.FS

// Configure what resources you share with the WASI program.
wasiConfig := wazero.NewWASIConfig()
// Share fs.FS as pre-opened directories
wasiConfig = wasiConfig.WithPreopens(
Copy link
Contributor

Choose a reason for hiding this comment

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

Feels like WithXXXMount will be easier for people to understand that "preopens" WDYT? Also, we can make it chained instead of a map. the map can be an internal detail

Ex.

// documented as read-only
wasiConfig.WithMount("/input", embeddedFS)

},
)
// the wasm module will concatenate the contents of the two input.txt and write to output.txt
wasiConfig = wasiConfig.WithArgs("./input.txt", "/input/input.txt", "./output.txt")
Copy link
Contributor

Choose a reason for hiding this comment

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

just overwrite a file instead? that's a lot simpler and less code.

// OpenFile opens the named file with specified flag in the same way as os.OpenFile.
// For example, if the path does not exist and flag includes os.O_CREATE, it will create a new file with perm.
//
// Unlike os.OpenFile, OpenFile must reject paths that do not satisfy `fs.ValidPath(path)`,
Copy link
Contributor

Choose a reason for hiding this comment

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

I have a hard time believing there are any strict requirements that need to be made here based on the WASI spec. It doesn't have spec tests and at best very vaguely documented. Any constraints that make something behave different than something else in Go should be not just mentioned but also answered why. (Ex in RATIONALE is a good place as there's not likely any spec or spectest to say why)

Can you please document where these are coming from?

@codefromthecrypt
Copy link
Contributor

I'm going to fork this and try to salvage what I can from it, but generally this is far from ideal and time is better spent on another path. Hence closing it.

func (c *Config) Stdin(reader io.Reader) {
c.stdin = reader
Copy link
Contributor

Choose a reason for hiding this comment

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

not currently planning to do anything to try to make stdin/stdout/stderr act like normal files as they aren't and it is more distracting forcing them though a struct that only has one side useful than the special casing in the first place.

@codefromthecrypt
Copy link
Contributor

There's nothing to salvage from this PR except experience. Once things are stable we can do another impl, but research and citation here are a prerequisite so that we don't fall into the same hole again #390

Thanks for giving this a try

// fs.FS requires a FS to reject a path that doesn't satisfy fs.ValidPath.
if !fs.ValidPath(name) ||
// '\' works as alternative path separater and ':' allows to express a root drive directory.
// fs.FS implementation must reject those path on Windows. See Note in the doc of fs.ValidPath.
Copy link
Contributor

@codefromthecrypt codefromthecrypt Mar 20, 2022

Choose a reason for hiding this comment

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

this is a good example of a leaky abstraction. WASI isn't and OS and if it were it wouldn't be windows, and there's nothing about fs.FS that should ever allow windows paths to leak into it. Ex. if someone exposes their "c:" drive, that's not on us to enforce. The only sensible file mappings would be chrooted if even an actual filesystem.

Copy link
Contributor

Choose a reason for hiding this comment

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

In other words windows-based file handing in a barely specified posix system layer is a non-goal. We can use this to simplify discussions in future work. By adding code to address things like OS-specific things inside WASI, we are actually making the problem more confused, especially to people new who may not know Wasm is effectively a virtual machine, and if shares anything in common with OS, it would be unix, not windows, that we'd optimize for.

@mathetake mathetake deleted the wasi_fdstat branch March 23, 2022 04:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants