Files
sqlite3/vfs/file.go

263 lines
5.5 KiB
Go
Raw Permalink Normal View History

2023-06-01 18:11:37 +01:00
package vfs
2023-03-29 15:01:25 +01:00
import (
2023-05-18 16:00:34 +01:00
"errors"
"io"
"io/fs"
2023-03-29 15:01:25 +01:00
"os"
2023-05-18 16:00:34 +01:00
"path/filepath"
2025-03-26 02:13:30 +00:00
"runtime"
2023-05-23 12:38:29 +01:00
"syscall"
2023-03-29 15:01:25 +01:00
)
2023-05-19 02:00:16 +01:00
type vfsOS struct{}
2023-05-18 16:00:34 +01:00
2023-05-19 02:00:16 +01:00
func (vfsOS) FullPathname(path string) (string, error) {
2025-05-13 14:27:04 +01:00
link, err := evalSymlinks(path)
2023-05-18 16:00:34 +01:00
if err != nil {
return "", err
}
2025-05-13 14:27:04 +01:00
full, err := filepath.Abs(link)
if err == nil && link != path {
err = _OK_SYMLINK
}
return full, err
2024-09-27 12:32:11 +01:00
}
2025-05-13 14:27:04 +01:00
func evalSymlinks(path string) (string, error) {
var file string
_, err := os.Lstat(path)
if errors.Is(err, fs.ErrNotExist) {
path, file = filepath.Split(path)
2023-05-18 16:00:34 +01:00
}
2025-05-13 14:27:04 +01:00
path, err = filepath.EvalSymlinks(path)
if err != nil {
return "", err
2023-05-18 16:00:34 +01:00
}
2025-05-13 14:27:04 +01:00
return filepath.Join(path, file), nil
2023-05-18 16:00:34 +01:00
}
2023-05-19 02:00:16 +01:00
func (vfsOS) Delete(path string, syncDir bool) error {
2023-05-18 16:00:34 +01:00
err := os.Remove(path)
2024-10-30 12:20:21 +00:00
if errors.Is(err, fs.ErrNotExist) {
2025-10-15 16:22:36 +01:00
return sysError{err, _IOERR_DELETE_NOENT}
2024-10-30 12:20:21 +00:00
}
2023-05-18 16:00:34 +01:00
if err != nil {
return err
}
2025-03-26 02:13:30 +00:00
if isUnix && syncDir {
2023-05-18 16:00:34 +01:00
f, err := os.Open(filepath.Dir(path))
if err != nil {
2025-10-17 16:40:15 +01:00
return nil
2023-05-18 16:00:34 +01:00
}
defer f.Close()
2025-09-30 12:54:18 +01:00
err = osSync(f, 0, SYNC_FULL)
2023-05-18 16:00:34 +01:00
if err != nil {
2025-10-15 16:22:36 +01:00
return sysError{err, _IOERR_DIR_FSYNC}
2023-05-18 16:00:34 +01:00
}
}
return nil
}
2023-05-19 03:04:07 +01:00
func (vfsOS) Access(name string, flags AccessFlag) (bool, error) {
2023-05-18 16:00:34 +01:00
err := osAccess(name, flags)
2023-05-19 03:04:07 +01:00
if flags == ACCESS_EXISTS {
2023-05-18 16:00:34 +01:00
if errors.Is(err, fs.ErrNotExist) {
return false, nil
}
} else {
if errors.Is(err, fs.ErrPermission) {
return false, nil
}
}
return err == nil, err
}
2023-05-19 03:04:07 +01:00
func (vfsOS) Open(name string, flags OpenFlag) (File, OpenFlag, error) {
2024-07-26 01:23:35 +01:00
// notest // OpenFilename is called instead
2025-08-14 15:04:10 +01:00
if name == "" {
return vfsOS{}.OpenFilename(nil, flags)
}
return nil, 0, _CANTOPEN
2023-06-02 02:31:15 +01:00
}
func (vfsOS) OpenFilename(name *Filename, flags OpenFlag) (File, OpenFlag, error) {
2024-09-27 12:32:11 +01:00
oflags := _O_NOFOLLOW
2023-05-19 03:04:07 +01:00
if flags&OPEN_EXCLUSIVE != 0 {
2023-05-18 16:00:34 +01:00
oflags |= os.O_EXCL
}
2023-05-19 03:04:07 +01:00
if flags&OPEN_CREATE != 0 {
2023-05-18 16:00:34 +01:00
oflags |= os.O_CREATE
}
2023-05-19 03:04:07 +01:00
if flags&OPEN_READONLY != 0 {
2023-05-18 16:00:34 +01:00
oflags |= os.O_RDONLY
}
2023-05-19 03:04:07 +01:00
if flags&OPEN_READWRITE != 0 {
2023-05-18 16:00:34 +01:00
oflags |= os.O_RDWR
}
2025-02-24 12:25:19 +00:00
isCreate := flags&(OPEN_CREATE) != 0
isJournl := flags&(OPEN_MAIN_JOURNAL|OPEN_SUPER_JOURNAL|OPEN_WAL) != 0
2023-05-18 16:00:34 +01:00
var err error
var f *os.File
if name == nil {
2025-02-24 13:12:30 +00:00
f, err = os.CreateTemp(os.Getenv("SQLITE_TMPDIR"), "*.db")
2023-05-18 16:00:34 +01:00
} else {
2025-03-26 02:13:30 +00:00
f, err = os.OpenFile(name.String(), oflags, 0666)
2023-05-18 16:00:34 +01:00
}
if err != nil {
2024-06-06 00:09:14 +01:00
if name == nil {
2025-10-15 16:22:36 +01:00
return nil, flags, sysError{err, _IOERR_GETTEMPPATH}
2024-06-06 00:09:14 +01:00
}
2023-05-23 12:38:29 +01:00
if errors.Is(err, syscall.EISDIR) {
2025-10-15 16:22:36 +01:00
return nil, flags, sysError{err, _CANTOPEN_ISDIR}
2023-05-23 12:38:29 +01:00
}
2025-02-24 12:25:19 +00:00
if isCreate && isJournl && errors.Is(err, fs.ErrPermission) &&
osAccess(name.String(), ACCESS_EXISTS) != nil {
2025-10-15 16:22:36 +01:00
return nil, flags, sysError{err, _READONLY_DIRECTORY}
2025-02-24 12:25:19 +00:00
}
2023-05-18 16:00:34 +01:00
return nil, flags, err
}
if modeof := name.URIParameter("modeof"); modeof != "" {
2023-06-02 02:31:15 +01:00
if err = osSetMode(f, modeof); err != nil {
f.Close()
2025-10-15 16:22:36 +01:00
return nil, flags, sysError{err, _IOERR_FSTAT}
2023-06-02 02:31:15 +01:00
}
}
2025-03-26 02:13:30 +00:00
if isUnix && flags&OPEN_DELETEONCLOSE != 0 {
2023-05-18 16:00:34 +01:00
os.Remove(f.Name())
}
file := vfsFile{
2025-09-30 12:54:18 +01:00
File: f,
flags: flags | _FLAG_PSOW,
shm: NewSharedMemory(name.String()+"-shm", flags),
}
if osBatchAtomic(f) {
file.flags |= _FLAG_ATOMIC
}
if isUnix && isCreate && isJournl {
file.flags |= _FLAG_SYNC_DIR
2023-05-18 16:00:34 +01:00
}
return &file, flags, nil
}
2023-04-10 19:55:44 +01:00
type vfsFile struct {
*os.File
2025-09-30 12:54:18 +01:00
shm SharedMemory
lock LockLevel
flags OpenFlag
2023-04-10 19:55:44 +01:00
}
2023-03-29 15:01:25 +01:00
2023-05-19 02:00:16 +01:00
var (
// Ensure these interfaces are implemented:
2023-05-19 03:04:07 +01:00
_ FileLockState = &vfsFile{}
_ FileHasMoved = &vfsFile{}
_ FileSizeHint = &vfsFile{}
2025-01-13 09:28:47 +00:00
_ FilePersistWAL = &vfsFile{}
2023-05-19 03:04:07 +01:00
_ FilePowersafeOverwrite = &vfsFile{}
2023-05-19 02:00:16 +01:00
)
func (f *vfsFile) Close() error {
2025-09-30 12:54:18 +01:00
if !isUnix && f.flags&OPEN_DELETEONCLOSE != 0 {
2025-03-26 02:13:30 +00:00
defer os.Remove(f.Name())
}
2024-04-27 20:53:54 +01:00
if f.shm != nil {
f.shm.Close()
}
2024-10-30 12:20:21 +00:00
f.Unlock(LOCK_NONE)
return f.File.Close()
}
2025-02-24 12:25:19 +00:00
func (f *vfsFile) ReadAt(p []byte, off int64) (n int, err error) {
return osReadAt(f.File, p, off)
}
func (f *vfsFile) WriteAt(p []byte, off int64) (n int, err error) {
return osWriteAt(f.File, p, off)
}
2023-05-19 03:04:07 +01:00
func (f *vfsFile) Sync(flags SyncFlag) error {
2025-09-30 12:54:18 +01:00
err := osSync(f.File, f.flags, flags)
2023-05-18 16:00:34 +01:00
if err != nil {
return err
}
2025-09-30 12:54:18 +01:00
if isUnix && f.flags&_FLAG_SYNC_DIR != 0 {
f.flags ^= _FLAG_SYNC_DIR
2023-05-18 16:00:34 +01:00
d, err := os.Open(filepath.Dir(f.File.Name()))
if err != nil {
return nil
}
defer d.Close()
2025-09-30 12:54:18 +01:00
err = osSync(f.File, f.flags, flags)
2023-05-18 16:00:34 +01:00
if err != nil {
2025-10-15 16:22:36 +01:00
return sysError{err, _IOERR_DIR_FSYNC}
2023-05-18 16:00:34 +01:00
}
}
return nil
}
2023-05-23 16:34:09 +01:00
func (f *vfsFile) Size() (int64, error) {
2023-05-18 16:00:34 +01:00
return f.Seek(0, io.SeekEnd)
}
2024-04-28 10:33:39 +01:00
func (f *vfsFile) SectorSize() int {
2023-05-18 16:00:34 +01:00
return _DEFAULT_SECTOR_SIZE
}
2023-05-19 02:00:16 +01:00
2023-05-19 03:04:07 +01:00
func (f *vfsFile) DeviceCharacteristics() DeviceCharacteristic {
2025-01-21 01:42:57 +00:00
ret := IOCAP_SUBPAGE_READ
2025-09-30 12:54:18 +01:00
if f.flags&_FLAG_ATOMIC != 0 {
2025-01-21 01:42:57 +00:00
ret |= IOCAP_BATCH_ATOMIC
}
2025-09-30 12:54:18 +01:00
if f.flags&_FLAG_PSOW != 0 {
2025-01-21 01:42:57 +00:00
ret |= IOCAP_POWERSAFE_OVERWRITE
2023-05-19 02:00:16 +01:00
}
2025-03-26 02:13:30 +00:00
if runtime.GOOS == "windows" {
ret |= IOCAP_UNDELETABLE_WHEN_OPEN
}
2025-01-21 01:42:57 +00:00
return ret
2023-05-19 02:00:16 +01:00
}
func (f *vfsFile) SizeHint(size int64) error {
return osAllocate(f.File, size)
}
func (f *vfsFile) HasMoved() (bool, error) {
2025-03-26 02:13:30 +00:00
if runtime.GOOS == "windows" {
return false, nil
}
2023-05-19 02:00:16 +01:00
fi, err := f.Stat()
if err != nil {
return false, err
}
pi, err := os.Stat(f.Name())
2024-10-30 12:20:21 +00:00
if errors.Is(err, fs.ErrNotExist) {
return true, nil
}
2023-05-23 12:38:29 +01:00
if err != nil {
2023-05-19 02:00:16 +01:00
return false, err
}
return !os.SameFile(fi, pi), nil
}
2025-09-30 12:54:18 +01:00
func (f *vfsFile) LockState() LockLevel { return f.lock }
func (f *vfsFile) PowersafeOverwrite() bool { return f.flags&_FLAG_PSOW != 0 }
func (f *vfsFile) PersistWAL() bool { return f.flags&_FLAG_KEEP_WAL != 0 }
func (f *vfsFile) SetPowersafeOverwrite(psow bool) {
f.flags &^= _FLAG_PSOW
if psow {
f.flags |= _FLAG_PSOW
}
}
func (f *vfsFile) SetPersistWAL(keepWAL bool) {
f.flags &^= _FLAG_KEEP_WAL
if keepWAL {
f.flags |= _FLAG_KEEP_WAL
}
}