file.go

  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 }