Merge branch '20083-sync-readonly'
[arvados.git] / sdk / go / arvados / fs_filehandle.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: Apache-2.0
4
5 package arvados
6
7 import (
8         "io"
9         "io/fs"
10         "os"
11 )
12
13 type filehandle struct {
14         inode
15         ptr        filenodePtr
16         append     bool
17         readable   bool
18         writable   bool
19         unreaddirs []os.FileInfo
20 }
21
22 func (f *filehandle) Read(p []byte) (n int, err error) {
23         if !f.readable {
24                 return 0, ErrWriteOnlyMode
25         }
26         f.inode.RLock()
27         defer f.inode.RUnlock()
28         n, f.ptr, err = f.inode.Read(p, f.ptr)
29         return
30 }
31
32 func (f *filehandle) Seek(off int64, whence int) (pos int64, err error) {
33         size := f.inode.Size()
34         ptr := f.ptr
35         switch whence {
36         case io.SeekStart:
37                 ptr.off = off
38         case io.SeekCurrent:
39                 ptr.off += off
40         case io.SeekEnd:
41                 ptr.off = size + off
42         }
43         if ptr.off < 0 {
44                 return f.ptr.off, ErrNegativeOffset
45         }
46         if ptr.off != f.ptr.off {
47                 f.ptr = ptr
48                 // force filenode to recompute f.ptr fields on next
49                 // use
50                 f.ptr.repacked = -1
51         }
52         return f.ptr.off, nil
53 }
54
55 func (f *filehandle) Truncate(size int64) error {
56         return f.inode.Truncate(size)
57 }
58
59 func (f *filehandle) Write(p []byte) (n int, err error) {
60         if !f.writable {
61                 return 0, ErrReadOnlyFile
62         }
63         f.inode.Lock()
64         defer f.inode.Unlock()
65         if fn, ok := f.inode.(*filenode); ok && f.append {
66                 f.ptr = filenodePtr{
67                         off:        fn.fileinfo.size,
68                         segmentIdx: len(fn.segments),
69                         segmentOff: 0,
70                         repacked:   fn.repacked,
71                 }
72         }
73         n, f.ptr, err = f.inode.Write(p, f.ptr)
74         return
75 }
76
77 // dirEntry implements fs.DirEntry, see (*filehandle)ReadDir().
78 type dirEntry struct {
79         os.FileInfo
80 }
81
82 func (ent dirEntry) Type() fs.FileMode {
83         return ent.Mode().Type()
84 }
85 func (ent dirEntry) Info() (fs.FileInfo, error) {
86         return ent, nil
87 }
88
89 // ReadDir implements fs.ReadDirFile.
90 func (f *filehandle) ReadDir(count int) ([]fs.DirEntry, error) {
91         fis, err := f.Readdir(count)
92         if len(fis) == 0 {
93                 return nil, err
94         }
95         ents := make([]fs.DirEntry, len(fis))
96         for i, fi := range fis {
97                 ents[i] = dirEntry{fi}
98         }
99         return ents, err
100 }
101
102 func (f *filehandle) Readdir(count int) ([]os.FileInfo, error) {
103         if !f.inode.IsDir() {
104                 return nil, ErrInvalidOperation
105         }
106         if count <= 0 {
107                 return f.inode.Readdir()
108         }
109         if f.unreaddirs == nil {
110                 var err error
111                 f.unreaddirs, err = f.inode.Readdir()
112                 if err != nil {
113                         return nil, err
114                 }
115         }
116         if len(f.unreaddirs) == 0 {
117                 return nil, io.EOF
118         }
119         if count > len(f.unreaddirs) {
120                 count = len(f.unreaddirs)
121         }
122         ret := f.unreaddirs[:count]
123         f.unreaddirs = f.unreaddirs[count:]
124         return ret, nil
125 }
126
127 func (f *filehandle) Stat() (os.FileInfo, error) {
128         return f.inode.FileInfo(), nil
129 }
130
131 func (f *filehandle) Close() error {
132         return nil
133 }
134
135 func (f *filehandle) Sync() error {
136         // Sync the containing filesystem.
137         return f.FS().Sync()
138 }
139
140 func (f *filehandle) Snapshot() (*Subtree, error) {
141         if !f.readable {
142                 return nil, ErrInvalidOperation
143         }
144         node, err := f.inode.Snapshot()
145         return &Subtree{inode: node}, err
146 }
147
148 func (f *filehandle) Splice(r *Subtree) error {
149         if !f.writable {
150                 return ErrReadOnlyFile
151         }
152         return f.inode.Splice(r.inode)
153 }