github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/os/dir_wasip1.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // This file was derived from src/os/dir_darwin.go since the logic for WASI is 6 // fairly similar: we use fdopendir, fdclosedir, and readdir from wasi-libc in 7 // a similar way that the darwin code uses functions from libc. 8 9 //go:build wasip1 10 11 package os 12 13 import ( 14 "io" 15 "runtime" 16 "syscall" 17 "unsafe" 18 ) 19 20 // opaque DIR* returned by fdopendir 21 // 22 // We add an unused field so it is not the empty struct, which is usually 23 // a special case in Go. 24 type dirInfo struct{ _ int32 } 25 26 func (d *dirInfo) close() { 27 syscall.Fdclosedir(uintptr(unsafe.Pointer(d))) 28 } 29 30 func (f *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEntry, infos []FileInfo, err error) { 31 if f.dirinfo == nil { 32 dir, errno := syscall.Fdopendir(syscallFd(f.handle.(unixFileHandle))) 33 if errno != nil { 34 return nil, nil, nil, &PathError{Op: "fdopendir", Path: f.name, Err: errno} 35 } 36 f.dirinfo = (*dirInfo)(unsafe.Pointer(dir)) 37 } 38 d := uintptr(unsafe.Pointer(f.dirinfo)) 39 40 // see src/os/dir_unix.go 41 if n == 0 { 42 n = -1 43 } 44 45 for n != 0 { 46 dirent, errno := syscall.Readdir(d) 47 if errno != nil { 48 if errno == syscall.EINTR { 49 continue 50 } 51 return names, dirents, infos, &PathError{Op: "readdir", Path: f.name, Err: errno} 52 } 53 if dirent == nil { // EOF 54 break 55 } 56 name := dirent.Name() 57 // Check for useless names before allocating a string. 58 if string(name) == "." || string(name) == ".." { 59 continue 60 } 61 if n > 0 { 62 n-- 63 } 64 if mode == readdirName { 65 names = append(names, string(name)) 66 } else if mode == readdirDirEntry { 67 de, err := newUnixDirent(f.name, string(name), dtToType(dirent.Type)) 68 if IsNotExist(err) { 69 // File disappeared between readdir and stat. 70 // Treat as if it didn't exist. 71 continue 72 } 73 if err != nil { 74 return nil, dirents, nil, err 75 } 76 dirents = append(dirents, de) 77 } else { 78 info, err := lstat(f.name + "/" + string(name)) 79 if IsNotExist(err) { 80 // File disappeared between readdir + stat. 81 // Treat as if it didn't exist. 82 continue 83 } 84 if err != nil { 85 return nil, nil, infos, err 86 } 87 infos = append(infos, info) 88 } 89 runtime.KeepAlive(f) 90 } 91 92 if n > 0 && len(names)+len(dirents)+len(infos) == 0 { 93 return nil, nil, nil, io.EOF 94 } 95 return names, dirents, infos, nil 96 } 97 98 func dtToType(typ uint8) FileMode { 99 switch typ { 100 case syscall.DT_BLK: 101 return ModeDevice 102 case syscall.DT_CHR: 103 return ModeDevice | ModeCharDevice 104 case syscall.DT_DIR: 105 return ModeDir 106 case syscall.DT_FIFO: 107 return ModeNamedPipe 108 case syscall.DT_LNK: 109 return ModeSymlink 110 case syscall.DT_REG: 111 return 0 112 } 113 return ^FileMode(0) 114 }