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