Skip to content

Commit

Permalink
FS implementation based on Cloud File API
Browse files Browse the repository at this point in the history
  • Loading branch information
balazsgrill committed Aug 10, 2024
1 parent f1e97a1 commit 53d8e8f
Show file tree
Hide file tree
Showing 19 changed files with 1,922 additions and 50 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
go-version: '1.21'

- name: Test
run: go test -v ./...
run: go test -v ./..

- name: Build
run: go build -o potatodrive.exe ./cmd/main
Expand Down
14 changes: 12 additions & 2 deletions bindings/bindings.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@ import (
"syscall"
"time"

"github.com/balazsgrill/potatodrive/win/projfs/filesystem"
"github.com/balazsgrill/potatodrive/win"
cfapi "github.com/balazsgrill/potatodrive/win/cfapi/filesystem"
prjfs "github.com/balazsgrill/potatodrive/win/projfs/filesystem"
"github.com/spf13/afero"
"golang.org/x/sys/windows/registry"
)

const UseCFAPI bool = true

func ConfigToFlags(config any) {
structPtrValue := reflect.ValueOf(config)
structValue := structPtrValue.Elem()
Expand Down Expand Up @@ -90,7 +94,13 @@ func (f closerFunc) Close() error {
}

func BindVirtualizationInstance(localpath string, remotefs afero.Fs) (io.Closer, error) {
closer, err := filesystem.StartProjecting(localpath, remotefs)
var closer win.Virtualization
var err error
if UseCFAPI {
closer, err = cfapi.StartProjecting(localpath, remotefs)
} else {
closer, err = prjfs.StartProjecting(localpath, remotefs)
}
if err != nil {
return nil, err
}
Expand Down
106 changes: 106 additions & 0 deletions win/cfapi/callbacks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package cfapi

import "syscall"
import "C"

type Callback_FetchData func(*CF_CALLBACK_INFO, *CF_CALLBACK_PARAMETERS_FetchData) uintptr
type Callback_ValidateData func(*CF_CALLBACK_INFO, *CF_CALLBACK_PARAMETERS_ValidateData) uintptr
type Callback_CancelFetchData func(*CF_CALLBACK_INFO, *CF_CALLBACK_PARAMETERS_Cancel) uintptr
type Callback_FetchPlaceholders func(*CF_CALLBACK_INFO, *CF_CALLBACK_PARAMETERS_FetchPlaceholders) uintptr
type Callback_CancelFetchPlaceholders func(*CF_CALLBACK_INFO, *CF_CALLBACK_PARAMETERS_Cancel) uintptr
type Callback_OpenCompletion func(*CF_CALLBACK_INFO, *CF_CALLBACK_PARAMETERS_OpenCompletion) uintptr
type Callback_CloseCompletion func(*CF_CALLBACK_INFO, *CF_CALLBACK_PARAMETERS_CloseCompletion) uintptr
type Callback_Dehydrate func(*CF_CALLBACK_INFO, *CF_CALLBACK_PARAMETERS_Dehydrate) uintptr
type Callback_DehydrateCompletion func(*CF_CALLBACK_INFO, *CF_CALLBACK_PARAMETERS_DehydrateCompletion) uintptr
type Callback_Delete func(*CF_CALLBACK_INFO, *CF_CALLBACK_PARAMETERS_Delete) uintptr
type Callback_DeleteCompletion func(*CF_CALLBACK_INFO, *CF_CALLBACK_PARAMETERS_DeleteCompletion) uintptr
type Callback_Rename func(*CF_CALLBACK_INFO, *CF_CALLBACK_PARAMETERS_Rename) uintptr
type Callback_RenameCompletion func(*CF_CALLBACK_INFO, *CF_CALLBACK_PARAMETERS_RenameCompletion) uintptr

type Callbacks struct {
FetchData Callback_FetchData
ValidateData Callback_ValidateData
CancelFetchData Callback_CancelFetchData
FetchPlaceholders Callback_FetchPlaceholders
CancelFetchPlaceholders Callback_CancelFetchPlaceholders
OpenCompletion Callback_OpenCompletion
CloseCompletion Callback_CloseCompletion
Dehydrate Callback_Dehydrate
DehydrateCompletion Callback_DehydrateCompletion
Delete Callback_Delete
DeleteCompletion Callback_DeleteCompletion
Rename Callback_Rename
RenameCompletion Callback_RenameCompletion
}

func (cb *Callbacks) CreateCallbackTable() []CF_CALLBACK_REGISTRATION {
result := make([]CF_CALLBACK_REGISTRATION, 14)
count := 0
if cb.FetchData != nil {
result[count].Callback = syscall.NewCallback(cb.FetchData)
result[count].Type = CF_CALLBACK_TYPE_FETCH_DATA
count++
}
if cb.ValidateData != nil {
result[count].Callback = syscall.NewCallback(cb.ValidateData)
result[count].Type = CF_CALLBACK_TYPE_VALIDATE_DATA
count++
}
if cb.CancelFetchData != nil {
result[count].Callback = syscall.NewCallback(cb.CancelFetchData)
result[count].Type = CF_CALLBACK_TYPE_CANCEL_FETCH_DATA
count++
}
if cb.FetchPlaceholders != nil {
result[count].Callback = syscall.NewCallback(cb.FetchPlaceholders)
result[count].Type = CF_CALLBACK_TYPE_FETCH_PLACEHOLDERS
count++
}
if cb.CancelFetchPlaceholders != nil {
result[count].Callback = syscall.NewCallback(cb.CancelFetchPlaceholders)
result[count].Type = CF_CALLBACK_TYPE_CANCEL_FETCH_PLACEHOLDERS
count++
}
if cb.OpenCompletion != nil {
result[count].Callback = syscall.NewCallback(cb.OpenCompletion)
result[count].Type = CF_CALLBACK_TYPE_NOTIFY_FILE_OPEN_COMPLETION
count++
}
if cb.CloseCompletion != nil {
result[count].Callback = syscall.NewCallback(cb.CloseCompletion)
result[count].Type = CF_CALLBACK_TYPE_NOTIFY_FILE_CLOSE_COMPLETION
count++
}
if cb.Dehydrate != nil {
result[count].Callback = syscall.NewCallback(cb.Dehydrate)
result[count].Type = CF_CALLBACK_TYPE_NOTIFY_DEHYDRATE
count++
}
if cb.DehydrateCompletion != nil {
result[count].Callback = syscall.NewCallback(cb.DehydrateCompletion)
result[count].Type = CF_CALLBACK_TYPE_NOTIFY_DEHYDRATE_COMPLETION
count++
}
if cb.Delete != nil {
result[count].Callback = syscall.NewCallback(cb.Delete)
result[count].Type = CF_CALLBACK_TYPE_NOTIFY_DELETE
count++
}
if cb.DeleteCompletion != nil {
result[count].Callback = syscall.NewCallback(cb.DeleteCompletion)
result[count].Type = CF_CALLBACK_TYPE_NOTIFY_DELETE_COMPLETION
count++
}
if cb.Rename != nil {
result[count].Callback = syscall.NewCallback(cb.Rename)
result[count].Type = CF_CALLBACK_TYPE_NOTIFY_RENAME
count++
}
if cb.RenameCompletion != nil {
result[count].Callback = syscall.NewCallback(cb.RenameCompletion)
result[count].Type = CF_CALLBACK_TYPE_NOTIFY_RENAME_COMPLETION
count++
}
result[count].Type = CF_CALLBACK_TYPE_NONE
return result
}
16 changes: 16 additions & 0 deletions win/cfapi/cfapi_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package cfapi_test

import (
"testing"

"github.com/balazsgrill/potatodrive/win/cfapi"
)

func Test_PlatformVersion(t *testing.T) {
var platforminfo cfapi.CF_PLATFORM_INFO
hr := cfapi.CfGetPlatformInfo(&platforminfo)
if hr != 0 {
t.Fatal(hr)
}
t.Logf("platform version: b%d i%d, r%d", platforminfo.BuildNumber, platforminfo.IntegrationNumber, platforminfo.RevisionNumber)
}
69 changes: 69 additions & 0 deletions win/cfapi/filesystem/fetchdata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package filesystem

import (
"io"
"log"
"syscall"
"unsafe"

"github.com/balazsgrill/potatodrive/win"
"github.com/balazsgrill/potatodrive/win/cfapi"
)

func (instance *VirtualizationInstance) callback_getRemoteFilePath(info *cfapi.CF_CALLBACK_INFO) string {
return instance.path_localToRemote(win.GetString(info.VolumeDosName) + win.GetString(info.NormalizedPath))
}

func (instance *VirtualizationInstance) fetchData(info *cfapi.CF_CALLBACK_INFO, data *cfapi.CF_CALLBACK_PARAMETERS_FetchData) uintptr {
instance.lock.Lock()
defer instance.lock.Unlock()
filename := instance.callback_getRemoteFilePath(info)
length := data.RequiredLength
byteOffset := data.RequiredFileOffset
if length == 0 || length < 0 {
length = info.FileSize
}
if data.OptionalLength > data.RequiredLength {
length = data.OptionalLength
byteOffset = data.OptionalFileOffset
}
log.Printf("Fetch data: %s %d bytes at %d", filename, length, byteOffset)
log.Printf("Optional %d at %d", data.OptionalLength, data.OptionalFileOffset)
file, err := instance.fs.Open(filename)
if err != nil {
log.Printf("Error opening file %s: %s", filename, err)
return uintptr(syscall.EIO)
}
defer file.Close()
buffer := make([]byte, length)

var n int
var count int64
for count < length {
n, err = file.ReadAt(buffer[count:], byteOffset+count)
count += int64(n)
if err == io.EOF {
err = nil
break
}
}

log.Printf("Read %d bytes", count)
if err != nil {
log.Printf("Error reading file %s: %s", filename, err)
return uintptr(syscall.EIO)
}

var transfer cfapi.CF_OPERATION_PARAMETERS_TransferData
transfer.Buffer = uintptr(unsafe.Pointer(&buffer[0]))
transfer.Length = count
transfer.Offset = byteOffset
transfer.ParamSize = uint32(unsafe.Sizeof(transfer))
transfer.Flags = cfapi.CF_OPERATION_TRANSFER_DATA_FLAG_NONE
hr := instance.transferData(info, &transfer)
if hr != 0 {
log.Printf("Error transferring data: %s", win.ErrorByCode(hr))
return hr
}
return 0
}
Loading

0 comments on commit 53d8e8f

Please sign in to comment.