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