Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
W
wendelin.core
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Joshua
wendelin.core
Commits
46e6f6a0
Commit
46e6f6a0
authored
Oct 15, 2018
by
Kirill Smelkov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
.
parent
7f4eb022
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
218 additions
and
69 deletions
+218
-69
wcfs/misc.go
wcfs/misc.go
+61
-0
wcfs/wcfs.go
wcfs/wcfs.go
+157
-69
No files found.
wcfs/misc.go
View file @
46e6f6a0
...
...
@@ -22,13 +22,74 @@ package main
import
(
"context"
"fmt"
"syscall"
log
"github.com/golang/glog"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/fuse/nodefs"
"github.com/pkg/errors"
"lab.nexedi.com/kirr/neo/go/zodb"
)
// // errors as fuse.Status (not already exposed by fuse iteself)
// const (
// fEEXIST = fuse.Status(syscall.EEXIST)
// )
// eInvalError is the error wrapper signifying that underlying error is about "invalid argument".
type
eInvalError
struct
{
err
error
}
func
(
e
*
eInvalError
)
Error
()
string
{
return
"invalid argument: "
+
e
.
err
.
Error
()
}
// don't propagate eInvalError.Cause -> e.err
func
eINVAL
(
err
error
)
*
eInvalError
{
return
&
eInvalError
{
err
}
}
func
eINVALf
(
format
string
,
argv
...
interface
{})
*
eInvalError
{
return
eINVAL
(
fmt
.
Errorf
(
format
,
argv
...
))
}
// err2LogStatus converts an error into FUSE status code and logs it appropriately.
//
// the error is logged because otherwise, if e.g. returning EINVAL or EIO
// codes, there is no more detail except the error code itself.
func
err2LogStatus
(
err
error
)
fuse
.
Status
{
// no error
if
err
==
nil
{
return
fuse
.
OK
}
// direct usage of error code - don't log
ecode
,
iscode
:=
err
.
(
syscall
.
Errno
)
if
iscode
{
return
fuse
.
Status
(
ecode
)
}
// otherwise log as warnings EINVAL and as errors everything else
var
st
fuse
.
Status
switch
errors
.
Cause
(
err
)
.
(
type
)
{
case
*
eInvalError
:
st
=
fuse
.
EINVAL
log
.
Warning
(
err
)
default
:
st
=
fuse
.
EIO
log
.
Error
(
err
)
}
return
st
}
// asctx represents fuse context as context.Context ready for interrupt handling.
//
// XXX temp. only after proper interrupt handling is not yet merged into go-fuse.
...
...
wcfs/wcfs.go
View file @
46e6f6a0
...
...
@@ -257,6 +257,7 @@ import (
"flag"
stdlog
"log"
"os"
"strings"
"sync"
"syscall"
...
...
@@ -264,6 +265,7 @@ import (
"golang.org/x/sync/errgroup"
"lab.nexedi.com/kirr/go123/xcontext"
"lab.nexedi.com/kirr/go123/xerr"
"lab.nexedi.com/kirr/neo/go/transaction"
"lab.nexedi.com/kirr/neo/go/zodb"
...
...
@@ -288,12 +290,16 @@ type BigFileRoot struct {
// /bigfile/<bigfileX>/ - served by BigFileDir.
type
BigFileDir
struct
{
nodefs
.
Node
oid
zodb
.
Oid
// oid of ZBigFile
// ZODB DB handle for this bigfile.
// keeps cache of connections for both head/ and @<rev>/ accesses.
zdb
*
zodb
.
DB
// head/ is implicitly linked to by fs
// {} rev -> @<rev>/ bigfile snapshot
revMu
sync
.
Mutex
mu
sync
.
Mutex
revTab
map
[
zodb
.
Tid
]
*
BigFileRev
}
...
...
@@ -350,30 +356,7 @@ type blkLoadState struct {
// ----------------------------------------
// /bigfile -> Mkdir receives client request to create /bigfile/<bigfileX>.
//
// It creates <bigfileX>/head/* along the way.
func
(
bfroot
*
BigFileRoot
)
Mkdir
(
name
string
,
mode
uint32
,
fctx
*
fuse
.
Context
)
(
_
*
nodefs
.
Inode
,
status
fuse
.
Status
)
{
oid
,
err
:=
zodb
.
ParseOid
(
name
)
if
err
!=
nil
{
log
.
Warningf
(
"/bigfile: mkdir %q: not-oid"
,
name
)
return
nil
,
fuse
.
EINVAL
}
// XXX ok to ignore mode?
// check to see if dir(oid) is already there
bfroot
.
mu
.
Lock
()
_
,
already
:=
bfroot
.
tab
[
oid
]
bfroot
.
mu
.
Unlock
()
if
already
{
return
nil
,
fuse
.
Status
(
syscall
.
EEXIST
)
}
// not there - without bfroot lock proceed to load corresponding objects from ZODB:
/*
// create new read-only transaction for this bigfile
txn, txnCtx := transaction.New(context.Background())
defer func() {
...
...
@@ -432,6 +415,48 @@ func (bfroot *BigFileRoot) Mkdir(name string, mode uint32, fctx *fuse.Context) (
log.Errorf("/bigfile: mkdir %q: %s", name, err)
return nil, fuse.EIO
}
*/
// /bigfile -> Mkdir receives client request to create /bigfile/<bigfileX>.
//
// It creates <bigfileX>/head/* along the way.
func
(
bfroot
*
BigFileRoot
)
Mkdir
(
name
string
,
mode
uint32
,
fctx
*
fuse
.
Context
)
(
_
*
nodefs
.
Inode
,
status
fuse
.
Status
)
{
// XXX ok to ignore mode?
inode
,
err
:=
bfroot
.
mkdir
(
name
,
fctx
)
return
inode
,
err2LogStatus
(
err
)
}
func
(
bfroot
*
BigFileRoot
)
mkdir
(
name
string
,
fctx
*
fuse
.
Context
)
(
_
*
nodefs
.
Inode
,
err
error
)
{
defer
xerr
.
Contextf
(
&
err
,
"/bigfile: mkdir %q"
,
name
)
oid
,
err
:=
zodb
.
ParseOid
(
name
)
if
err
!=
nil
{
return
nil
,
eINVALf
(
"not oid"
)
}
// check to see if dir(oid) is already there
bfroot
.
mu
.
Lock
()
_
,
already
:=
bfroot
.
tab
[
oid
]
bfroot
.
mu
.
Unlock
()
if
already
{
return
nil
,
syscall
.
EEXIST
}
// not there - without bfroot lock proceed to load corresponding objects from ZODB
zdb
:=
zodb
.
NewDB
(
bfroot
.
zstor
)
bf
,
err
:=
bigopen
(
asctx
(
fctx
),
zdb
,
oid
,
&
zodb
.
ConnOptions
{})
if
err
!=
nil
{
return
nil
,
err
}
defer
func
()
{
if
err
!=
nil
{
bf
.
Close
()
}
}()
// relock bfroot and either mkdir or EEXIST if the directory was maybe
// simultanously created while we were not holding bfroot.mu
...
...
@@ -439,11 +464,12 @@ func (bfroot *BigFileRoot) Mkdir(name string, mode uint32, fctx *fuse.Context) (
_
,
already
=
bfroot
.
tab
[
oid
]
if
already
{
bfroot
.
mu
.
Unlock
()
return
nil
,
fuse
.
Status
(
syscall
.
EEXIST
)
return
nil
,
syscall
.
EEXIST
}
bfdir
:=
&
BigFileDir
{
Node
:
nodefs
.
NewDefaultNode
(),
oid
:
oid
,
zdb
:
zdb
,
}
...
...
@@ -451,13 +477,6 @@ func (bfroot *BigFileRoot) Mkdir(name string, mode uint32, fctx *fuse.Context) (
Node
:
nodefs
.
NewDefaultNode
(),
}
bf
:=
&
BigFile
{
txnCtx
:
txnCtx
,
zconn
:
zconn
,
zbf
:
zbf
,
zbfSize
:
zbfSize
,
}
bfdata
:=
&
BigFileData
{
Node
:
nodefs
.
NewDefaultNode
(),
bigfile
:
bf
,
...
...
@@ -474,16 +493,22 @@ func (bfroot *BigFileRoot) Mkdir(name string, mode uint32, fctx *fuse.Context) (
mkfile
(
bfhead
,
"at"
,
NewSmallFile
(
bf
.
readAt
))
// TODO mtime(at) = tidtime(at)
// XXX mkfile(bh, "invalidations", bh.inv)
return
bfdir
.
Inode
(),
fuse
.
OK
return
bfdir
.
Inode
(),
nil
}
// XXX do we need to support rmdir? (probably no)
/*
// /bigfile/<bigfileX> -> Mkdir receives client request to create @<tid>.
// /bigfile/<bigfileX> -> Mkdir receives client request to create @<tid>/.
func
(
bfdir
*
BigFileDir
)
Mkdir
(
name
string
,
mode
uint32
,
fctx
*
fuse
.
Context
)
(
*
nodefs
.
Inode
,
fuse
.
Status
)
{
// XXX ok to ignore mode?
inode
,
err
:=
bfdir
.
mkdir
(
name
,
fctx
)
return
inode
,
err2LogStatus
(
err
)
}
func
(
bfdir
*
BigFileDir
)
mkdir
(
name
string
,
fctx
*
fuse
.
Context
)
(
_
*
nodefs
.
Inode
,
err
error
)
{
defer
xerr
.
Contextf
(
&
err
,
"/bigfile/%s: mkdir %q"
,
bfdir
.
oid
,
name
)
var
tid
zodb
.
Tid
var err error
ok
:=
false
if
strings
.
HasPrefix
(
name
,
"@"
)
{
...
...
@@ -491,13 +516,9 @@ func (bfdir *BigFileDir) Mkdir(name string, mode uint32, fctx *fuse.Context) (*n
ok
=
(
err
==
nil
)
}
if
!
ok
{
log.Warning("/bigfile/XXX: mkdir %q: not-@tid", name)
return nil, fuse.EINVAL
return
nil
,
eINVALf
(
"not @tid"
)
}
// XXX ok to ignore mode?
// XXX vvv dups BigFileRoot.Mkdir ?
// check to see if dir(tid) is already
bfdir
.
mu
.
Lock
()
...
...
@@ -505,47 +526,67 @@ func (bfdir *BigFileDir) Mkdir(name string, mode uint32, fctx *fuse.Context) (*n
bfdir
.
mu
.
Unlock
()
if
already
{
return nil,
fuse.Status(syscall.EEXIST)
return
nil
,
syscall
.
EEXIST
}
// not there - without bfdir lock proceed to create @tid historical connection
// create new read-only transaction for this bigfile conn
txn, txnCtx := transaction.New(context.Background())
bf
,
err
:=
bigopen
(
asctx
(
fctx
),
bfdir
.
zdb
,
bfdir
.
oid
,
&
zodb
.
ConnOptions
{
At
:
tid
,
})
if
err
!=
nil
{
return
nil
,
err
}
defer
func
()
{
if
status != fuse.OK
{
txn.Abort
()
if
err
!=
nil
{
bf
.
Close
()
}
}
}
()
// create new DB/Connection for this @tid
// relock bfdir and either mkdir or EEXIST if the directory was maybe
// simultanously created while we were not holding bfroot.mu
bfdir
.
mu
.
Lock
()
_
,
already
=
bfdir
.
revTab
[
tid
]
if
already
{
bfdir
.
mu
.
Unlock
()
return
nil
,
syscall
.
EEXIST
}
// XXX better ctx = transaction.PutIntoContext(ctx, txn)
ctx, cancel := xcontext.Merge(asctx(fctx), txnCtx)
defer cancel()
bfrev
:=
&
BigFileRev
{
Node
:
nodefs
.
NewDefaultNode
(),
}
// XXX ok to reuse bfdir.zdb? (or better keep that only for head?)
zconn, err := bfdir.zdb.Open(ctx, &zodb.ConnOptions{
At: tid,
})
revdata
:=
&
BigFileData
{
Node
:
nodefs
.
NewDefaultNode
(),
bigfile
:
bf
,
loading
:
make
(
map
[
int64
]
*
blkLoadState
),
}
...
bfdir
.
revTab
[
tid
]
=
bfrev
bfdir
.
mu
.
Unlock
()
// mkdir takes filesystem treeLock - do it outside bfroot.mu
mkdir
(
bfdir
,
name
,
bfrev
)
mkfile
(
bfrev
,
"data"
,
revdata
)
return bfrev.Inode(),
fuse.OK
return
bfrev
.
Inode
(),
nil
}
// XXX -> zopen ?
func openBigFile(zopt *zodb.ConnOptions) (, err error) {
// XXX errctx
defer xerr.Contextf(&err, "XXX")
// bigopen opens BigFile corresponding to oid and zopt.
//
// A new read-only transaction is opened.
// A new ZODB connection is opened via zdb.
// A ZBigFile corresponding to oid is activated and statted.
//
// The whole result is returned as BigFile.
func
bigopen
(
ctx
context
.
Context
,
zdb
*
zodb
.
DB
,
oid
zodb
.
Oid
,
zopt
*
zodb
.
ConnOptions
)
(
_
*
BigFile
,
err
error
)
{
defer
xerr
.
Contextf
(
&
err
,
"bigopen %s %s"
,
oid
,
zopt
)
// create new read-only transaction for this bigfile
txn
,
txnCtx
:=
transaction
.
New
(
context
.
Background
())
defer
func
()
{
if
status != fuse.OK
{
if
err
!=
nil
{
txn
.
Abort
()
}
}()
...
...
@@ -554,18 +595,65 @@ func openBigFile(zopt *zodb.ConnOptions) (, err error) {
// create new DB/Connection for this bigfile open
// XXX better ctx = transaction.PutIntoContext(ctx, txn)
ctx, cancel := xcontext.Merge(
asctx(fctx)
, txnCtx)
ctx
,
cancel
:=
xcontext
.
Merge
(
ctx
,
txnCtx
)
defer
cancel
()
zconn, err := zdb.Open(ctx, zop
e
)
zconn
,
err
:=
zdb
.
Open
(
ctx
,
zop
t
)
if
err
!=
nil
{
return err
//log.Errorf("/bigfile: mkdir %q: %s", name, err)
//return nil, fuse.EIO
return
nil
,
err
}
xzbf
,
err
:=
zconn
.
Get
(
ctx
,
oid
)
if
err
!=
nil
{
switch
errors
.
Cause
(
err
)
.
(
type
)
{
case
*
zodb
.
NoObjectError
:
return
nil
,
eINVAL
(
err
)
case
*
zodb
.
NoDataError
:
return
nil
,
eINVAL
(
err
)
// XXX what to do if it was existing and got deleted?
default
:
return
nil
,
err
}
}
zbf
,
ok
:=
xzbf
.
(
*
ZBigFile
)
if
!
ok
{
return
nil
,
eINVALf
(
"%s is not a ZBigFile"
,
typeOf
(
xzbf
))
}
// activate ZBigFile and keep it this way
err
=
zbf
.
PActivate
(
ctx
)
if
err
!=
nil
{
return
nil
,
err
}
defer
func
()
{
if
err
!=
nil
{
zbf
.
PDeactivate
()
}
}()
zbfSize
,
err
:=
zbf
.
Size
(
ctx
)
if
err
!=
nil
{
return
nil
,
err
}
return
&
BigFile
{
txnCtx
:
txnCtx
,
zconn
:
zconn
,
zbf
:
zbf
,
zbfSize
:
zbfSize
,
},
nil
}
*/
// Close release all resources of BigFile.
func
(
bf
*
BigFile
)
Close
()
error
{
bf
.
zbf
.
PDeactivate
()
bf
.
zbf
=
nil
transaction
.
Current
(
bf
.
txnCtx
)
.
Abort
()
bf
.
zconn
=
nil
return
nil
}
// /bigfile/<bigfileX>/head/data -> Getattr serves stat.
func
(
bfdata
*
BigFileData
)
GetAttr
(
out
*
fuse
.
Attr
,
_
nodefs
.
File
,
fctx
*
fuse
.
Context
)
fuse
.
Status
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment