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// }