dir_darwin.mx raw
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 package os
6
7 import (
8 "io"
9 "runtime"
10 "syscall"
11 "unsafe"
12 )
13
14 // Auxiliary information if the File describes a directory
15 type dirInfo struct {
16 dir uintptr // Pointer to DIR structure from dirent.h
17 }
18
19 func (d *dirInfo) close() {
20 if d.dir == 0 {
21 return
22 }
23 closedir(d.dir)
24 d.dir = 0
25 }
26
27 func (f *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEntry, infos []FileInfo, err error) {
28 if f.dirinfo == nil {
29 dir, call, errno := darwinOpenDir(syscallFd(f.handle.(unixFileHandle)))
30 if errno != nil {
31 return nil, nil, nil, &PathError{Op: call, Path: f.name, Err: errno}
32 }
33 f.dirinfo = &dirInfo{
34 dir: dir,
35 }
36 }
37 d := f.dirinfo
38
39 size := n
40 if size <= 0 {
41 size = 100
42 n = -1
43 }
44
45 var dirent syscall.Dirent
46 var entptr *syscall.Dirent
47 for len(names)+len(dirents)+len(infos) < size || n == -1 {
48 if errno := readdir_r(d.dir, &dirent, &entptr); errno != 0 {
49 if errno == syscall.EINTR {
50 continue
51 }
52 return names, dirents, infos, &PathError{Op: "readdir", Path: f.name, Err: errno}
53 }
54 if entptr == nil { // EOF
55 break
56 }
57 if dirent.Ino == 0 {
58 continue
59 }
60 name := (*[len(syscall.Dirent{}.Name)]byte)(unsafe.Pointer(&dirent.Name))[:]
61 for i, c := range name {
62 if c == 0 {
63 name = name[:i]
64 break
65 }
66 }
67 // Check for useless names before allocating a string.
68 if string(name) == "." || string(name) == ".." {
69 continue
70 }
71 if mode == readdirName {
72 names = append(names, string(name))
73 } else if mode == readdirDirEntry {
74 de, err := newUnixDirent(f.name, string(name), dtToType(dirent.Type))
75 if IsNotExist(err) {
76 // File disappeared between readdir and stat.
77 // Treat as if it didn't exist.
78 continue
79 }
80 if err != nil {
81 return nil, dirents, nil, err
82 }
83 dirents = append(dirents, de)
84 } else {
85 info, err := lstat(f.name + "/" + string(name))
86 if IsNotExist(err) {
87 // File disappeared between readdir + stat.
88 // Treat as if it didn't exist.
89 continue
90 }
91 if err != nil {
92 return nil, nil, infos, err
93 }
94 infos = append(infos, info)
95 }
96 runtime.KeepAlive(f)
97 }
98
99 if n > 0 && len(names)+len(dirents)+len(infos) == 0 {
100 return nil, nil, nil, io.EOF
101 }
102 return names, dirents, infos, nil
103 }
104
105 func dtToType(typ uint8) FileMode {
106 switch typ {
107 case syscall.DT_BLK:
108 return ModeDevice
109 case syscall.DT_CHR:
110 return ModeDevice | ModeCharDevice
111 case syscall.DT_DIR:
112 return ModeDir
113 case syscall.DT_FIFO:
114 return ModeNamedPipe
115 case syscall.DT_LNK:
116 return ModeSymlink
117 case syscall.DT_REG:
118 return 0
119 case syscall.DT_SOCK:
120 return ModeSocket
121 }
122 return ^FileMode(0)
123 }
124
125 // darwinOpenDir returns a pointer to a DIR structure suitable for
126 // ReadDir. In case of an error, the name of the failed
127 // syscall is returned along with a syscall.Errno.
128 // Borrowed from upstream's internal/poll/fd_opendir_darwin.go
129 func darwinOpenDir(fd syscallFd) (uintptr, string, error) {
130 // fdopendir(3) takes control of the file descriptor,
131 // so use a dup.
132 fd2, err := syscall.Dup(fd)
133 if err != nil {
134 return 0, "dup", err
135 }
136 var dir uintptr
137 for {
138 dir, err = fdopendir(fd2)
139 if err != syscall.EINTR {
140 break
141 }
142 }
143 if err != nil {
144 syscall.Close(fd2)
145 return 0, "fdopendir", err
146 }
147 return dir, "", nil
148 }
149
150 // Implemented in syscall/syscall_libc_darwin_*.go.
151
152 //go:linkname fdopendir syscall.fdopendir
153 func fdopendir(fd int) (dir uintptr, err error)
154
155 //go:linkname closedir syscall.closedir
156 func closedir(dir uintptr) (err error)
157
158 //go:linkname readdir_r syscall.readdir_r
159 func readdir_r(dir uintptr, entry *syscall.Dirent, result **syscall.Dirent) (res syscall.Errno)
160