...

Source file src/os/dir_darwin.go

Documentation: os

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

View as plain text