Source file src/os/dir_darwin.go

     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 := f.pfd.OpenDir()
    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  		// Darwin may return a zero inode when a directory entry has been
    58  		// deleted but not yet removed from the directory. The man page for
    59  		// getdirentries(2) states that programs are responsible for skipping
    60  		// those entries:
    61  		//
    62  		//   Users of getdirentries() should skip entries with d_fileno = 0,
    63  		//   as such entries represent files which have been deleted but not
    64  		//   yet removed from the directory entry.
    65  		//
    66  		if dirent.Ino == 0 {
    67  			continue
    68  		}
    69  		name := (*[len(syscall.Dirent{}.Name)]byte)(unsafe.Pointer(&dirent.Name))[:]
    70  		for i, c := range name {
    71  			if c == 0 {
    72  				name = name[:i]
    73  				break
    74  			}
    75  		}
    76  		// Check for useless names before allocating a string.
    77  		if string(name) == "." || string(name) == ".." {
    78  			continue
    79  		}
    80  		if mode == readdirName {
    81  			names = append(names, string(name))
    82  		} else if mode == readdirDirEntry {
    83  			de, err := newUnixDirent(f.name, string(name), dtToType(dirent.Type))
    84  			if IsNotExist(err) {
    85  				// File disappeared between readdir and stat.
    86  				// Treat as if it didn't exist.
    87  				continue
    88  			}
    89  			if err != nil {
    90  				return nil, dirents, nil, err
    91  			}
    92  			dirents = append(dirents, de)
    93  		} else {
    94  			info, err := lstat(f.name + "/" + string(name))
    95  			if IsNotExist(err) {
    96  				// File disappeared between readdir + stat.
    97  				// Treat as if it didn't exist.
    98  				continue
    99  			}
   100  			if err != nil {
   101  				return nil, nil, infos, err
   102  			}
   103  			infos = append(infos, info)
   104  		}
   105  		runtime.KeepAlive(f)
   106  	}
   107  
   108  	if n > 0 && len(names)+len(dirents)+len(infos) == 0 {
   109  		return nil, nil, nil, io.EOF
   110  	}
   111  	return names, dirents, infos, nil
   112  }
   113  
   114  func dtToType(typ uint8) FileMode {
   115  	switch typ {
   116  	case syscall.DT_BLK:
   117  		return ModeDevice
   118  	case syscall.DT_CHR:
   119  		return ModeDevice | ModeCharDevice
   120  	case syscall.DT_DIR:
   121  		return ModeDir
   122  	case syscall.DT_FIFO:
   123  		return ModeNamedPipe
   124  	case syscall.DT_LNK:
   125  		return ModeSymlink
   126  	case syscall.DT_REG:
   127  		return 0
   128  	case syscall.DT_SOCK:
   129  		return ModeSocket
   130  	}
   131  	return ^FileMode(0)
   132  }
   133  
   134  // Implemented in syscall/syscall_darwin.go.
   135  
   136  //go:linkname closedir syscall.closedir
   137  func closedir(dir uintptr) (err error)
   138  
   139  //go:linkname readdir_r syscall.readdir_r
   140  func readdir_r(dir uintptr, entry *syscall.Dirent, result **syscall.Dirent) (res syscall.Errno)
   141  

View as plain text