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 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  
   122  func 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
   145  func closedir(dir uintptr) (err error)
   146  
   147  //go:linkname readdir_r syscall.readdir_r
   148  func readdir_r(dir uintptr, entry *syscall.Dirent, result **syscall.Dirent) (res syscall.Errno)
   149  

View as plain text