github.com/ncruces/go-sqlite3@v0.15.1-0.20240520133447-53eef1510ff0/vfs/file.go (about)

     1  package vfs
     2  
     3  import (
     4  	"errors"
     5  	"io"
     6  	"io/fs"
     7  	"os"
     8  	"path/filepath"
     9  	"runtime"
    10  	"syscall"
    11  
    12  	"github.com/ncruces/go-sqlite3/util/osutil"
    13  )
    14  
    15  type vfsOS struct{}
    16  
    17  func (vfsOS) FullPathname(path string) (string, error) {
    18  	path, err := filepath.Abs(path)
    19  	if err != nil {
    20  		return "", err
    21  	}
    22  	fi, err := os.Lstat(path)
    23  	if err != nil {
    24  		if errors.Is(err, fs.ErrNotExist) {
    25  			return path, nil
    26  		}
    27  		return "", err
    28  	}
    29  	if fi.Mode()&fs.ModeSymlink != 0 {
    30  		err = _OK_SYMLINK
    31  	}
    32  	return path, err
    33  }
    34  
    35  func (vfsOS) Delete(path string, syncDir bool) error {
    36  	err := os.Remove(path)
    37  	if err != nil {
    38  		if errors.Is(err, fs.ErrNotExist) {
    39  			return _IOERR_DELETE_NOENT
    40  		}
    41  		return err
    42  	}
    43  	if runtime.GOOS != "windows" && syncDir {
    44  		f, err := os.Open(filepath.Dir(path))
    45  		if err != nil {
    46  			return _OK
    47  		}
    48  		defer f.Close()
    49  		err = osSync(f, false, false)
    50  		if err != nil {
    51  			return _IOERR_DIR_FSYNC
    52  		}
    53  	}
    54  	return nil
    55  }
    56  
    57  func (vfsOS) Access(name string, flags AccessFlag) (bool, error) {
    58  	err := osAccess(name, flags)
    59  	if flags == ACCESS_EXISTS {
    60  		if errors.Is(err, fs.ErrNotExist) {
    61  			return false, nil
    62  		}
    63  	} else {
    64  		if errors.Is(err, fs.ErrPermission) {
    65  			return false, nil
    66  		}
    67  	}
    68  	return err == nil, err
    69  }
    70  
    71  func (vfsOS) Open(name string, flags OpenFlag) (File, OpenFlag, error) {
    72  	return nil, 0, _CANTOPEN
    73  }
    74  
    75  func (vfsOS) OpenFilename(name *Filename, flags OpenFlag) (File, OpenFlag, error) {
    76  	var oflags int
    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  	var err error
    91  	var f *os.File
    92  	if name == nil {
    93  		f, err = os.CreateTemp("", "*.db")
    94  	} else {
    95  		f, err = osutil.OpenFile(name.String(), oflags, 0666)
    96  	}
    97  	if err != nil {
    98  		if errors.Is(err, syscall.EISDIR) {
    99  			return nil, flags, _CANTOPEN_ISDIR
   100  		}
   101  		return nil, flags, err
   102  	}
   103  
   104  	if modeof := name.URIParameter("modeof"); modeof != "" {
   105  		if err = osSetMode(f, modeof); err != nil {
   106  			f.Close()
   107  			return nil, flags, _IOERR_FSTAT
   108  		}
   109  	}
   110  	if flags&OPEN_DELETEONCLOSE != 0 {
   111  		os.Remove(f.Name())
   112  	}
   113  
   114  	file := vfsFile{
   115  		File:     f,
   116  		psow:     true,
   117  		readOnly: flags&OPEN_READONLY != 0,
   118  		syncDir: runtime.GOOS != "windows" &&
   119  			flags&(OPEN_CREATE) != 0 &&
   120  			flags&(OPEN_MAIN_JOURNAL|OPEN_SUPER_JOURNAL|OPEN_WAL) != 0,
   121  		shm: NewSharedMemory(name.String()+"-shm", flags),
   122  	}
   123  	return &file, flags, nil
   124  }
   125  
   126  type vfsFile struct {
   127  	*os.File
   128  	shm      SharedMemory
   129  	lock     LockLevel
   130  	readOnly bool
   131  	keepWAL  bool
   132  	syncDir  bool
   133  	psow     bool
   134  }
   135  
   136  var (
   137  	// Ensure these interfaces are implemented:
   138  	_ FileLockState          = &vfsFile{}
   139  	_ FileHasMoved           = &vfsFile{}
   140  	_ FileSizeHint           = &vfsFile{}
   141  	_ FilePersistentWAL      = &vfsFile{}
   142  	_ FilePowersafeOverwrite = &vfsFile{}
   143  )
   144  
   145  func (f *vfsFile) Close() error {
   146  	if f.shm != nil {
   147  		f.shm.Close()
   148  	}
   149  	return f.File.Close()
   150  }
   151  
   152  func (f *vfsFile) Sync(flags SyncFlag) error {
   153  	dataonly := (flags & SYNC_DATAONLY) != 0
   154  	fullsync := (flags & 0x0f) == SYNC_FULL
   155  
   156  	err := osSync(f.File, fullsync, dataonly)
   157  	if err != nil {
   158  		return err
   159  	}
   160  	if runtime.GOOS != "windows" && f.syncDir {
   161  		f.syncDir = false
   162  		d, err := os.Open(filepath.Dir(f.File.Name()))
   163  		if err != nil {
   164  			return nil
   165  		}
   166  		defer d.Close()
   167  		err = osSync(d, false, false)
   168  		if err != nil {
   169  			return _IOERR_DIR_FSYNC
   170  		}
   171  	}
   172  	return nil
   173  }
   174  
   175  func (f *vfsFile) Size() (int64, error) {
   176  	return f.Seek(0, io.SeekEnd)
   177  }
   178  
   179  func (f *vfsFile) SectorSize() int {
   180  	return _DEFAULT_SECTOR_SIZE
   181  }
   182  
   183  func (f *vfsFile) DeviceCharacteristics() DeviceCharacteristic {
   184  	var res DeviceCharacteristic
   185  	if osBatchAtomic(f.File) {
   186  		res |= IOCAP_BATCH_ATOMIC
   187  	}
   188  	if f.psow {
   189  		res |= IOCAP_POWERSAFE_OVERWRITE
   190  	}
   191  	return res
   192  }
   193  
   194  func (f *vfsFile) SizeHint(size int64) error {
   195  	return osAllocate(f.File, size)
   196  }
   197  
   198  func (f *vfsFile) HasMoved() (bool, error) {
   199  	fi, err := f.Stat()
   200  	if err != nil {
   201  		return false, err
   202  	}
   203  	pi, err := os.Stat(f.Name())
   204  	if err != nil {
   205  		if errors.Is(err, fs.ErrNotExist) {
   206  			return true, nil
   207  		}
   208  		return false, err
   209  	}
   210  	return !os.SameFile(fi, pi), nil
   211  }
   212  
   213  func (f *vfsFile) LockState() LockLevel            { return f.lock }
   214  func (f *vfsFile) PowersafeOverwrite() bool        { return f.psow }
   215  func (f *vfsFile) PersistentWAL() bool             { return f.keepWAL }
   216  func (f *vfsFile) SetPowersafeOverwrite(psow bool) { f.psow = psow }
   217  func (f *vfsFile) SetPersistentWAL(keepWAL bool)   { f.keepWAL = keepWAL }