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 5package os 6 7import ( 8 "io" 9 "runtime" 10 "syscall" 11 "unsafe" 12) 13 14// Auxiliary information if the File describes a directory 15type dirInfo struct { 16 dir uintptr // Pointer to DIR structure from dirent.h 17} 18 19func (d *dirInfo) close() { 20 if d.dir == 0 { 21 return 22 } 23 closedir(d.dir) 24 d.dir = 0 25} 26 27func (f *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEntry, infos []FileInfo, err error) { 28 // If this file has no dirinfo, create one. 29 var d *dirInfo 30 for { 31 d = f.dirinfo.Load() 32 if d != nil { 33 break 34 } 35 dir, call, errno := f.pfd.OpenDir() 36 if errno != nil { 37 return nil, nil, nil, &PathError{Op: call, Path: f.name, Err: errno} 38 } 39 d = &dirInfo{dir: dir} 40 if f.dirinfo.CompareAndSwap(nil, d) { 41 break 42 } 43 // We lost the race: try again. 44 d.close() 45 } 46 47 size := n 48 if size <= 0 { 49 size = 100 50 n = -1 51 } 52 53 var dirent syscall.Dirent 54 var entptr *syscall.Dirent 55 for len(names)+len(dirents)+len(infos) < size || n == -1 { 56 if errno := readdir_r(d.dir, &dirent, &entptr); errno != 0 { 57 if errno == syscall.EINTR { 58 continue 59 } 60 return names, dirents, infos, &PathError{Op: "readdir", Path: f.name, Err: errno} 61 } 62 if entptr == nil { // EOF 63 break 64 } 65 // Darwin may return a zero inode when a directory entry has been 66 // deleted but not yet removed from the directory. The man page for 67 // getdirentries(2) states that programs are responsible for skipping 68 // those entries: 69 // 70 // Users of getdirentries() should skip entries with d_fileno = 0, 71 // as such entries represent files which have been deleted but not 72 // yet removed from the directory entry. 73 // 74 if dirent.Ino == 0 { 75 continue 76 } 77 name := (*[len(syscall.Dirent{}.Name)]byte)(unsafe.Pointer(&dirent.Name))[:] 78 for i, c := range name { 79 if c == 0 { 80 name = name[:i] 81 break 82 } 83 } 84 // Check for useless names before allocating a string. 85 if string(name) == "." || string(name) == ".." { 86 continue 87 } 88 if mode == readdirName { 89 names = append(names, string(name)) 90 } else if mode == readdirDirEntry { 91 de, err := newUnixDirent(f.name, string(name), dtToType(dirent.Type)) 92 if IsNotExist(err) { 93 // File disappeared between readdir and stat. 94 // Treat as if it didn't exist. 95 continue 96 } 97 if err != nil { 98 return nil, dirents, nil, err 99 } 100 dirents = append(dirents, de) 101 } else { 102 info, err := lstat(f.name + "/" + string(name)) 103 if IsNotExist(err) { 104 // File disappeared between readdir + stat. 105 // Treat as if it didn't exist. 106 continue 107 } 108 if err != nil { 109 return nil, nil, infos, err 110 } 111 infos = append(infos, info) 112 } 113 runtime.KeepAlive(f) 114 } 115 116 if n > 0 && len(names)+len(dirents)+len(infos) == 0 { 117 return nil, nil, nil, io.EOF 118 } 119 return names, dirents, infos, nil 120} 121 122func dtToType(typ uint8) FileMode { 123 switch typ { 124 case syscall.DT_BLK: 125 return ModeDevice 126 case syscall.DT_CHR: 127 return ModeDevice | ModeCharDevice 128 case syscall.DT_DIR: 129 return ModeDir 130 case syscall.DT_FIFO: 131 return ModeNamedPipe 132 case syscall.DT_LNK: 133 return ModeSymlink 134 case syscall.DT_REG: 135 return 0 136 case syscall.DT_SOCK: 137 return ModeSocket 138 } 139 return ^FileMode(0) 140} 141 142// Implemented in syscall/syscall_darwin.go. 143 144//go:linkname closedir syscall.closedir 145func closedir(dir uintptr) (err error) 146 147//go:linkname readdir_r syscall.readdir_r 148func readdir_r(dir uintptr, entry *syscall.Dirent, result **syscall.Dirent) (res syscall.Errno) 149