ggstd/os/
file_unix.rs

1// Copyright 2023 The rust-ggstd authors.
2// Copyright 2009 The Go Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style
4// license that can be found in the LICENSE file.
5
6// //go:build unix || (js && wasm)
7
8// package os
9
10// import (
11// 	"internal/poll"
12// 	"internal/syscall/unix"
13// 	"runtime"
14// 	"syscall"
15// 	_ "unsafe" // for go:linkname
16// )
17
18/// fix_long_path is a noop on non-Windows platforms.
19pub fn fix_long_path(path: &str) -> String {
20    path.to_string()
21}
22
23// fn rename(oldname, newname string) error {
24// 	fi, err := Lstat(newname)
25// 	if err == nil && fi.IsDir() {
26// 		// There are two independent errors this function can return:
27// 		// one for a bad oldname, and one for a bad newname.
28// 		// At this point we've determined the newname is bad.
29// 		// But just in case oldname is also bad, prioritize returning
30// 		// the oldname error because that's what we did historically.
31// 		// However, if the old name and new name are not the same, yet
32// 		// they refer to the same file, it implies a case-only
33// 		// rename on a case-insensitive filesystem, which is ok.
34// 		if ofi, err := Lstat(oldname); err != nil {
35// 			if pe, ok := err.(*PathError); ok {
36// 				err = pe.Err
37// 			}
38// 			return &LinkError{"rename", oldname, newname, err}
39// 		} else if newname == oldname || !SameFile(fi, ofi) {
40// 			return &LinkError{"rename", oldname, newname, syscall.EEXIST}
41// 		}
42// 	}
43// 	err = ignoringEINTR(fn() error {
44// 		return syscall.Rename(oldname, newname)
45// 	})
46// 	if err != nil {
47// 		return &LinkError{"rename", oldname, newname, err}
48// 	}
49// 	return nil
50// }
51
52// // file is the real representation of *File.
53// // The extra level of indirection ensures that no clients of os
54// // can overwrite this data, which could cause the finalizer
55// // to close the wrong file descriptor.
56// type file struct {
57// 	pfd         poll.FD
58// 	name        string
59// 	dirinfo     *dirInfo // nil unless directory being read
60// 	nonblock    bool     // whether we set nonblocking mode
61// 	stdoutOrErr bool     // whether this is stdout or stderr
62// 	appendMode  bool     // whether file is opened for appending
63// }
64
65// // Fd returns the integer Unix file descriptor referencing the open file.
66// // If f is closed, the file descriptor becomes invalid.
67// // If f is garbage collected, a finalizer may close the file descriptor,
68// // making it invalid; see runtime.SetFinalizer for more information on when
69// // a finalizer might be run. On Unix systems this will cause the SetDeadline
70// // methods to stop working.
71// // Because file descriptors can be reused, the returned file descriptor may
72// // only be closed through the Close method of f, or by its finalizer during
73// // garbage collection. Otherwise, during garbage collection the finalizer
74// // may close an unrelated file descriptor with the same (reused) number.
75// //
76// // As an alternative, see the f.SyscallConn method.
77// fn (f *File) Fd() uintptr {
78// 	if f == nil {
79// 		return ^(uintptr(0))
80// 	}
81
82// 	// If we put the file descriptor into nonblocking mode,
83// 	// then set it to blocking mode before we return it,
84// 	// because historically we have always returned a descriptor
85// 	// opened in blocking mode. The File will continue to work,
86// 	// but any blocking operation will tie up a thread.
87// 	if f.nonblock {
88// 		f.pfd.SetBlocking()
89// 	}
90
91// 	return uintptr(f.pfd.Sysfd)
92// }
93
94// // NewFile returns a new File with the given file descriptor and
95// // name. The returned value will be nil if fd is not a valid file
96// // descriptor. On Unix systems, if the file descriptor is in
97// // non-blocking mode, NewFile will attempt to return a pollable File
98// // (one for which the SetDeadline methods work).
99// //
100// // After passing it to NewFile, fd may become invalid under the same
101// // conditions described in the comments of the Fd method, and the same
102// // constraints apply.
103// fn NewFile(fd uintptr, name string) *File {
104// 	kind := kindNewFile
105// 	if nb, err := unix.IsNonblock(int(fd)); err == nil && nb {
106// 		kind = kindNonBlock
107// 	}
108// 	return newFile(fd, name, kind)
109// }
110
111// // net_newUnixFile is a hidden entry point called by net.conn.File.
112// // This is used so that a nonblocking network connection will become
113// // blocking if code calls the Fd method. We don't want that for direct
114// // calls to NewFile: passing a nonblocking descriptor to NewFile should
115// // remain nonblocking if you get it back using Fd. But for net.conn.File
116// // the call to NewFile is hidden from the user. Historically in that case
117// // the Fd method has returned a blocking descriptor, and we want to
118// // retain that behavior because existing code expects it and depends on it.
119// //
120// //go:linkname net_newUnixFile net.newUnixFile
121// fn net_newUnixFile(fd uintptr, name string) *File {
122// 	f := newFile(fd, name, kindNonBlock)
123// 	f.nonblock = true // tell Fd to return blocking descriptor
124// 	return f
125// }
126
127// // newFileKind describes the kind of file to newFile.
128// type newFileKind int
129
130// const (
131// 	kindNewFile newFileKind = iota
132// 	kindOpenFile
133// 	kindPipe
134// 	kindNonBlock
135// )
136
137// // newFile is like NewFile, but if called from OpenFile or Pipe
138// // (as passed in the kind parameter) it tries to add the file to
139// // the runtime poller.
140// fn newFile(fd uintptr, name string, kind newFileKind) *File {
141// 	fdi := int(fd)
142// 	if fdi < 0 {
143// 		return nil
144// 	}
145// 	f := &File{&file{
146// 		pfd: poll.FD{
147// 			Sysfd:         fdi,
148// 			IsStream:      true,
149// 			ZeroReadIsEOF: true,
150// 		},
151// 		name:        name,
152// 		stdoutOrErr: fdi == 1 || fdi == 2,
153// 	}}
154
155// 	pollable := kind == kindOpenFile || kind == kindPipe || kind == kindNonBlock
156
157// 	// If the caller passed a non-blocking filedes (kindNonBlock),
158// 	// we assume they know what they are doing so we allow it to be
159// 	// used with kqueue.
160// 	if kind == kindOpenFile {
161// 		switch runtime.GOOS {
162// 		case "darwin", "ios", "dragonfly", "freebsd", "netbsd", "openbsd":
163// 			var st syscall.Stat_t
164// 			err := ignoringEINTR(fn() error {
165// 				return syscall.Fstat(fdi, &st)
166// 			})
167// 			typ := st.Mode & syscall.S_IFMT
168// 			// Don't try to use kqueue with regular files on *BSDs.
169// 			// On FreeBSD a regular file is always
170// 			// reported as ready for writing.
171// 			// On Dragonfly, NetBSD and OpenBSD the fd is signaled
172// 			// only once as ready (both read and write).
173// 			// Issue 19093.
174// 			// Also don't add directories to the netpoller.
175// 			if err == nil && (typ == syscall.S_IFREG || typ == syscall.S_IFDIR) {
176// 				pollable = false
177// 			}
178
179// 			// In addition to the behavior described above for regular files,
180// 			// on Darwin, kqueue does not work properly with fifos:
181// 			// closing the last writer does not cause a kqueue event
182// 			// for any readers. See issue #24164.
183// 			if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && typ == syscall.S_IFIFO {
184// 				pollable = false
185// 			}
186// 		}
187// 	}
188
189// 	clearNonBlock := false
190// 	if pollable {
191// 		if kind == kindNonBlock {
192// 			// The descriptor is already in non-blocking mode.
193// 			// We only set f.nonblock if we put the file into
194// 			// non-blocking mode.
195// 		} else if err := syscall.SetNonblock(fdi, true); err == nil {
196// 			f.nonblock = true
197// 			clearNonBlock = true
198// 		} else {
199// 			pollable = false
200// 		}
201// 	}
202
203// 	// An error here indicates a failure to register
204// 	// with the netpoll system. That can happen for
205// 	// a file descriptor that is not supported by
206// 	// epoll/kqueue; for example, disk files on
207// 	// Linux systems. We assume that any real error
208// 	// will show up in later I/O.
209// 	// We do restore the blocking behavior if it was set by us.
210// 	if pollErr := f.pfd.Init("file", pollable); pollErr != nil && clearNonBlock {
211// 		if err := syscall.SetNonblock(fdi, false); err == nil {
212// 			f.nonblock = false
213// 		}
214// 	}
215
216// 	runtime.SetFinalizer(f.file, (*file).close)
217// 	return f
218// }
219
220// // epipecheck raises SIGPIPE if we get an EPIPE error on standard
221// // output or standard error. See the SIGPIPE docs in os/signal, and
222// // issue 11845.
223// fn epipecheck(file *File, e error) {
224// 	if e == syscall.EPIPE && file.stdoutOrErr {
225// 		sigpipe()
226// 	}
227// }
228
229// // DevNull is the name of the operating system's “null device.”
230// // On Unix-like systems, it is "/dev/null"; on Windows, "NUL".
231// const DevNull = "/dev/null"
232
233// // openFileNolog is the Unix implementation of OpenFile.
234// // Changes here should be reflected in openFdAt, if relevant.
235// fn openFileNolog(name string, flag int, perm FileMode) (*File, error) {
236// 	setSticky := false
237// 	if !supportsCreateWithStickyBit && flag&O_CREATE != 0 && perm&ModeSticky != 0 {
238// 		if _, err := Stat(name); IsNotExist(err) {
239// 			setSticky = true
240// 		}
241// 	}
242
243// 	var r int
244// 	for {
245// 		var e error
246// 		r, e = syscall.Open(name, flag|syscall.O_CLOEXEC, syscallMode(perm))
247// 		if e == nil {
248// 			break
249// 		}
250
251// 		// We have to check EINTR here, per issues 11180 and 39237.
252// 		if e == syscall.EINTR {
253// 			continue
254// 		}
255
256// 		return nil, &PathError{Op: "open", Path: name, Err: e}
257// 	}
258
259// 	// open(2) itself won't handle the sticky bit on *BSD and Solaris
260// 	if setSticky {
261// 		setStickyBit(name)
262// 	}
263
264// 	// There's a race here with fork/exec, which we are
265// 	// content to live with. See ../syscall/exec_unix.go.
266// 	if !supportsCloseOnExec {
267// 		syscall.CloseOnExec(r)
268// 	}
269
270// 	kind := kindOpenFile
271// 	if unix.HasNonblockFlag(flag) {
272// 		kind = kindNonBlock
273// 	}
274
275// 	return newFile(uintptr(r), name, kind), nil
276// }
277
278// fn (file *file) close() error {
279// 	if file == nil {
280// 		return syscall.EINVAL
281// 	}
282// 	if file.dirinfo != nil {
283// 		file.dirinfo.close()
284// 		file.dirinfo = nil
285// 	}
286// 	var err error
287// 	if e := file.pfd.Close(); e != nil {
288// 		if e == poll.ErrFileClosing {
289// 			e = ErrClosed
290// 		}
291// 		err = &PathError{Op: "close", Path: file.name, Err: e}
292// 	}
293
294// 	// no need for a finalizer anymore
295// 	runtime.SetFinalizer(file, nil)
296// 	return err
297// }
298
299// // seek sets the offset for the next Read or Write on file to offset, interpreted
300// // according to whence: 0 means relative to the origin of the file, 1 means
301// // relative to the current offset, and 2 means relative to the end.
302// // It returns the new offset and an error, if any.
303// fn (f *File) seek(offset int64, whence int) (ret int64, err error) {
304// 	if f.dirinfo != nil {
305// 		// Free cached dirinfo, so we allocate a new one if we
306// 		// access this file as a directory again. See #35767 and #37161.
307// 		f.dirinfo.close()
308// 		f.dirinfo = nil
309// 	}
310// 	ret, err = f.pfd.Seek(offset, whence)
311// 	runtime.KeepAlive(f)
312// 	return ret, err
313// }
314
315// // Truncate changes the size of the named file.
316// // If the file is a symbolic link, it changes the size of the link's target.
317// // If there is an error, it will be of type *PathError.
318// fn Truncate(name string, size int64) error {
319// 	e := ignoringEINTR(fn() error {
320// 		return syscall.Truncate(name, size)
321// 	})
322// 	if e != nil {
323// 		return &PathError{Op: "truncate", Path: name, Err: e}
324// 	}
325// 	return nil
326// }
327
328// // Remove removes the named file or (empty) directory.
329// // If there is an error, it will be of type *PathError.
330// fn Remove(name string) error {
331// 	// System call interface forces us to know
332// 	// whether name is a file or directory.
333// 	// Try both: it is cheaper on average than
334// 	// doing a Stat plus the right one.
335// 	e := ignoringEINTR(fn() error {
336// 		return syscall.Unlink(name)
337// 	})
338// 	if e == nil {
339// 		return nil
340// 	}
341// 	e1 := ignoringEINTR(fn() error {
342// 		return syscall.Rmdir(name)
343// 	})
344// 	if e1 == nil {
345// 		return nil
346// 	}
347
348// 	// Both failed: figure out which error to return.
349// 	// OS X and Linux differ on whether unlink(dir)
350// 	// returns EISDIR, so can't use that. However,
351// 	// both agree that rmdir(file) returns ENOTDIR,
352// 	// so we can use that to decide which error is real.
353// 	// Rmdir might also return ENOTDIR if given a bad
354// 	// file path, like /etc/passwd/foo, but in that case,
355// 	// both errors will be ENOTDIR, so it's okay to
356// 	// use the error from unlink.
357// 	if e1 != syscall.ENOTDIR {
358// 		e = e1
359// 	}
360// 	return &PathError{Op: "remove", Path: name, Err: e}
361// }
362
363// fn tempDir() string {
364// 	dir := Getenv("TMPDIR")
365// 	if dir == "" {
366// 		if runtime.GOOS == "android" {
367// 			dir = "/data/local/tmp"
368// 		} else {
369// 			dir = "/tmp"
370// 		}
371// 	}
372// 	return dir
373// }
374
375// // Link creates newname as a hard link to the oldname file.
376// // If there is an error, it will be of type *LinkError.
377// fn Link(oldname, newname string) error {
378// 	e := ignoringEINTR(fn() error {
379// 		return syscall.Link(oldname, newname)
380// 	})
381// 	if e != nil {
382// 		return &LinkError{"link", oldname, newname, e}
383// 	}
384// 	return nil
385// }
386
387// // Symlink creates newname as a symbolic link to oldname.
388// // On Windows, a symlink to a non-existent oldname creates a file symlink;
389// // if oldname is later created as a directory the symlink will not work.
390// // If there is an error, it will be of type *LinkError.
391// fn Symlink(oldname, newname string) error {
392// 	e := ignoringEINTR(fn() error {
393// 		return syscall.Symlink(oldname, newname)
394// 	})
395// 	if e != nil {
396// 		return &LinkError{"symlink", oldname, newname, e}
397// 	}
398// 	return nil
399// }
400
401// // Readlink returns the destination of the named symbolic link.
402// // If there is an error, it will be of type *PathError.
403// fn Readlink(name string) (string, error) {
404// 	for len := 128; ; len *= 2 {
405// 		b := make([]byte, len)
406// 		var (
407// 			n int
408// 			e error
409// 		)
410// 		for {
411// 			n, e = fixCount(syscall.Readlink(name, b))
412// 			if e != syscall.EINTR {
413// 				break
414// 			}
415// 		}
416// 		// buffer too small
417// 		if runtime.GOOS == "aix" && e == syscall.ERANGE {
418// 			continue
419// 		}
420// 		if e != nil {
421// 			return "", &PathError{Op: "readlink", Path: name, Err: e}
422// 		}
423// 		if n < len {
424// 			return string(b[0:n]), nil
425// 		}
426// 	}
427// }
428
429// type unixDirent struct {
430// 	parent string
431// 	name   string
432// 	typ    FileMode
433// 	info   FileInfo
434// }
435
436// fn (d *unixDirent) Name() string   { return d.name }
437// fn (d *unixDirent) IsDir() bool    { return d.typ.IsDir() }
438// fn (d *unixDirent) Type() FileMode { return d.typ }
439
440// fn (d *unixDirent) Info() (FileInfo, error) {
441// 	if d.info != nil {
442// 		return d.info, nil
443// 	}
444// 	return lstat(d.parent + "/" + d.name)
445// }
446
447// fn newUnixDirent(parent, name string, typ FileMode) (DirEntry, error) {
448// 	ude := &unixDirent{
449// 		parent: parent,
450// 		name:   name,
451// 		typ:    typ,
452// 	}
453// 	if typ != ^FileMode(0) && !testingForceReadDirLstat {
454// 		return ude, nil
455// 	}
456
457// 	info, err := lstat(parent + "/" + name)
458// 	if err != nil {
459// 		return nil, err
460// 	}
461
462// 	ude.typ = info.Mode().Type()
463// 	ude.info = info
464// 	return ude, nil
465// }