Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ For support and discussion, see [![Gitter chat](https://badges.gitter.im/desync-

Among the distinguishing factors:

- Supported on MacOS, though there could be incompatibilities when exchanging catar-files between Linux and Mac for example since devices and filemodes differ slightly. \*BSD should work as well but hasn't been tested. Windows supports a subset of commands.
- Supported on MacOS, though there could be incompatibilities when exchanging catar-files between Linux, Mac and Windows for example since devices and filemodes differ slightly. \*BSD should work as well but hasn't been tested.
- Where the upstream command has chosen to optimize for storage efficiency (f/e, being able to use local files as "seeds", building temporary indexes into them), this command chooses to optimize for runtime performance (maintaining a local explicit chunk store, avoiding the need to reindex) at cost to storage efficiency.
- Where the upstream command has chosen to take full advantage of Linux platform features, this client chooses to implement a minimum featureset and, while high-value platform-specific features (such as support for btrfs reflinks into a decompressed local chunk cache) might be added in the future, the ability to build without them on other platforms will be maintained.
- Both, SHA512/256 and SHA256 are supported hash functions.
Expand Down Expand Up @@ -73,12 +73,21 @@ The tool is provided for convenience. It uses the desync library and makes most

### Installation

If GOPATH is set correctly, building the tool and installing it into `$GOPATH/bin` can be done with:
#### Linux

```text
go get -u github.com/folbricht/desync/cmd/desync
```

#### Windows

Windows builds require a gcc compiler in PATH (for example [http://mingw-w64.org/](http://mingw-w64.org/)) and [WinFsp](https://github.com/billziss-gh/winfsp).

```text
set CPATH=C:\Program Files (x86)\WinFsp\inc\fuse
go get -u github.com/folbricht/desync/cmd/desync
```

### Subcommands

- `extract` - build a blob from an index file, optionally using seed indexes+blobs
Expand Down
2 changes: 0 additions & 2 deletions cmd/desync/mount-index.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// +build !windows

package main

import (
Expand Down
18 changes: 0 additions & 18 deletions cmd/desync/mount-index_windows.go

This file was deleted.

1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/folbricht/desync
go 1.11

require (
github.com/billziss-gh/cgofuse v1.2.0
github.com/datadog/zstd v1.4.4
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dchest/siphash v1.2.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/billziss-gh/cgofuse v1.2.0 h1:FMdQSygSBpD4yEPENJcmvfCdmNWMVkPLlD7wWdl/7IA=
github.com/billziss-gh/cgofuse v1.2.0/go.mod h1:LJjoaUojlVjgo5GQoEJTcJNqZJeRU0nCR84CyxKt2YM=
github.com/datadog/zstd v1.4.4 h1:VXuvuDMxP8fBcMt66fD2Z4LSC+VmNIhm59jslWe9J5o=
github.com/datadog/zstd v1.4.4/go.mod h1:inRp+etsHuvVqMPNTXaFlpf/Tj7wqviBtdJoPVrPEFQ=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
Expand Down
132 changes: 132 additions & 0 deletions mount-index_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package desync

import (
"context"
"fmt"
"io"
"os"
"sync"

"github.com/billziss-gh/cgofuse/fuse"
)

// IndexMountFS is used to FUSE mount an index file (as a blob, not an archive).
// It present a single file underneath the mountpoint.
type IndexMountFS struct {
fuse.FileSystemBase

FName string // File name in the mountpoint
Idx Index // Index of the blob
Store Store

mu sync.Mutex
handles map[uint64]*indexFileHandle
handleCounter uint64
}

// NewIndexMountFS initializes a FUSE filesystem mount based on an index and a chunk store.
func NewIndexMountFS(idx Index, name string, s Store) *IndexMountFS {
return &IndexMountFS{
FName: name,
Idx: idx,
Store: s,
handles: make(map[uint64]*indexFileHandle),
}
}

func (fs *IndexMountFS) Open(path string, flags int) (errc int, fh uint64) {
if path != "/"+fs.FName {
return -fuse.ENOENT, ^uint64(0)
}
fs.mu.Lock()
defer fs.mu.Unlock()
fs.handleCounter++
fs.handles[fs.handleCounter] = newIndexFileHandle(fs.Idx, fs.Store)
return 0, fs.handleCounter
}

func (fs *IndexMountFS) Release(path string, fh uint64) int {
fs.mu.Lock()
defer fs.mu.Unlock()
_, ok := fs.handles[fh]
if !ok {
return -fuse.ENOSYS
}
delete(fs.handles, fh)
return 0
}

func (fs *IndexMountFS) Getattr(path string, stat *fuse.Stat_t, fh uint64) (errc int) {
switch path {
case "/":
stat.Mode = fuse.S_IFDIR | 0555
return 0
case "/" + fs.FName:
stat.Mode = fuse.S_IFREG | 0444
stat.Size = fs.Idx.Length()
return 0
default:
return -fuse.ENOENT
}
}

func (fs *IndexMountFS) Read(path string, b []byte, offset int64, fh uint64) (n int) {
fs.mu.Lock()
f, ok := fs.handles[fh]
fs.mu.Unlock()
if !ok {
return 0 // apparently this means error??? The method has no dedicated error
}
n, err := f.read(b, offset)
if err != nil { // don't ignore the error
fmt.Fprintf(os.Stderr, "error reading: %v", err)
}
return n
}

func (fs *IndexMountFS) Readdir(path string, fill func(name string, stat *fuse.Stat_t, ofst int64) bool, offset int64, fh uint64) (errc int) {
fill(".", nil, 0)
fill("..", nil, 0)
fill(fs.FName, nil, 0)
return 0
}

// indexFileHandle represents a (read-only) file handle on a blob in a FUSE mounted filesystem
type indexFileHandle struct {
r *IndexPos

// perhaps not needed, but in case something is trying to use the same filehandle concurrently
mu sync.Mutex
}

// NewIndexMountFile initializes a blob file opened in a FUSE mount.
func newIndexFileHandle(idx Index, s Store) *indexFileHandle {
return &indexFileHandle{
r: NewIndexReadSeeker(idx, s),
}
}

// read from a blob file in a FUSE mount.
func (f *indexFileHandle) read(dest []byte, off int64) (int, error) {
f.mu.Lock()
defer f.mu.Unlock()
if _, err := f.r.Seek(off, io.SeekStart); err != nil {
fmt.Fprintln(os.Stderr, err)
return 0, err
}
n, err := f.r.Read(dest)
if err != nil && err != io.EOF {
fmt.Fprintln(os.Stderr, err)
return 0, err
}
return n, nil
}

// MountIndex mounts an index file under a FUSE mount point. The mount will only expose a single
// blob file as represented by the index.
func MountIndex(ctx context.Context, idx Index, path, name string, s Store, n int) error {
ifs := NewIndexMountFS(idx, name, s)
host := fuse.NewFileSystemHost(ifs)
host.Mount(path, nil)
return nil
}