1package vfs
2
3import (
4 "errors"
5 "io"
6 "io/fs"
7 "os"
8 "path/filepath"
9 "runtime"
10 "syscall"
11)
12
13type vfsOS struct{}
14
15func (vfsOS) FullPathname(path string) (string, error) {
16 path, err := filepath.Abs(path)
17 if err != nil {
18 return "", err
19 }
20 return path, testSymlinks(filepath.Dir(path))
21}
22
23func testSymlinks(path string) error {
24 p, err := filepath.EvalSymlinks(path)
25 if err != nil {
26 return err
27 }
28 if p != path {
29 return _OK_SYMLINK
30 }
31 return nil
32}
33
34func (vfsOS) Delete(path string, syncDir bool) error {
35 err := os.Remove(path)
36 if errors.Is(err, fs.ErrNotExist) {
37 return _IOERR_DELETE_NOENT
38 }
39 if err != nil {
40 return err
41 }
42 if isUnix && syncDir {
43 f, err := os.Open(filepath.Dir(path))
44 if err != nil {
45 return _OK
46 }
47 defer f.Close()
48 err = osSync(f, false, false)
49 if err != nil {
50 return _IOERR_DIR_FSYNC
51 }
52 }
53 return nil
54}
55
56func (vfsOS) Access(name string, flags AccessFlag) (bool, error) {
57 err := osAccess(name, flags)
58 if flags == ACCESS_EXISTS {
59 if errors.Is(err, fs.ErrNotExist) {
60 return false, nil
61 }
62 } else {
63 if errors.Is(err, fs.ErrPermission) {
64 return false, nil
65 }
66 }
67 return err == nil, err
68}
69
70func (vfsOS) Open(name string, flags OpenFlag) (File, OpenFlag, error) {
71 // notest // OpenFilename is called instead
72 return nil, 0, _CANTOPEN
73}
74
75func (vfsOS) OpenFilename(name *Filename, flags OpenFlag) (File, OpenFlag, error) {
76 oflags := _O_NOFOLLOW
77 if flags&OPEN_EXCLUSIVE != 0 {
78 oflags |= os.O_EXCL
79 }
80 if flags&OPEN_CREATE != 0 {
81 oflags |= os.O_CREATE
82 }
83 if flags&OPEN_READONLY != 0 {
84 oflags |= os.O_RDONLY
85 }
86 if flags&OPEN_READWRITE != 0 {
87 oflags |= os.O_RDWR
88 }
89
90 isCreate := flags&(OPEN_CREATE) != 0
91 isJournl := flags&(OPEN_MAIN_JOURNAL|OPEN_SUPER_JOURNAL|OPEN_WAL) != 0
92
93 var err error
94 var f *os.File
95 if name == nil {
96 f, err = os.CreateTemp(os.Getenv("SQLITE_TMPDIR"), "*.db")
97 } else {
98 f, err = os.OpenFile(name.String(), oflags, 0666)
99 }
100 if err != nil {
101 if name == nil {
102 return nil, flags, _IOERR_GETTEMPPATH
103 }
104 if errors.Is(err, syscall.EISDIR) {
105 return nil, flags, _CANTOPEN_ISDIR
106 }
107 if isCreate && isJournl && errors.Is(err, fs.ErrPermission) &&
108 osAccess(name.String(), ACCESS_EXISTS) != nil {
109 return nil, flags, _READONLY_DIRECTORY
110 }
111 return nil, flags, err
112 }
113
114 if modeof := name.URIParameter("modeof"); modeof != "" {
115 if err = osSetMode(f, modeof); err != nil {
116 f.Close()
117 return nil, flags, _IOERR_FSTAT
118 }
119 }
120 if isUnix && flags&OPEN_DELETEONCLOSE != 0 {
121 os.Remove(f.Name())
122 }
123
124 file := vfsFile{
125 File: f,
126 psow: true,
127 atomic: osBatchAtomic(f),
128 readOnly: flags&OPEN_READONLY != 0,
129 syncDir: isUnix && isCreate && isJournl,
130 delete: !isUnix && flags&OPEN_DELETEONCLOSE != 0,
131 shm: NewSharedMemory(name.String()+"-shm", flags),
132 }
133 return &file, flags, nil
134}
135
136type vfsFile struct {
137 *os.File
138 shm SharedMemory
139 lock LockLevel
140 readOnly bool
141 keepWAL bool
142 syncDir bool
143 atomic bool
144 delete bool
145 psow bool
146}
147
148var (
149 // Ensure these interfaces are implemented:
150 _ FileLockState = &vfsFile{}
151 _ FileHasMoved = &vfsFile{}
152 _ FileSizeHint = &vfsFile{}
153 _ FilePersistWAL = &vfsFile{}
154 _ FilePowersafeOverwrite = &vfsFile{}
155)
156
157func (f *vfsFile) Close() error {
158 if f.delete {
159 defer os.Remove(f.Name())
160 }
161 if f.shm != nil {
162 f.shm.Close()
163 }
164 f.Unlock(LOCK_NONE)
165 return f.File.Close()
166}
167
168func (f *vfsFile) ReadAt(p []byte, off int64) (n int, err error) {
169 return osReadAt(f.File, p, off)
170}
171
172func (f *vfsFile) WriteAt(p []byte, off int64) (n int, err error) {
173 return osWriteAt(f.File, p, off)
174}
175
176func (f *vfsFile) Sync(flags SyncFlag) error {
177 dataonly := (flags & SYNC_DATAONLY) != 0
178 fullsync := (flags & 0x0f) == SYNC_FULL
179
180 err := osSync(f.File, fullsync, dataonly)
181 if err != nil {
182 return err
183 }
184 if isUnix && f.syncDir {
185 f.syncDir = false
186 d, err := os.Open(filepath.Dir(f.File.Name()))
187 if err != nil {
188 return nil
189 }
190 defer d.Close()
191 err = osSync(d, false, false)
192 if err != nil {
193 return _IOERR_DIR_FSYNC
194 }
195 }
196 return nil
197}
198
199func (f *vfsFile) Size() (int64, error) {
200 return f.Seek(0, io.SeekEnd)
201}
202
203func (f *vfsFile) SectorSize() int {
204 return _DEFAULT_SECTOR_SIZE
205}
206
207func (f *vfsFile) DeviceCharacteristics() DeviceCharacteristic {
208 ret := IOCAP_SUBPAGE_READ
209 if f.atomic {
210 ret |= IOCAP_BATCH_ATOMIC
211 }
212 if f.psow {
213 ret |= IOCAP_POWERSAFE_OVERWRITE
214 }
215 if runtime.GOOS == "windows" {
216 ret |= IOCAP_UNDELETABLE_WHEN_OPEN
217 }
218 return ret
219}
220
221func (f *vfsFile) SizeHint(size int64) error {
222 return osAllocate(f.File, size)
223}
224
225func (f *vfsFile) HasMoved() (bool, error) {
226 if runtime.GOOS == "windows" {
227 return false, nil
228 }
229 fi, err := f.Stat()
230 if err != nil {
231 return false, err
232 }
233 pi, err := os.Stat(f.Name())
234 if errors.Is(err, fs.ErrNotExist) {
235 return true, nil
236 }
237 if err != nil {
238 return false, err
239 }
240 return !os.SameFile(fi, pi), nil
241}
242
243func (f *vfsFile) LockState() LockLevel { return f.lock }
244func (f *vfsFile) PowersafeOverwrite() bool { return f.psow }
245func (f *vfsFile) PersistWAL() bool { return f.keepWAL }
246func (f *vfsFile) SetPowersafeOverwrite(psow bool) { f.psow = psow }
247func (f *vfsFile) SetPersistWAL(keepWAL bool) { f.keepWAL = keepWAL }