package sysfs

import (
	
	
	

	experimentalsys 
	
	
)

func newOsFile( string,  experimentalsys.Oflag,  fs.FileMode,  *os.File) fsapi.File {
	// On POSIX, if a file is removed from or added to the directory after the
	// most recent call to opendir() or rewinddir(), whether a subsequent call
	// to readdir() returns an entry for that file is unspecified.
	//
	// And Windows cannot read files added to a directory after it was opened.
	// This was noticed in #1087 in zig tests.
	//
	// So there is no guarantee that files added after opendir() will be visible
	// in readdir(). Since we want those files to be visible, we need to
	// reopendir() to get the new state of the directory before readdir().
	return &osFile{path: , flag: , perm: , reopenDir: true, file: , fd: .Fd()}
}

// osFile is a file opened with this package, and uses os.File or syscalls to
// implement api.File.
type osFile struct {
	path string
	flag experimentalsys.Oflag
	perm fs.FileMode
	file *os.File
	fd   uintptr

	// reopenDir is true if reopen should be called before Readdir. This flag
	// is deferred until Readdir to prevent redundant rewinds. This could
	// happen if Seek(0) was called twice, or if in Windows, Seek(0) was called
	// before Readdir.
	reopenDir bool

	// closed is true when closed was called. This ensures proper sys.EBADF
	closed bool

	// cachedStat includes fields that won't change while a file is open.
	cachedSt *cachedStat
}

// cachedStat returns the cacheable parts of sys.Stat_t or an error if they
// couldn't be retrieved.
func ( *osFile) () ( uint64,  sys.Inode,  bool,  experimentalsys.Errno) {
	if .cachedSt == nil {
		if _,  = .Stat();  != 0 {
			return
		}
	}
	return .cachedSt.dev, .cachedSt.ino, .cachedSt.isDir, 0
}

// Dev implements the same method as documented on sys.File
func ( *osFile) () (uint64, experimentalsys.Errno) {
	, , ,  := .cachedStat()
	return , 
}

// Ino implements the same method as documented on sys.File
func ( *osFile) () (sys.Inode, experimentalsys.Errno) {
	, , ,  := .cachedStat()
	return , 
}

// IsDir implements the same method as documented on sys.File
func ( *osFile) () (bool, experimentalsys.Errno) {
	, , ,  := .cachedStat()
	return , 
}

// IsAppend implements File.IsAppend
func ( *osFile) () bool {
	return .flag&experimentalsys.O_APPEND == experimentalsys.O_APPEND
}

// SetAppend implements the same method as documented on sys.File
func ( *osFile) ( bool) ( experimentalsys.Errno) {
	if  {
		.flag |= experimentalsys.O_APPEND
	} else {
		.flag &= ^experimentalsys.O_APPEND
	}

	// appendMode cannot be changed later, so we have to re-open the file
	// https://github.com/golang/go/blob/go1.23/src/os/file_unix.go#L60
	return fileError(, .closed, .reopen())
}

func ( *osFile) () ( experimentalsys.Errno) {
	var (
		  bool
		 int64
		    error
	)

	,  = .IsDir()
	if  != 0 {
		return 
	}

	if ! {
		,  = .file.Seek(0, io.SeekCurrent)
		if  != nil {
			return experimentalsys.UnwrapOSError()
		}
	}

	// Clear any create or trunc flag, as we are re-opening, not re-creating.
	 := .flag &^ (experimentalsys.O_CREAT | experimentalsys.O_TRUNC)
	,  := OpenFile(.path, , .perm)
	if  != 0 {
		return 
	}
	 = .checkSameFile()
	if  != 0 {
		return 
	}

	if ! {
		_,  = .Seek(, io.SeekStart)
		if  != nil {
			_ = .Close()
			return experimentalsys.UnwrapOSError()
		}
	}

	// Only update f on success.
	_ = .file.Close()
	.file = 
	.fd = .Fd()
	return 0
}

func ( *osFile) ( *os.File) experimentalsys.Errno {
	,  := .file.Stat()
	if  != nil {
		return experimentalsys.UnwrapOSError()
	}
	,  := .Stat()
	if  != nil {
		return experimentalsys.UnwrapOSError()
	}
	if os.SameFile(, ) {
		return 0
	}
	return experimentalsys.ENOENT
}

// IsNonblock implements the same method as documented on fsapi.File
func ( *osFile) () bool {
	return isNonblock()
}

// SetNonblock implements the same method as documented on fsapi.File
func ( *osFile) ( bool) ( experimentalsys.Errno) {
	if  {
		.flag |= experimentalsys.O_NONBLOCK
	} else {
		.flag &= ^experimentalsys.O_NONBLOCK
	}
	if  = setNonblock(.fd, );  != 0 {
		return fileError(, .closed, )
	}
	return 0
}

