Skip to content

Commit 0b93d85

Browse files
committed
Initial commit
Signed-off-by: Gustavo Sampaio <[email protected]>
0 parents  commit 0b93d85

28 files changed

+2139
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
tmp

.vscode/launch.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
{
8+
"name": "Launch",
9+
"type": "go",
10+
"request": "launch",
11+
"mode": "auto",
12+
"program": "${fileDirname}",
13+
"env": {},
14+
"args": [],
15+
"cwd": "${workspaceFolder}"
16+
}
17+
]
18+
}

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"go.formatTool": "goimports"
3+
}

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2019 Runlet
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# 🦀 CrabFS
2+
3+
CrabFS is a distributed (p2p) filesystem using the billy filesystem interface.
4+
5+
See [examples](https://github.com/runletapp/crabfs/tree/master/examples) for a concrete example.
6+
7+
## This is a big WIP. Use with caution and PR's are welcome

Taskfile

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/bin/bash
2+
PATH=./node_modules/.bin:$PATH
3+
4+
function generateProtos {
5+
protoc -I ./protobuf ./protobuf/*.proto --go_out=plugins=grpc:./protos
6+
}
7+
8+
function help {
9+
echo "$0 <task> <args>"
10+
echo "Tasks:"
11+
compgen -A function | cat -n
12+
}
13+
14+
TIMEFORMAT="Task completed in %3lR"
15+
time ${@:-default}

block_slicer.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package crabfs
2+
3+
import (
4+
"io"
5+
6+
blocks "github.com/ipfs/go-block-format"
7+
"github.com/runletapp/crabfs/interfaces"
8+
pb "github.com/runletapp/crabfs/protos"
9+
)
10+
11+
var _ interfaces.Slicer = &BlockSlicer{}
12+
13+
// BlockSlicer a simple block slicer
14+
type BlockSlicer struct {
15+
reader io.Reader
16+
17+
blockSize int64
18+
19+
offset int64
20+
}
21+
22+
// BlockSlicerNew creates a new BlockSlicer with 'blockSize'
23+
func BlockSlicerNew(reader io.Reader, blockSize int64) (interfaces.Slicer, error) {
24+
slicer := &BlockSlicer{
25+
reader: reader,
26+
blockSize: blockSize,
27+
28+
offset: 0,
29+
}
30+
31+
return slicer, nil
32+
}
33+
34+
func (slicer *BlockSlicer) Next() (*pb.BlockMetadata, blocks.Block, error) {
35+
data := make([]byte, slicer.blockSize)
36+
37+
n, err := slicer.reader.Read(data)
38+
if err != nil {
39+
return nil, nil, err
40+
}
41+
42+
block := blocks.NewBlock(data[:n])
43+
44+
metadata := &pb.BlockMetadata{
45+
Start: slicer.offset,
46+
Size: int64(n),
47+
Cid: block.Cid().Bytes(),
48+
}
49+
50+
slicer.offset += int64(n)
51+
52+
return metadata, block, nil
53+
}

crabfs.go

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
package crabfs
2+
3+
import (
4+
"context"
5+
"crypto/rand"
6+
"fmt"
7+
"io"
8+
"time"
9+
10+
"github.com/ipfs/go-cid"
11+
12+
"github.com/runletapp/crabfs/interfaces"
13+
"github.com/runletapp/crabfs/options"
14+
15+
ipfsDatastore "github.com/ipfs/go-datastore"
16+
ipfsDatastoreSync "github.com/ipfs/go-datastore/sync"
17+
ipfsBlockstore "github.com/ipfs/go-ipfs-blockstore"
18+
libp2pCrypto "github.com/libp2p/go-libp2p-crypto"
19+
)
20+
21+
var _ interfaces.Core = &crabFS{}
22+
23+
type crabFS struct {
24+
settings *options.Settings
25+
26+
host interfaces.Host
27+
28+
datastore *ipfsDatastoreSync.MutexDatastore
29+
blockstore ipfsBlockstore.Blockstore
30+
}
31+
32+
// New create a new CrabFS
33+
func New(opts ...options.Option) (interfaces.Core, error) {
34+
settings := options.Settings{}
35+
if err := settings.SetDefaults(); err != nil {
36+
return nil, err
37+
}
38+
39+
for _, opt := range opts {
40+
if err := opt(&settings); err != nil {
41+
return nil, err
42+
}
43+
}
44+
45+
if settings.BlockSize == 0 {
46+
return nil, ErrInvalidBlockSize
47+
}
48+
49+
var privateKey *libp2pCrypto.RsaPrivateKey
50+
var err error
51+
if settings.PrivateKey == nil {
52+
privKey, _, err := libp2pCrypto.GenerateRSAKeyPair(2048, rand.Reader)
53+
if err != nil {
54+
return nil, err
55+
}
56+
privateKey = privKey.(*libp2pCrypto.RsaPrivateKey)
57+
} else {
58+
privateKey, err = ReadPrivateKey(settings.PrivateKey)
59+
if err != nil {
60+
return nil, err
61+
}
62+
}
63+
64+
// TODO: Persistence
65+
datastore := ipfsDatastoreSync.MutexWrap(ipfsDatastore.NewMapDatastore())
66+
67+
blockstore := ipfsBlockstore.NewBlockstore(datastore)
68+
69+
host, err := HostNew(&settings, privateKey, datastore, blockstore)
70+
if err != nil {
71+
return nil, err
72+
}
73+
74+
if !settings.RelayOnly {
75+
if err := host.Announce(); err != nil {
76+
return nil, err
77+
}
78+
}
79+
80+
fs := &crabFS{
81+
settings: &settings,
82+
83+
host: host,
84+
85+
datastore: datastore,
86+
87+
blockstore: blockstore,
88+
}
89+
90+
return fs, nil
91+
}
92+
93+
func (fs *crabFS) Get(ctx context.Context, filename string) (io.ReadSeeker, int64, error) {
94+
blockMap, err := fs.host.GetContent(ctx, filename)
95+
if err != nil {
96+
return nil, 0, err
97+
}
98+
99+
fetcher, err := BasicFetcherNew(ctx, fs, blockMap)
100+
if err != nil {
101+
return nil, 0, err
102+
}
103+
104+
return fetcher, fetcher.Size(), nil
105+
}
106+
107+
func (fs *crabFS) Put(ctx context.Context, filename string, file io.Reader, mtime time.Time) error {
108+
slicer, err := BlockSlicerNew(file, fs.settings.BlockSize)
109+
if err != nil {
110+
return err
111+
}
112+
113+
blockMap := interfaces.BlockMap{}
114+
totalSize := int64(0)
115+
116+
blockMeta, block, err := slicer.Next()
117+
for {
118+
if err != nil && err != io.EOF {
119+
return err
120+
}
121+
122+
if block == nil {
123+
break
124+
}
125+
126+
if err := fs.blockstore.Put(block); err != nil {
127+
return err
128+
}
129+
130+
blockMap[blockMeta.Start] = blockMeta
131+
totalSize += blockMeta.Size
132+
133+
// Process block
134+
blockMeta, block, err = slicer.Next()
135+
}
136+
137+
return fs.host.Publish(ctx, filename, blockMap, mtime, totalSize)
138+
}
139+
140+
func (fs *crabFS) Remove(ctx context.Context, filename string) error {
141+
blockMap, err := fs.host.GetContent(ctx, filename)
142+
if err != nil {
143+
return err
144+
}
145+
146+
for _, blockMeta := range blockMap {
147+
cid, _ := cid.Cast(blockMeta.Cid)
148+
149+
if err := fs.blockstore.DeleteBlock(cid); err != nil {
150+
fmt.Printf("Could not delete block: %s", cid.String())
151+
}
152+
}
153+
154+
return nil
155+
}
156+
157+
func (fs *crabFS) GetID() string {
158+
return fs.host.GetID()
159+
}
160+
161+
func (fs *crabFS) GetAddrs() []string {
162+
return fs.host.GetAddrs()
163+
}

dht_namespace_pk_validator.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package crabfs
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
libp2pCrypto "github.com/libp2p/go-libp2p-crypto"
8+
libp2pRecord "github.com/libp2p/go-libp2p-record"
9+
multihash "github.com/multiformats/go-multihash"
10+
)
11+
12+
// DHTNamespacePKValidatorNew creates a new validator that validates for all versions
13+
func DHTNamespacePKValidatorNew() libp2pRecord.Validator {
14+
return DHTNamespacePKValidatorV1{}
15+
}
16+
17+
// DHTNamespacePKValidatorV1 validates the /crabfs keys on the dht datastore
18+
type DHTNamespacePKValidatorV1 struct {
19+
}
20+
21+
// Validate validates the given record, returning an error if it's
22+
// invalid (e.g., expired, signed by the wrong key, etc.).
23+
func (validator DHTNamespacePKValidatorV1) Validate(key string, value []byte) error {
24+
parts := strings.Split(key, "/")
25+
26+
if len(parts) != 3 {
27+
return fmt.Errorf("Invalid key. Expexted format: /crabfs_pk/<hash> Got: %v", key)
28+
}
29+
30+
if _, err := libp2pCrypto.UnmarshalRsaPublicKey(value); err != nil {
31+
return err
32+
}
33+
34+
pskHash, err := multihash.Sum(value, multihash.SHA3_256, -1)
35+
if err != nil {
36+
return err
37+
}
38+
39+
expectedKey := fmt.Sprintf("/crabfs_pk/%s", pskHash.String())
40+
if !strings.HasPrefix(key, expectedKey) {
41+
return fmt.Errorf("Invalid key. Expexted: %s Got: %v", expectedKey, key)
42+
}
43+
44+
return nil
45+
}
46+
47+
// Select selects the best record from the set of records (e.g., the
48+
// newest).
49+
//
50+
// Decisions made by select should be stable.
51+
func (validator DHTNamespacePKValidatorV1) Select(key string, values [][]byte) (int, error) {
52+
if len(values) == 0 {
53+
return 0, fmt.Errorf("No values")
54+
}
55+
56+
return 0, nil
57+
}

0 commit comments

Comments
 (0)