]> Cypherpunks repositories - gostls13.git/commitdiff
syscall: fix opening of directories on wasip1
authorAchille Roussel <achille.roussel@gmail.com>
Sat, 29 Apr 2023 18:17:25 +0000 (11:17 -0700)
committerGopher Robot <gobot@golang.org>
Sun, 30 Apr 2023 00:23:53 +0000 (00:23 +0000)
Go programs targeting GOOS=wasip1 were failing to open directories when
executed with runtimes like wasmtime or wasmedge due to requesting
rights for operations that are not supported on directories such as
fd_read, fd_write, etc...

This change addresses the issue by performing a second path_open when
observing EISDIR, and masking the requested rights to only ask for
permissions to perform operations supported by a directory.

Change-Id: Ibf65acf4a38bc848a649f41dbd026507d8b63c82
Reviewed-on: https://go-review.googlesource.com/c/go/+/490755
Auto-Submit: Ian Lance Taylor <iant@google.com>
Run-TryBot: Ian Lance Taylor <iant@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>

src/syscall/fs_wasip1.go

index ef04af69660599ce6b7e2d66568d8fab0c736458..ab00e5ba22eafbf7b3eda2db18ab8f99ddaae1c8 100644 (file)
@@ -100,6 +100,63 @@ const (
        fullRights  = rights(^uint32(0))
        readRights  = rights(RIGHT_FD_READ | RIGHT_FD_READDIR)
        writeRights = rights(RIGHT_FD_DATASYNC | RIGHT_FD_WRITE | RIGHT_FD_ALLOCATE | RIGHT_PATH_FILESTAT_SET_SIZE)
+
+       // Some runtimes have very strict expectations when it comes to which
+       // rights can be enabled on files opened by path_open. The fileRights
+       // constant is used as a mask to retain only bits for operations that
+       // are supported on files.
+       fileRights rights = RIGHT_FD_DATASYNC |
+               RIGHT_FD_READ |
+               RIGHT_FD_SEEK |
+               RIGHT_FDSTAT_SET_FLAGS |
+               RIGHT_FD_SYNC |
+               RIGHT_FD_TELL |
+               RIGHT_FD_WRITE |
+               RIGHT_FD_ADVISE |
+               RIGHT_FD_ALLOCATE |
+               RIGHT_PATH_CREATE_DIRECTORY |
+               RIGHT_PATH_CREATE_FILE |
+               RIGHT_PATH_LINK_SOURCE |
+               RIGHT_PATH_LINK_TARGET |
+               RIGHT_PATH_OPEN |
+               RIGHT_FD_READDIR |
+               RIGHT_PATH_READLINK |
+               RIGHT_PATH_RENAME_SOURCE |
+               RIGHT_PATH_RENAME_TARGET |
+               RIGHT_PATH_FILESTAT_GET |
+               RIGHT_PATH_FILESTAT_SET_SIZE |
+               RIGHT_PATH_FILESTAT_SET_TIMES |
+               RIGHT_FD_FILESTAT_GET |
+               RIGHT_FD_FILESTAT_SET_SIZE |
+               RIGHT_FD_FILESTAT_SET_TIMES |
+               RIGHT_PATH_SYMLINK |
+               RIGHT_PATH_REMOVE_DIRECTORY |
+               RIGHT_PATH_UNLINK_FILE |
+               RIGHT_POLL_FD_READWRITE
+
+       // Runtimes like wasmtime and wasmedge will refuse to open directories
+       // if the rights requested by the application exceed the operations that
+       // can be performed on a directory.
+       dirRights rights = RIGHT_FD_SEEK |
+               RIGHT_FDSTAT_SET_FLAGS |
+               RIGHT_FD_SYNC |
+               RIGHT_PATH_CREATE_DIRECTORY |
+               RIGHT_PATH_CREATE_FILE |
+               RIGHT_PATH_LINK_SOURCE |
+               RIGHT_PATH_LINK_TARGET |
+               RIGHT_PATH_OPEN |
+               RIGHT_FD_READDIR |
+               RIGHT_PATH_READLINK |
+               RIGHT_PATH_RENAME_SOURCE |
+               RIGHT_PATH_RENAME_TARGET |
+               RIGHT_PATH_FILESTAT_GET |
+               RIGHT_PATH_FILESTAT_SET_SIZE |
+               RIGHT_PATH_FILESTAT_SET_TIMES |
+               RIGHT_FD_FILESTAT_GET |
+               RIGHT_FD_FILESTAT_SET_TIMES |
+               RIGHT_PATH_SYMLINK |
+               RIGHT_PATH_REMOVE_DIRECTORY |
+               RIGHT_PATH_UNLINK_FILE
 )
 
 // https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-fd_closefd-fd---result-errno
@@ -435,11 +492,11 @@ func Open(path string, openmode int, perm uint32) (int, error) {
        var rights rights
        switch openmode & (O_RDONLY | O_WRONLY | O_RDWR) {
        case O_RDONLY:
-               rights = fullRights & ^writeRights
+               rights = fileRights & ^writeRights
        case O_WRONLY:
-               rights = fullRights & ^readRights
+               rights = fileRights & ^readRights
        case O_RDWR:
-               rights = fullRights
+               rights = fileRights
        }
 
        var fdflags fdflags
@@ -458,10 +515,32 @@ func Open(path string, openmode int, perm uint32) (int, error) {
                pathLen,
                oflags,
                rights,
-               fullRights,
+               fileRights,
                fdflags,
                unsafe.Pointer(&fd),
        )
+       if errno == EISDIR && oflags == 0 && fdflags == 0 && ((rights & writeRights) == 0) {
+               // wasmtime and wasmedge will error if attempting to open a directory
+               // because we are asking for too many rights. However, we cannot
+               // determine ahread of time if the path we are about to open is a
+               // directory, so instead we fallback to a second call to path_open with
+               // a more limited set of rights.
+               //
+               // This approach is subject to a race if the file system is modified
+               // concurrently, so we also inject OFLAG_DIRECTORY to ensure that we do
+               // not accidentally open a file which is not a directory.
+               errno = path_open(
+                       dirFd,
+                       LOOKUP_SYMLINK_FOLLOW,
+                       pathPtr,
+                       pathLen,
+                       oflags|OFLAG_DIRECTORY,
+                       rights&dirRights,
+                       fileRights,
+                       fdflags,
+                       unsafe.Pointer(&fd),
+               )
+       }
        return int(fd), errnoErr(errno)
 }