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	"runtime"
9	"syscall"
10	"time"
11)
12
13// File represents an open file descriptor.
14type File struct {
15	*file
16}
17
18// file is the real representation of *File.
19// The extra level of indirection ensures that no clients of os
20// can overwrite this data, which could cause the finalizer
21// to close the wrong file descriptor.
22type file struct {
23	fd      int
24	name    string
25	dirinfo *dirInfo // nil unless directory being read
26}
27
28// Fd returns the integer Plan 9 file descriptor referencing the open file.
29// The file descriptor is valid only until f.Close is called or f is garbage collected.
30func (f *File) Fd() uintptr {
31	if f == nil {
32		return ^(uintptr(0))
33	}
34	return uintptr(f.fd)
35}
36
37// NewFile returns a new File with the given file descriptor and name.
38func NewFile(fd uintptr, name string) *File {
39	fdi := int(fd)
40	if fdi < 0 {
41		return nil
42	}
43	f := &File{&file{fd: fdi, name: name}}
44	runtime.SetFinalizer(f.file, (*file).close)
45	return f
46}
47
48// Auxiliary information if the File describes a directory
49type dirInfo struct {
50	buf  [syscall.STATMAX]byte // buffer for directory I/O
51	nbuf int                   // length of buf; return value from Read
52	bufp int                   // location of next record in buf.
53}
54
55func epipecheck(file *File, e error) {
56}
57
58// DevNull is the name of the operating system's ``null device.''
59// On Unix-like systems, it is "/dev/null"; on Windows, "NUL".
60const DevNull = "/dev/null"
61
62// syscallMode returns the syscall-specific mode bits from Go's portable mode bits.
63func syscallMode(i FileMode) (o uint32) {
64	o |= uint32(i.Perm())
65	if i&ModeAppend != 0 {
66		o |= syscall.DMAPPEND
67	}
68	if i&ModeExclusive != 0 {
69		o |= syscall.DMEXCL
70	}
71	if i&ModeTemporary != 0 {
72		o |= syscall.DMTMP
73	}
74	return
75}
76
77// OpenFile is the generalized open call; most users will use Open
78// or Create instead.  It opens the named file with specified flag
79// (O_RDONLY etc.) and perm, (0666 etc.) if applicable.  If successful,
80// methods on the returned File can be used for I/O.
81// If there is an error, it will be of type *PathError.
82func OpenFile(name string, flag int, perm FileMode) (*File, error) {
83	var (
84		fd     int
85		e      error
86		create bool
87		excl   bool
88		trunc  bool
89		append bool
90	)
91
92	if flag&O_CREATE == O_CREATE {
93		flag = flag & ^O_CREATE
94		create = true
95	}
96	if flag&O_EXCL == O_EXCL {
97		excl = true
98	}
99	if flag&O_TRUNC == O_TRUNC {
100		trunc = true
101	}
102	// O_APPEND is emulated on Plan 9
103	if flag&O_APPEND == O_APPEND {
104		flag = flag &^ O_APPEND
105		append = true
106	}
107
108	if (create && trunc) || excl {
109		fd, e = syscall.Create(name, flag, syscallMode(perm))
110	} else {
111		fd, e = syscall.Open(name, flag)
112		if e != nil && create {
113			var e1 error
114			fd, e1 = syscall.Create(name, flag, syscallMode(perm))
115			if e1 == nil {
116				e = nil
117			}
118		}
119	}
120
121	if e != nil {
122		return nil, &PathError{"open", name, e}
123	}
124
125	if append {
126		if _, e = syscall.Seek(fd, 0, SEEK_END); e != nil {
127			return nil, &PathError{"seek", name, e}
128		}
129	}
130
131	return NewFile(uintptr(fd), name), nil
132}
133
134// Close closes the File, rendering it unusable for I/O.
135// It returns an error, if any.
136func (f *File) Close() error {
137	if f == nil {
138		return ErrInvalid
139	}
140	return f.file.close()
141}
142
143func (file *file) close() error {
144	if file == nil || file.fd < 0 {
145		return ErrInvalid
146	}
147	var err error
148	syscall.ForkLock.RLock()
149	if e := syscall.Close(file.fd); e != nil {
150		err = &PathError{"close", file.name, e}
151	}
152	syscall.ForkLock.RUnlock()
153	file.fd = -1 // so it can't be closed again
154
155	// no need for a finalizer anymore
156	runtime.SetFinalizer(file, nil)
157	return err
158}
159
160// Stat returns the FileInfo structure describing file.
161// If there is an error, it will be of type *PathError.
162func (f *File) Stat() (FileInfo, error) {
163	if f == nil {
164		return nil, ErrInvalid
165	}
166	d, err := dirstat(f)
167	if err != nil {
168		return nil, err
169	}
170	return fileInfoFromStat(d), nil
171}
172
173// Truncate changes the size of the file.
174// It does not change the I/O offset.
175// If there is an error, it will be of type *PathError.
176func (f *File) Truncate(size int64) error {
177	if f == nil {
178		return ErrInvalid
179	}
180
181	var d syscall.Dir
182	d.Null()
183	d.Length = size
184
185	var buf [syscall.STATFIXLEN]byte
186	n, err := d.Marshal(buf[:])
187	if err != nil {
188		return &PathError{"truncate", f.name, err}
189	}
190	if err = syscall.Fwstat(f.fd, buf[:n]); err != nil {
191		return &PathError{"truncate", f.name, err}
192	}
193	return nil
194}
195
196const chmodMask = uint32(syscall.DMAPPEND | syscall.DMEXCL | syscall.DMTMP | ModePerm)
197
198// Chmod changes the mode of the file to mode.
199// If there is an error, it will be of type *PathError.
200func (f *File) Chmod(mode FileMode) error {
201	if f == nil {
202		return ErrInvalid
203	}
204	var d syscall.Dir
205
206	odir, e := dirstat(f)
207	if e != nil {
208		return &PathError{"chmod", f.name, e}
209	}
210	d.Null()
211	d.Mode = odir.Mode&^chmodMask | syscallMode(mode)&chmodMask
212
213	var buf [syscall.STATFIXLEN]byte
214	n, err := d.Marshal(buf[:])
215	if err != nil {
216		return &PathError{"chmod", f.name, err}
217	}
218	if err = syscall.Fwstat(f.fd, buf[:n]); err != nil {
219		return &PathError{"chmod", f.name, err}
220	}
221	return nil
222}
223
224// Sync commits the current contents of the file to stable storage.
225// Typically, this means flushing the file system's in-memory copy
226// of recently written data to disk.
227func (f *File) Sync() error {
228	if f == nil {
229		return ErrInvalid
230	}
231	var d syscall.Dir
232	d.Null()
233
234	var buf [syscall.STATFIXLEN]byte
235	n, err := d.Marshal(buf[:])
236	if err != nil {
237		return NewSyscallError("fsync", err)
238	}
239	if err = syscall.Fwstat(f.fd, buf[:n]); err != nil {
240		return NewSyscallError("fsync", err)
241	}
242	return nil
243}
244
245// read reads up to len(b) bytes from the File.
246// It returns the number of bytes read and an error, if any.
247func (f *File) read(b []byte) (n int, err error) {
248	return fixCount(syscall.Read(f.fd, b))
249}
250
251// pread reads len(b) bytes from the File starting at byte offset off.
252// It returns the number of bytes read and the error, if any.
253// EOF is signaled by a zero count with err set to nil.
254func (f *File) pread(b []byte, off int64) (n int, err error) {
255	return fixCount(syscall.Pread(f.fd, b, off))
256}
257
258// write writes len(b) bytes to the File.
259// It returns the number of bytes written and an error, if any.
260// Since Plan 9 preserves message boundaries, never allow
261// a zero-byte write.
262func (f *File) write(b []byte) (n int, err error) {
263	if len(b) == 0 {
264		return 0, nil
265	}
266	return fixCount(syscall.Write(f.fd, b))
267}
268
269// pwrite writes len(b) bytes to the File starting at byte offset off.
270// It returns the number of bytes written and an error, if any.
271// Since Plan 9 preserves message boundaries, never allow
272// a zero-byte write.
273func (f *File) pwrite(b []byte, off int64) (n int, err error) {
274	if len(b) == 0 {
275		return 0, nil
276	}
277	return fixCount(syscall.Pwrite(f.fd, b, off))
278}
279
280// seek sets the offset for the next Read or Write on file to offset, interpreted
281// according to whence: 0 means relative to the origin of the file, 1 means
282// relative to the current offset, and 2 means relative to the end.
283// It returns the new offset and an error, if any.
284func (f *File) seek(offset int64, whence int) (ret int64, err error) {
285	return syscall.Seek(f.fd, offset, whence)
286}
287
288// Truncate changes the size of the named file.
289// If the file is a symbolic link, it changes the size of the link's target.
290// If there is an error, it will be of type *PathError.
291func Truncate(name string, size int64) error {
292	var d syscall.Dir
293
294	d.Null()
295	d.Length = size
296
297	var buf [syscall.STATFIXLEN]byte
298	n, err := d.Marshal(buf[:])
299	if err != nil {
300		return &PathError{"truncate", name, err}
301	}
302	if err = syscall.Wstat(name, buf[:n]); err != nil {
303		return &PathError{"truncate", name, err}
304	}
305	return nil
306}
307
308// Remove removes the named file or directory.
309// If there is an error, it will be of type *PathError.
310func Remove(name string) error {
311	if e := syscall.Remove(name); e != nil {
312		return &PathError{"remove", name, e}
313	}
314	return nil
315}
316
317// HasPrefix from the strings package.
318func hasPrefix(s, prefix string) bool {
319	return len(s) >= len(prefix) && s[0:len(prefix)] == prefix
320}
321
322// LastIndexByte from the strings package.
323func lastIndex(s string, sep byte) int {
324	for i := len(s) - 1; i >= 0; i-- {
325		if s[i] == sep {
326			return i
327		}
328	}
329	return -1
330}
331
332func rename(oldname, newname string) error {
333	dirname := oldname[:lastIndex(oldname, '/')+1]
334	if hasPrefix(newname, dirname) {
335		newname = newname[len(dirname):]
336	} else {
337		return &LinkError{"rename", oldname, newname, ErrInvalid}
338	}
339
340	// If newname still contains slashes after removing the oldname
341	// prefix, the rename is cross-directory and must be rejected.
342	if lastIndex(newname, '/') >= 0 {
343		return &LinkError{"rename", oldname, newname, ErrInvalid}
344	}
345
346	var d syscall.Dir
347
348	d.Null()
349	d.Name = newname
350
351	buf := make([]byte, syscall.STATFIXLEN+len(d.Name))
352	n, err := d.Marshal(buf[:])
353	if err != nil {
354		return &LinkError{"rename", oldname, newname, err}
355	}
356
357	// If newname already exists and is not a directory, rename replaces it.
358	f, err := Stat(dirname + newname)
359	if err == nil && !f.IsDir() {
360		Remove(dirname + newname)
361	}
362
363	if err = syscall.Wstat(oldname, buf[:n]); err != nil {
364		return &LinkError{"rename", oldname, newname, err}
365	}
366	return nil
367}
368
369// Chmod changes the mode of the named file to mode.
370// If the file is a symbolic link, it changes the mode of the link's target.
371// If there is an error, it will be of type *PathError.
372func Chmod(name string, mode FileMode) error {
373	var d syscall.Dir
374
375	odir, e := dirstat(name)
376	if e != nil {
377		return &PathError{"chmod", name, e}
378	}
379	d.Null()
380	d.Mode = odir.Mode&^chmodMask | syscallMode(mode)&chmodMask
381
382	var buf [syscall.STATFIXLEN]byte
383	n, err := d.Marshal(buf[:])
384	if err != nil {
385		return &PathError{"chmod", name, err}
386	}
387	if err = syscall.Wstat(name, buf[:n]); err != nil {
388		return &PathError{"chmod", name, err}
389	}
390	return nil
391}
392
393// Chtimes changes the access and modification times of the named
394// file, similar to the Unix utime() or utimes() functions.
395//
396// The underlying filesystem may truncate or round the values to a
397// less precise time unit.
398// If there is an error, it will be of type *PathError.
399func Chtimes(name string, atime time.Time, mtime time.Time) error {
400	var d syscall.Dir
401
402	d.Null()
403	d.Atime = uint32(atime.Unix())
404	d.Mtime = uint32(mtime.Unix())
405
406	var buf [syscall.STATFIXLEN]byte
407	n, err := d.Marshal(buf[:])
408	if err != nil {
409		return &PathError{"chtimes", name, err}
410	}
411	if err = syscall.Wstat(name, buf[:n]); err != nil {
412		return &PathError{"chtimes", name, err}
413	}
414	return nil
415}
416
417// Pipe returns a connected pair of Files; reads from r return bytes
418// written to w. It returns the files and an error, if any.
419func Pipe() (r *File, w *File, err error) {
420	var p [2]int
421
422	syscall.ForkLock.RLock()
423	if e := syscall.Pipe(p[0:]); e != nil {
424		syscall.ForkLock.RUnlock()
425		return nil, nil, NewSyscallError("pipe", e)
426	}
427	syscall.ForkLock.RUnlock()
428
429	return NewFile(uintptr(p[0]), "|0"), NewFile(uintptr(p[1]), "|1"), nil
430}
431
432// not supported on Plan 9
433
434// Link creates newname as a hard link to the oldname file.
435// If there is an error, it will be of type *LinkError.
436func Link(oldname, newname string) error {
437	return &LinkError{"link", oldname, newname, syscall.EPLAN9}
438}
439
440// Symlink creates newname as a symbolic link to oldname.
441// If there is an error, it will be of type *LinkError.
442func Symlink(oldname, newname string) error {
443	return &LinkError{"symlink", oldname, newname, syscall.EPLAN9}
444}
445
446// Readlink returns the destination of the named symbolic link.
447// If there is an error, it will be of type *PathError.
448func Readlink(name string) (string, error) {
449	return "", &PathError{"readlink", name, syscall.EPLAN9}
450}
451
452// Chown changes the numeric uid and gid of the named file.
453// If the file is a symbolic link, it changes the uid and gid of the link's target.
454// If there is an error, it will be of type *PathError.
455func Chown(name string, uid, gid int) error {
456	return &PathError{"chown", name, syscall.EPLAN9}
457}
458
459// Lchown changes the numeric uid and gid of the named file.
460// If the file is a symbolic link, it changes the uid and gid of the link itself.
461// If there is an error, it will be of type *PathError.
462func Lchown(name string, uid, gid int) error {
463	return &PathError{"lchown", name, syscall.EPLAN9}
464}
465
466// Chown changes the numeric uid and gid of the named file.
467// If there is an error, it will be of type *PathError.
468func (f *File) Chown(uid, gid int) error {
469	if f == nil {
470		return ErrInvalid
471	}
472	return &PathError{"chown", f.name, syscall.EPLAN9}
473}
474
475// TempDir returns the default directory to use for temporary files.
476func TempDir() string {
477	return "/tmp"
478}
479