mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-04 09:02:25 -06:00 
			
		
		
		
	
		
			
				
	
	
		
			157 lines
		
	
	
	
		
			3.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			157 lines
		
	
	
	
		
			3.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package vfs
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"context"
 | 
						|
	_ "embed"
 | 
						|
	"encoding/binary"
 | 
						|
	"strconv"
 | 
						|
 | 
						|
	"github.com/tetratelabs/wazero/api"
 | 
						|
 | 
						|
	"github.com/ncruces/go-sqlite3/internal/util"
 | 
						|
	"github.com/ncruces/go-sqlite3/util/sql3util"
 | 
						|
)
 | 
						|
 | 
						|
func cksmWrapFile(name *Filename, flags OpenFlag, file File) File {
 | 
						|
	// Checksum only main databases and WALs.
 | 
						|
	if flags&(OPEN_MAIN_DB|OPEN_WAL) == 0 {
 | 
						|
		return file
 | 
						|
	}
 | 
						|
 | 
						|
	cksm := cksmFile{File: file}
 | 
						|
 | 
						|
	if flags&OPEN_WAL != 0 {
 | 
						|
		main, _ := name.DatabaseFile().(cksmFile)
 | 
						|
		cksm.cksmFlags = main.cksmFlags
 | 
						|
	} else {
 | 
						|
		cksm.cksmFlags = new(cksmFlags)
 | 
						|
		cksm.isDB = true
 | 
						|
	}
 | 
						|
 | 
						|
	return cksm
 | 
						|
}
 | 
						|
 | 
						|
type cksmFile struct {
 | 
						|
	File
 | 
						|
	*cksmFlags
 | 
						|
	isDB bool
 | 
						|
}
 | 
						|
 | 
						|
type cksmFlags struct {
 | 
						|
	computeCksm bool
 | 
						|
	verifyCksm  bool
 | 
						|
	inCkpt      bool
 | 
						|
	pageSize    int
 | 
						|
}
 | 
						|
 | 
						|
func (c cksmFile) ReadAt(p []byte, off int64) (n int, err error) {
 | 
						|
	n, err = c.File.ReadAt(p, off)
 | 
						|
 | 
						|
	// SQLite is reading the header of a database file.
 | 
						|
	if c.isDB && off == 0 && len(p) >= 100 &&
 | 
						|
		bytes.HasPrefix(p, []byte("SQLite format 3\000")) {
 | 
						|
		c.init(p)
 | 
						|
	}
 | 
						|
 | 
						|
	// Verify checksums.
 | 
						|
	if c.verifyCksm && !c.inCkpt && len(p) == c.pageSize {
 | 
						|
		cksm1 := cksmCompute(p[:len(p)-8])
 | 
						|
		cksm2 := *(*[8]byte)(p[len(p)-8:])
 | 
						|
		if cksm1 != cksm2 {
 | 
						|
			return 0, _IOERR_DATA
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return n, err
 | 
						|
}
 | 
						|
 | 
						|
func (c cksmFile) WriteAt(p []byte, off int64) (n int, err error) {
 | 
						|
	// SQLite is writing the first page of a database file.
 | 
						|
	if c.isDB && off == 0 && len(p) >= 100 &&
 | 
						|
		bytes.HasPrefix(p, []byte("SQLite format 3\000")) {
 | 
						|
		c.init(p)
 | 
						|
	}
 | 
						|
 | 
						|
	// Compute checksums.
 | 
						|
	if c.computeCksm && !c.inCkpt && len(p) == c.pageSize {
 | 
						|
		*(*[8]byte)(p[len(p)-8:]) = cksmCompute(p[:len(p)-8])
 | 
						|
	}
 | 
						|
 | 
						|
	return c.File.WriteAt(p, off)
 | 
						|
}
 | 
						|
 | 
						|
func (c cksmFile) Pragma(name string, value string) (string, error) {
 | 
						|
	switch name {
 | 
						|
	case "checksum_verification":
 | 
						|
		b, ok := sql3util.ParseBool(value)
 | 
						|
		if ok {
 | 
						|
			c.verifyCksm = b && c.computeCksm
 | 
						|
		}
 | 
						|
		if !c.verifyCksm {
 | 
						|
			return "0", nil
 | 
						|
		}
 | 
						|
		return "1", nil
 | 
						|
 | 
						|
	case "page_size":
 | 
						|
		if c.computeCksm {
 | 
						|
			// Do not allow page size changes on a checksum database.
 | 
						|
			return strconv.Itoa(c.pageSize), nil
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return "", _NOTFOUND
 | 
						|
}
 | 
						|
 | 
						|
func (c cksmFile) DeviceCharacteristics() DeviceCharacteristic {
 | 
						|
	ret := c.File.DeviceCharacteristics()
 | 
						|
	if c.verifyCksm {
 | 
						|
		ret &^= IOCAP_SUBPAGE_READ
 | 
						|
	}
 | 
						|
	return ret
 | 
						|
}
 | 
						|
 | 
						|
func (c cksmFile) fileControl(ctx context.Context, mod api.Module, op _FcntlOpcode, pArg ptr_t) _ErrorCode {
 | 
						|
	switch op {
 | 
						|
	case _FCNTL_CKPT_START:
 | 
						|
		c.inCkpt = true
 | 
						|
	case _FCNTL_CKPT_DONE:
 | 
						|
		c.inCkpt = false
 | 
						|
	}
 | 
						|
	if rc := vfsFileControlImpl(ctx, mod, c, op, pArg); rc != _NOTFOUND {
 | 
						|
		return rc
 | 
						|
	}
 | 
						|
	return vfsFileControlImpl(ctx, mod, c.File, op, pArg)
 | 
						|
}
 | 
						|
 | 
						|
func (f *cksmFlags) init(header []byte) {
 | 
						|
	f.pageSize = 256 * int(binary.LittleEndian.Uint16(header[16:18]))
 | 
						|
	if r := header[20] == 8; r != f.computeCksm {
 | 
						|
		f.computeCksm = r
 | 
						|
		f.verifyCksm = r
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func cksmCompute(a []byte) (cksm [8]byte) {
 | 
						|
	var s1, s2 uint32
 | 
						|
	for len(a) >= 8 {
 | 
						|
		s1 += binary.LittleEndian.Uint32(a[0:4]) + s2
 | 
						|
		s2 += binary.LittleEndian.Uint32(a[4:8]) + s1
 | 
						|
		a = a[8:]
 | 
						|
	}
 | 
						|
	if len(a) != 0 {
 | 
						|
		panic(util.AssertErr())
 | 
						|
	}
 | 
						|
	binary.LittleEndian.PutUint32(cksm[0:4], s1)
 | 
						|
	binary.LittleEndian.PutUint32(cksm[4:8], s2)
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
func (c cksmFile) SharedMemory() SharedMemory {
 | 
						|
	if f, ok := c.File.(FileSharedMemory); ok {
 | 
						|
		return f.SharedMemory()
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (c cksmFile) Unwrap() File {
 | 
						|
	return c.File
 | 
						|
}
 |