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//go:build unix || (js && wasm) || wasip1 || windows
6
7package os
8
9import (
10	"runtime"
11	"syscall"
12	"time"
13)
14
15// Close closes the [File], rendering it unusable for I/O.
16// On files that support [File.SetDeadline], any pending I/O operations will
17// be canceled and return immediately with an [ErrClosed] error.
18// Close will return an error if it has already been called.
19func (f *File) Close() error {
20	if f == nil {
21		return ErrInvalid
22	}
23	return f.file.close()
24}
25
26// read reads up to len(b) bytes from the File.
27// It returns the number of bytes read and an error, if any.
28func (f *File) read(b []byte) (n int, err error) {
29	n, err = f.pfd.Read(b)
30	runtime.KeepAlive(f)
31	return n, err
32}
33
34// pread reads len(b) bytes from the File starting at byte offset off.
35// It returns the number of bytes read and the error, if any.
36// EOF is signaled by a zero count with err set to nil.
37func (f *File) pread(b []byte, off int64) (n int, err error) {
38	n, err = f.pfd.Pread(b, off)
39	runtime.KeepAlive(f)
40	return n, err
41}
42
43// write writes len(b) bytes to the File.
44// It returns the number of bytes written and an error, if any.
45func (f *File) write(b []byte) (n int, err error) {
46	n, err = f.pfd.Write(b)
47	runtime.KeepAlive(f)
48	return n, err
49}
50
51// pwrite writes len(b) bytes to the File starting at byte offset off.
52// It returns the number of bytes written and an error, if any.
53func (f *File) pwrite(b []byte, off int64) (n int, err error) {
54	n, err = f.pfd.Pwrite(b, off)
55	runtime.KeepAlive(f)
56	return n, err
57}
58
59// syscallMode returns the syscall-specific mode bits from Go's portable mode bits.
60func syscallMode(i FileMode) (o uint32) {
61	o |= uint32(i.Perm())
62	if i&ModeSetuid != 0 {
63		o |= syscall.S_ISUID
64	}
65	if i&ModeSetgid != 0 {
66		o |= syscall.S_ISGID
67	}
68	if i&ModeSticky != 0 {
69		o |= syscall.S_ISVTX
70	}
71	// No mapping for Go's ModeTemporary (plan9 only).
72	return
73}
74
75// See docs in file.go:Chmod.
76func chmod(name string, mode FileMode) error {
77	longName := fixLongPath(name)
78	e := ignoringEINTR(func() error {
79		return syscall.Chmod(longName, syscallMode(mode))
80	})
81	if e != nil {
82		return &PathError{Op: "chmod", Path: name, Err: e}
83	}
84	return nil
85}
86
87// See docs in file.go:(*File).Chmod.
88func (f *File) chmod(mode FileMode) error {
89	if err := f.checkValid("chmod"); err != nil {
90		return err
91	}
92	if e := f.pfd.Fchmod(syscallMode(mode)); e != nil {
93		return f.wrapErr("chmod", e)
94	}
95	return nil
96}
97
98// Chown changes the numeric uid and gid of the named file.
99// If the file is a symbolic link, it changes the uid and gid of the link's target.
100// A uid or gid of -1 means to not change that value.
101// If there is an error, it will be of type [*PathError].
102//
103// On Windows or Plan 9, Chown always returns the [syscall.EWINDOWS] or
104// EPLAN9 error, wrapped in *PathError.
105func Chown(name string, uid, gid int) error {
106	e := ignoringEINTR(func() error {
107		return syscall.Chown(name, uid, gid)
108	})
109	if e != nil {
110		return &PathError{Op: "chown", Path: name, Err: e}
111	}
112	return nil
113}
114
115// Lchown changes the numeric uid and gid of the named file.
116// If the file is a symbolic link, it changes the uid and gid of the link itself.
117// If there is an error, it will be of type [*PathError].
118//
119// On Windows, it always returns the [syscall.EWINDOWS] error, wrapped
120// in *PathError.
121func Lchown(name string, uid, gid int) error {
122	e := ignoringEINTR(func() error {
123		return syscall.Lchown(name, uid, gid)
124	})
125	if e != nil {
126		return &PathError{Op: "lchown", Path: name, Err: e}
127	}
128	return nil
129}
130
131// Chown changes the numeric uid and gid of the named file.
132// If there is an error, it will be of type [*PathError].
133//
134// On Windows, it always returns the [syscall.EWINDOWS] error, wrapped
135// in *PathError.
136func (f *File) Chown(uid, gid int) error {
137	if err := f.checkValid("chown"); err != nil {
138		return err
139	}
140	if e := f.pfd.Fchown(uid, gid); e != nil {
141		return f.wrapErr("chown", e)
142	}
143	return nil
144}
145
146// Truncate changes the size of the file.
147// It does not change the I/O offset.
148// If there is an error, it will be of type [*PathError].
149func (f *File) Truncate(size int64) error {
150	if err := f.checkValid("truncate"); err != nil {
151		return err
152	}
153	if e := f.pfd.Ftruncate(size); e != nil {
154		return f.wrapErr("truncate", e)
155	}
156	return nil
157}
158
159// Sync commits the current contents of the file to stable storage.
160// Typically, this means flushing the file system's in-memory copy
161// of recently written data to disk.
162func (f *File) Sync() error {
163	if err := f.checkValid("sync"); err != nil {
164		return err
165	}
166	if e := f.pfd.Fsync(); e != nil {
167		return f.wrapErr("sync", e)
168	}
169	return nil
170}
171
172// Chtimes changes the access and modification times of the named
173// file, similar to the Unix utime() or utimes() functions.
174// A zero [time.Time] value will leave the corresponding file time unchanged.
175//
176// The underlying filesystem may truncate or round the values to a
177// less precise time unit.
178// If there is an error, it will be of type [*PathError].
179func Chtimes(name string, atime time.Time, mtime time.Time) error {
180	var utimes [2]syscall.Timespec
181	set := func(i int, t time.Time) {
182		if t.IsZero() {
183			utimes[i] = syscall.Timespec{Sec: _UTIME_OMIT, Nsec: _UTIME_OMIT}
184		} else {
185			utimes[i] = syscall.NsecToTimespec(t.UnixNano())
186		}
187	}
188	set(0, atime)
189	set(1, mtime)
190	if e := syscall.UtimesNano(fixLongPath(name), utimes[0:]); e != nil {
191		return &PathError{Op: "chtimes", Path: name, Err: e}
192	}
193	return nil
194}
195
196// Chdir changes the current working directory to the file,
197// which must be a directory.
198// If there is an error, it will be of type [*PathError].
199func (f *File) Chdir() error {
200	if err := f.checkValid("chdir"); err != nil {
201		return err
202	}
203	if e := f.pfd.Fchdir(); e != nil {
204		return f.wrapErr("chdir", e)
205	}
206	return nil
207}
208
209// setDeadline sets the read and write deadline.
210func (f *File) setDeadline(t time.Time) error {
211	if err := f.checkValid("SetDeadline"); err != nil {
212		return err
213	}
214	return f.pfd.SetDeadline(t)
215}
216
217// setReadDeadline sets the read deadline.
218func (f *File) setReadDeadline(t time.Time) error {
219	if err := f.checkValid("SetReadDeadline"); err != nil {
220		return err
221	}
222	return f.pfd.SetReadDeadline(t)
223}
224
225// setWriteDeadline sets the write deadline.
226func (f *File) setWriteDeadline(t time.Time) error {
227	if err := f.checkValid("SetWriteDeadline"); err != nil {
228		return err
229	}
230	return f.pfd.SetWriteDeadline(t)
231}
232
233// checkValid checks whether f is valid for use.
234// If not, it returns an appropriate error, perhaps incorporating the operation name op.
235func (f *File) checkValid(op string) error {
236	if f == nil {
237		return ErrInvalid
238	}
239	return nil
240}
241
242// ignoringEINTR makes a function call and repeats it if it returns an
243// EINTR error. This appears to be required even though we install all
244// signal handlers with SA_RESTART: see #22838, #38033, #38836, #40846.
245// Also #20400 and #36644 are issues in which a signal handler is
246// installed without setting SA_RESTART. None of these are the common case,
247// but there are enough of them that it seems that we can't avoid
248// an EINTR loop.
249func ignoringEINTR(fn func() error) error {
250	for {
251		err := fn()
252		if err != syscall.EINTR {
253			return err
254		}
255	}
256}
257