// Stat implements the same method as documented on sys.File
func ( *osFile) () (sys.Stat_t, experimentalsys.Errno) {
	if .closed {
		return sys.Stat_t{}, experimentalsys.EBADF
	}

	,  := statFile(.file)
	switch  {
	case 0:
		.cachedSt = &cachedStat{dev: .Dev, ino: .Ino, isDir: .Mode&fs.ModeDir == fs.ModeDir}
	case experimentalsys.EIO:
		 = experimentalsys.EBADF
	}
	return , 
}

// Read implements the same method as documented on sys.File
func ( *osFile) ( []byte) ( int,  experimentalsys.Errno) {
	if len() == 0 {
		return 0, 0 // Short-circuit 0-len reads.
	}
	if nonBlockingFileReadSupported && .IsNonblock() {
		,  = readFd(.fd, )
	} else {
		,  = read(.file, )
	}
	if  != 0 {
		// Defer validation overhead until we've already had an error.
		 = fileError(, .closed, )
	}
	return
}

// Pread implements the same method as documented on sys.File
func ( *osFile) ( []byte,  int64) ( int,  experimentalsys.Errno) {
	if ,  = pread(.file, , );  != 0 {
		// Defer validation overhead until we've already had an error.
		 = fileError(, .closed, )
	}
	return
}

// Seek implements the same method as documented on sys.File
func ( *osFile) ( int64,  int) ( int64,  experimentalsys.Errno) {
	if ,  = seek(.file, , );  != 0 {
		// Defer validation overhead until we've already had an error.
		 = fileError(, .closed, )

		// If the error was trying to rewind a directory, re-open it. Notably,
		// seeking to zero on a directory doesn't work on Windows with Go 1.19.
		if  == experimentalsys.EISDIR &&  == 0 &&  == io.SeekStart {
			 = 0
			.reopenDir = true
		}
	}
	return
}

// Poll implements the same method as documented on fsapi.File
func ( *osFile) ( fsapi.Pflag,  int32) ( bool,  experimentalsys.Errno) {
	return poll(.fd, , )
}

// Readdir implements File.Readdir. Notably, this uses "Readdir", not
// "ReadDir", from os.File.
func ( *osFile) ( int) ( []experimentalsys.Dirent,  experimentalsys.Errno) {
	if .reopenDir { // re-open the directory if needed.
		.reopenDir = false
		if  = adjustReaddirErr(, .closed, .reopen());  != 0 {
			return
		}
	}

	if ,  = readdir(.file, .path, );  != 0 {
		 = adjustReaddirErr(, .closed, )
	}
	return
}

// Write implements the same method as documented on sys.File
func ( *osFile) ( []byte) ( int,  experimentalsys.Errno) {
	if len() == 0 {
		return 0, 0 // Short-circuit 0-len writes.
	}
	if nonBlockingFileWriteSupported && .IsNonblock() {
		,  = writeFd(.fd, )
	} else if ,  = write(.file, );  != 0 {
		// Defer validation overhead until we've already had an error.
		 = fileError(, .closed, )
	}
	return
}

// Pwrite implements the same method as documented on sys.File
func ( *osFile) ( []byte,  int64) ( int,  experimentalsys.Errno) {
	if ,  = pwrite(.file, , );  != 0 {
		// Defer validation overhead until we've already had an error.
		 = fileError(, .closed, )
	}
	return
}

// Truncate implements the same method as documented on sys.File
func ( *osFile) ( int64) ( experimentalsys.Errno) {
	if  < 0 {
		return experimentalsys.EINVAL
	}
	if  = experimentalsys.UnwrapOSError(.file.Truncate());  != 0 {
		// Defer validation overhead until we've already had an error.
		 = fileError(, .closed, )
	}
	return
}

// Sync implements the same method as documented on sys.File
func ( *osFile) () experimentalsys.Errno {
	return fsync(.file)
}

// Datasync implements the same method as documented on sys.File
func ( *osFile) () experimentalsys.Errno {
	return datasync(.file)
}

// Utimens implements the same method as documented on sys.File
func ( *osFile) (,  int64) experimentalsys.Errno {
	if .closed {
		return experimentalsys.EBADF
	}

	 := futimens(.fd, , )
	return experimentalsys.UnwrapOSError()
}

// Close implements the same method as documented on sys.File
func ( *osFile) () experimentalsys.Errno {
	if .closed {
		return 0
	}
	.closed = true
	return .close()
}

func ( *osFile) () experimentalsys.Errno {
	return experimentalsys.UnwrapOSError(.file.Close())
}