1// Copyright 2011 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
5package os
6
7import (
8	"internal/bytealg"
9	"internal/poll"
10	"internal/stringslite"
11	"io"
12	"runtime"
13	"sync"
14	"sync/atomic"
15	"syscall"
16	"time"
17)
18
19// fixLongPath is a noop on non-Windows platforms.
20func fixLongPath(path string) string {
21	return path
22}
23
24// file is the real representation of *File.
25// The extra level of indirection ensures that no clients of os
26// can overwrite this data, which could cause the finalizer
27// to close the wrong file descriptor.
28type file struct {
29	fdmu       poll.FDMutex
30	fd         int
31	name       string
32	dirinfo    atomic.Pointer[dirInfo] // nil unless directory being read
33	appendMode bool                    // whether file is opened for appending
34}
35
36// Fd returns the integer Plan 9 file descriptor referencing the open file.
37// If f is closed, the file descriptor becomes invalid.
38// If f is garbage collected, a finalizer may close the file descriptor,
39// making it invalid; see [runtime.SetFinalizer] for more information on when
40// a finalizer might be run. On Unix systems this will cause the [File.SetDeadline]
41// methods to stop working.
42//
43// As an alternative, see the f.SyscallConn method.
44func (f *File) Fd() uintptr {
45	if f == nil {
46		return ^(uintptr(0))
47	}
48	return uintptr(f.fd)
49}
50
51// NewFile returns a new File with the given file descriptor and
52// name. The returned value will be nil if fd is not a valid file
53// descriptor.
54func NewFile(fd uintptr, name string) *File {
55	fdi := int(fd)
56	if fdi < 0 {
57		return nil
58	}
59	f := &File{&file{fd: fdi, name: name}}
60	runtime.SetFinalizer(f.file, (*file).close)
61	return f
62}
63
64// Auxiliary information if the File describes a directory
65type dirInfo struct {
66	mu   sync.Mutex
67	buf  [syscall.STATMAX]byte // buffer for directory I/O
68	nbuf int                   // length of buf; return value from Read
69	bufp int                   // location of next record in buf.
70}
71
72func epipecheck(file *File, e error) {
73}
74
75// DevNull is the name of the operating system's “null device.”
76// On Unix-like systems, it is "/dev/null"; on Windows, "NUL".
77const DevNull = "/dev/null"
78
79// syscallMode returns the syscall-specific mode bits from Go's portable mode bits.
80func syscallMode(i FileMode) (o uint32) {
81	o |= uint32(i.Perm())
82	if i&ModeAppend != 0 {
83		o |= syscall.DMAPPEND
84	}
85	if i&ModeExclusive != 0 {
86		o |= syscall.DMEXCL
87	}
88	if i&ModeTemporary != 0 {
89		o |= syscall.DMTMP
90	}
91	return
92}
93
94// openFileNolog is the Plan 9 implementation of OpenFile.
95func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
96	var (
97		fd     int
98		e      error
99		create bool
100		excl   bool
101		trunc  bool
102		append bool
103	)
104
105	if flag&O_CREATE == O_CREATE {
106		flag = flag & ^O_CREATE
107		create = true
108	}
109	if flag&O_EXCL == O_EXCL {
110		excl = true
111	}
112	if flag&O_TRUNC == O_TRUNC {
113		trunc = true
114	}
115	// O_APPEND is emulated on Plan 9
116	if flag&O_APPEND == O_APPEND {
117		flag = flag &^ O_APPEND
118		append = true
119	}
120
121	if (create && trunc) || excl {
122		fd, e = syscall.Create(name, flag, syscallMode(perm))
123	} else {
124		fd, e = syscall.Open(name, flag)
125		if IsNotExist(e) && create {
126			fd, e = syscall.Create(name, flag, syscallMode(perm))
127			if e != nil {
128				return nil, &PathError{Op: "create", Path: name, Err: e}
129			}
130		}
131	}
132
133	if e != nil {
134		return nil, &PathError{Op: "open", Path: name, Err: e}
135	}
136
137	if append {
138		if _, e = syscall.Seek(fd, 0, io.SeekEnd); e != nil {
139			return nil, &PathError{Op: "seek", Path: name, Err: e}
140		}
141	}
142
143	return NewFile(uintptr(fd), name), nil
144}
145
146func openDirNolog(name string) (*File, error) {
147	return openFileNolog(name, O_RDONLY, 0)
148}
149
150// Close closes the File, rendering it unusable for I/O.
151// On files that support SetDeadline, any pending I/O operations will
152// be canceled and return immediately with an ErrClosed error.
153// Close will return an error if it has already been called.
154func (f *File) Close() error {
155	if f == nil {
156		return ErrInvalid
157	}
158	return f.file.close()
159}
160
161func (file *file) close() error {
162	if !file.fdmu.IncrefAndClose() {
163		return &PathError{Op: "close", Path: file.name, Err: ErrClosed}
164	}
165
166	// At this point we should cancel any pending I/O.
167	// How do we do that on Plan 9?
168
169	err := file.decref()
170
171	// no need for a finalizer anymore
172	runtime.SetFinalizer(file, nil)
173	return err
174}
175
176// destroy actually closes the descriptor. This is called when
177// there are no remaining references, by the decref, readUnlock,
178// and writeUnlock methods.
179func (file *file) destroy() error {
180	var err error
181	if e := syscall.Close(file.fd); e != nil {
182		err = &PathError{Op: "close", Path: file.name, Err: e}
183	}
184	return err
185}
186
187// Stat returns the FileInfo structure describing file.
188// If there is an error, it will be of type *PathError.
189func (f *File) Stat() (FileInfo, error) {
190	if f == nil {
191		return nil, ErrInvalid
192	}
193	d, err := dirstat(f)
194	if err != nil {
195		return nil, err
196	}
197	return fileInfoFromStat(d), nil
198}
199
200// Truncate changes the size of the file.
201// It does not change the I/O offset.
202// If there is an error, it will be of type *PathError.
203func (f *File) Truncate(size int64) error {
204	if f == nil {
205		return ErrInvalid
206	}
207
208	var d syscall.Dir
209	d.Null()
210	d.Length = size
211
212	var buf [syscall.STATFIXLEN]byte
213	n, err := d.Marshal(buf[:])
214	if err != nil {
215		return &PathError{Op: "truncate", Path: f.name, Err: err}
216	}
217
218	if err := f.incref("truncate"); err != nil {
219		return err
220	}
221	defer f.decref()
222
223	if err = syscall.Fwstat(f.fd, buf[:n]); err != nil {
224		return &PathError{Op: "truncate", Path: f.name, Err: err}
225	}
226	return nil
227}
228
229const chmodMask = uint32(syscall.DMAPPEND | syscall.DMEXCL | syscall.DMTMP | ModePerm)
230
231func (f *File) chmod(mode FileMode) error {
232	if f == nil {
233		return ErrInvalid
234	}
235	var d syscall.Dir
236
237	odir, e := dirstat(f)
238	if e != nil {
239		return &PathError{Op: "chmod", Path: f.name, Err: e}
240	}
241	d.Null()
242	d.Mode = odir.Mode&^chmodMask | syscallMode(mode)&chmodMask
243
244	var buf [syscall.STATFIXLEN]byte
245	n, err := d.Marshal(buf[:])
246	if err != nil {
247		return &PathError{Op: "chmod", Path: f.name, Err: err}
248	}
249
250	if err := f.incref("chmod"); err != nil {
251		return err
252	}
253	defer f.decref()
254
255	if err = syscall.Fwstat(f.fd, buf[:n]); err != nil {
256		return &PathError{Op: "chmod", Path: f.name, Err: err}
257	}
258	return nil
259}
260
261// Sync commits the current contents of the file to stable storage.
262// Typically, this means flushing the file system's in-memory copy
263// of recently written data to disk.
264func (f *File) Sync() error {
265	if f == nil {
266		return ErrInvalid
267	}
268	var d syscall.Dir
269	d.Null()
270
271	var buf [syscall.STATFIXLEN]byte
272	n, err := d.Marshal(buf[:])
273	if err != nil {
274		return &PathError{Op: "sync", Path: f.name, Err: err}
275	}
276
277	if err := f.incref("sync"); err != nil {
278		return err
279	}
280	defer f.decref()
281
282	if err = syscall.Fwstat(f.fd, buf[:n]); err != nil {
283		return &PathError{Op: "sync", Path: f.name, Err: err}
284	}
285	return nil
286}
287
288// read reads up to len(b) bytes from the File.
289// It returns the number of bytes read and an error, if any.
290func (f *File) read(b []byte) (n int, err error) {
291	if err := f.readLock(); err != nil {
292		return 0, err
293	}
294	defer f.readUnlock()
295	n, e := fixCount(syscall.Read(f.fd, b))
296	if n == 0 && len(b) > 0 && e == nil {
297		return 0, io.EOF
298	}
299	return n, e
300}
301
302// pread reads len(b) bytes from the File starting at byte offset off.
303// It returns the number of bytes read and the error, if any.
304// EOF is signaled by a zero count with err set to nil.
305func (f *File) pread(b []byte, off int64) (n int, err error) {
306	if err := f.readLock(); err != nil {
307		return 0, err
308	}
309	defer f.readUnlock()
310	n, e := fixCount(syscall.Pread(f.fd, b, off))
311	if n == 0 && len(b) > 0 && e == nil {
312		return 0, io.EOF
313	}
314	return n, e
315}
316
317// write writes len(b) bytes to the File.
318// It returns the number of bytes written and an error, if any.
319// Since Plan 9 preserves message boundaries, never allow
320// a zero-byte write.
321func (f *File) write(b []byte) (n int, err error) {
322	if err := f.writeLock(); err != nil {
323		return 0, err
324	}
325	defer f.writeUnlock()
326	if len(b) == 0 {
327		return 0, nil
328	}
329	return fixCount(syscall.Write(f.fd, b))
330}
331
332// pwrite writes len(b) bytes to the File starting at byte offset off.
333// It returns the number of bytes written and an error, if any.
334// Since Plan 9 preserves message boundaries, never allow
335// a zero-byte write.
336func (f *File) pwrite(b []byte, off int64) (n int, err error) {
337	if err := f.writeLock(); err != nil {
338		return 0, err
339	}
340	defer f.writeUnlock()
341	if len(b) == 0 {
342		return 0, nil
343	}
344	return fixCount(syscall.Pwrite(f.fd, b, off))
345}
346
347// seek sets the offset for the next Read or Write on file to offset, interpreted
348// according to whence: 0 means relative to the origin of the file, 1 means
349// relative to the current offset, and 2 means relative to the end.
350// It returns the new offset and an error, if any.
351func (f *File) seek(offset int64, whence int) (ret int64, err error) {
352	if err := f.incref(""); err != nil {
353		return 0, err
354	}
355	defer f.decref()
356	// Free cached dirinfo, so we allocate a new one if we
357	// access this file as a directory again. See #35767 and #37161.
358	f.dirinfo.Store(nil)
359	return syscall.Seek(f.fd, offset, whence)
360}
361
362// Truncate changes the size of the named file.
363// If the file is a symbolic link, it changes the size of the link's target.
364// If there is an error, it will be of type *PathError.
365func Truncate(name string, size int64) error {
366	var d syscall.Dir
367
368	d.Null()
369	d.Length = size
370
371	var buf [syscall.STATFIXLEN]byte
372	n, err := d.Marshal(buf[:])
373	if err != nil {
374		return &PathError{Op: "truncate", Path: name, Err: err}
375	}
376	if err = syscall.Wstat(name, buf[:n]); err != nil {
377		return &PathError{Op: "truncate", Path: name, Err: err}
378	}
379	return nil
380}
381
382// Remove removes the named file or directory.
383// If there is an error, it will be of type *PathError.
384func Remove(name string) error {
385	if e := syscall.Remove(name); e != nil {
386		return &PathError{Op: "remove", Path: name, Err: e}
387	}
388	return nil
389}
390
391func rename(oldname, newname string) error {
392	dirname := oldname[:bytealg.LastIndexByteString(oldname, '/')+1]
393	if stringslite.HasPrefix(newname, dirname) {
394		newname = newname[len(dirname):]
395	} else {
396		return &LinkError{"rename", oldname, newname, ErrInvalid}
397	}
398
399	// If newname still contains slashes after removing the oldname
400	// prefix, the rename is cross-directory and must be rejected.
401	if bytealg.LastIndexByteString(newname, '/') >= 0 {
402		return &LinkError{"rename", oldname, newname, ErrInvalid}
403	}
404
405	var d syscall.Dir
406
407	d.Null()
408	d.Name = newname
409
410	buf := make([]byte, syscall.STATFIXLEN+len(d.Name))
411	n, err := d.Marshal(buf[:])
412	if err != nil {
413		return &LinkError{"rename", oldname, newname, err}
414	}
415
416	// If newname already exists and is not a directory, rename replaces it.
417	f, err := Stat(dirname + newname)
418	if err == nil && !f.IsDir() {
419		Remove(dirname + newname)
420	}
421
422	if err = syscall.Wstat(oldname, buf[:n]); err != nil {
423		return &LinkError{"rename", oldname, newname, err}
424	}
425	return nil
426}
427
428// See docs in file.go:Chmod.
429func chmod(name string, mode FileMode) error {
430	var d syscall.Dir
431
432	odir, e := dirstat(name)
433	if e != nil {
434		return &PathError{Op: "chmod", Path: name, Err: e}
435	}
436	d.Null()
437	d.Mode = odir.Mode&^chmodMask | syscallMode(mode)&chmodMask
438
439	var buf [syscall.STATFIXLEN]byte
440	n, err := d.Marshal(buf[:])
441	if err != nil {
442		return &PathError{Op: "chmod", Path: name, Err: err}
443	}
444	if err = syscall.Wstat(name, buf[:n]); err != nil {
445		return &PathError{Op: "chmod", Path: name, Err: err}
446	}
447	return nil
448}
449
450// Chtimes changes the access and modification times of the named
451// file, similar to the Unix utime() or utimes() functions.
452// A zero time.Time value will leave the corresponding file time unchanged.
453//
454// The underlying filesystem may truncate or round the values to a
455// less precise time unit.
456// If there is an error, it will be of type *PathError.
457func Chtimes(name string, atime time.Time, mtime time.Time) error {
458	var d syscall.Dir
459
460	d.Null()
461	d.Atime = uint32(atime.Unix())
462	d.Mtime = uint32(mtime.Unix())
463	if atime.IsZero() {
464		d.Atime = 0xFFFFFFFF
465	}
466	if mtime.IsZero() {
467		d.Mtime = 0xFFFFFFFF
468	}
469
470	var buf [syscall.STATFIXLEN]byte
471	n, err := d.Marshal(buf[:])
472	if err != nil {
473		return &PathError{Op: "chtimes", Path: name, Err: err}
474	}
475	if err = syscall.Wstat(name, buf[:n]); err != nil {
476		return &PathError{Op: "chtimes", Path: name, Err: err}
477	}
478	return nil
479}
480
481// Pipe returns a connected pair of Files; reads from r return bytes
482// written to w. It returns the files and an error, if any.
483func Pipe() (r *File, w *File, err error) {
484	var p [2]int
485
486	if e := syscall.Pipe(p[0:]); e != nil {
487		return nil, nil, NewSyscallError("pipe", e)
488	}
489
490	return NewFile(uintptr(p[0]), "|0"), NewFile(uintptr(p[1]), "|1"), nil
491}
492
493// not supported on Plan 9
494
495// Link creates newname as a hard link to the oldname file.
496// If there is an error, it will be of type *LinkError.
497func Link(oldname, newname string) error {
498	return &LinkError{"link", oldname, newname, syscall.EPLAN9}
499}
500
501// Symlink creates newname as a symbolic link to oldname.
502// On Windows, a symlink to a non-existent oldname creates a file symlink;
503// if oldname is later created as a directory the symlink will not work.
504// If there is an error, it will be of type *LinkError.
505func Symlink(oldname, newname string) error {
506	return &LinkError{"symlink", oldname, newname, syscall.EPLAN9}
507}
508
509func readlink(name string) (string, error) {
510	return "", &PathError{Op: "readlink", Path: name, Err: syscall.EPLAN9}
511}
512
513// Chown changes the numeric uid and gid of the named file.
514// If the file is a symbolic link, it changes the uid and gid of the link's target.
515// A uid or gid of -1 means to not change that value.
516// If there is an error, it will be of type *PathError.
517//
518// On Windows or Plan 9, Chown always returns the syscall.EWINDOWS or
519// EPLAN9 error, wrapped in *PathError.
520func Chown(name string, uid, gid int) error {
521	return &PathError{Op: "chown", Path: name, Err: syscall.EPLAN9}
522}
523
524// Lchown changes the numeric uid and gid of the named file.
525// If the file is a symbolic link, it changes the uid and gid of the link itself.
526// If there is an error, it will be of type *PathError.
527func Lchown(name string, uid, gid int) error {
528	return &PathError{Op: "lchown", Path: name, Err: syscall.EPLAN9}
529}
530
531// Chown changes the numeric uid and gid of the named file.
532// If there is an error, it will be of type *PathError.
533func (f *File) Chown(uid, gid int) error {
534	if f == nil {
535		return ErrInvalid
536	}
537	return &PathError{Op: "chown", Path: f.name, Err: syscall.EPLAN9}
538}
539
540func tempDir() string {
541	dir := Getenv("TMPDIR")
542	if dir == "" {
543		dir = "/tmp"
544	}
545	return dir
546}
547
548// Chdir changes the current working directory to the file,
549// which must be a directory.
550// If there is an error, it will be of type *PathError.
551func (f *File) Chdir() error {
552	if err := f.incref("chdir"); err != nil {
553		return err
554	}
555	defer f.decref()
556	if e := syscall.Fchdir(f.fd); e != nil {
557		return &PathError{Op: "chdir", Path: f.name, Err: e}
558	}
559	return nil
560}
561
562// setDeadline sets the read and write deadline.
563func (f *File) setDeadline(time.Time) error {
564	if err := f.checkValid("SetDeadline"); err != nil {
565		return err
566	}
567	return poll.ErrNoDeadline
568}
569
570// setReadDeadline sets the read deadline.
571func (f *File) setReadDeadline(time.Time) error {
572	if err := f.checkValid("SetReadDeadline"); err != nil {
573		return err
574	}
575	return poll.ErrNoDeadline
576}
577
578// setWriteDeadline sets the write deadline.
579func (f *File) setWriteDeadline(time.Time) error {
580	if err := f.checkValid("SetWriteDeadline"); err != nil {
581		return err
582	}
583	return poll.ErrNoDeadline
584}
585
586// checkValid checks whether f is valid for use, but does not prepare
587// to actually use it. If f is not ready checkValid returns an appropriate
588// error, perhaps incorporating the operation name op.
589func (f *File) checkValid(op string) error {
590	if f == nil {
591		return ErrInvalid
592	}
593	if err := f.incref(op); err != nil {
594		return err
595	}
596	return f.decref()
597}
598
599type rawConn struct{}
600
601func (c *rawConn) Control(f func(uintptr)) error {
602	return syscall.EPLAN9
603}
604
605func (c *rawConn) Read(f func(uintptr) bool) error {
606	return syscall.EPLAN9
607}
608
609func (c *rawConn) Write(f func(uintptr) bool) error {
610	return syscall.EPLAN9
611}
612
613func newRawConn(file *File) (*rawConn, error) {
614	return nil, syscall.EPLAN9
615}
616
617func ignoringEINTR(fn func() error) error {
618	return fn()
619}
620