Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
go-fuse
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
Kirill Smelkov
go-fuse
Commits
144cb541
Commit
144cb541
authored
Mar 23, 2023
by
Han-Wen Nienhuys
1
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fs: move interface checks to the implementations
Change-Id: I1612f912dd6b113e7776b13c40f9a41038a3fd53
parent
9ce778ed
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
46 additions
and
19 deletions
+46
-19
fs/loopback.go
fs/loopback.go
+26
-19
fs/loopback_darwin.go
fs/loopback_darwin.go
+10
-0
fs/loopback_linux.go
fs/loopback_linux.go
+10
-0
No files found.
fs/loopback.go
View file @
144cb541
...
@@ -70,25 +70,6 @@ type LoopbackNode struct {
...
@@ -70,25 +70,6 @@ type LoopbackNode struct {
}
}
var
_
=
(
NodeStatfser
)((
*
LoopbackNode
)(
nil
))
var
_
=
(
NodeStatfser
)((
*
LoopbackNode
)(
nil
))
var
_
=
(
NodeStatfser
)((
*
LoopbackNode
)(
nil
))
var
_
=
(
NodeGetattrer
)((
*
LoopbackNode
)(
nil
))
var
_
=
(
NodeGetxattrer
)((
*
LoopbackNode
)(
nil
))
var
_
=
(
NodeSetxattrer
)((
*
LoopbackNode
)(
nil
))
var
_
=
(
NodeRemovexattrer
)((
*
LoopbackNode
)(
nil
))
var
_
=
(
NodeListxattrer
)((
*
LoopbackNode
)(
nil
))
var
_
=
(
NodeReadlinker
)((
*
LoopbackNode
)(
nil
))
var
_
=
(
NodeOpener
)((
*
LoopbackNode
)(
nil
))
var
_
=
(
NodeCopyFileRanger
)((
*
LoopbackNode
)(
nil
))
var
_
=
(
NodeLookuper
)((
*
LoopbackNode
)(
nil
))
var
_
=
(
NodeOpendirer
)((
*
LoopbackNode
)(
nil
))
var
_
=
(
NodeReaddirer
)((
*
LoopbackNode
)(
nil
))
var
_
=
(
NodeMkdirer
)((
*
LoopbackNode
)(
nil
))
var
_
=
(
NodeMknoder
)((
*
LoopbackNode
)(
nil
))
var
_
=
(
NodeLinker
)((
*
LoopbackNode
)(
nil
))
var
_
=
(
NodeSymlinker
)((
*
LoopbackNode
)(
nil
))
var
_
=
(
NodeUnlinker
)((
*
LoopbackNode
)(
nil
))
var
_
=
(
NodeRmdirer
)((
*
LoopbackNode
)(
nil
))
var
_
=
(
NodeRenamer
)((
*
LoopbackNode
)(
nil
))
func
(
n
*
LoopbackNode
)
Statfs
(
ctx
context
.
Context
,
out
*
fuse
.
StatfsOut
)
syscall
.
Errno
{
func
(
n
*
LoopbackNode
)
Statfs
(
ctx
context
.
Context
,
out
*
fuse
.
StatfsOut
)
syscall
.
Errno
{
s
:=
syscall
.
Statfs_t
{}
s
:=
syscall
.
Statfs_t
{}
...
@@ -107,6 +88,8 @@ func (n *LoopbackNode) path() string {
...
@@ -107,6 +88,8 @@ func (n *LoopbackNode) path() string {
return
filepath
.
Join
(
n
.
RootData
.
Path
,
path
)
return
filepath
.
Join
(
n
.
RootData
.
Path
,
path
)
}
}
var
_
=
(
NodeLookuper
)((
*
LoopbackNode
)(
nil
))
func
(
n
*
LoopbackNode
)
Lookup
(
ctx
context
.
Context
,
name
string
,
out
*
fuse
.
EntryOut
)
(
*
Inode
,
syscall
.
Errno
)
{
func
(
n
*
LoopbackNode
)
Lookup
(
ctx
context
.
Context
,
name
string
,
out
*
fuse
.
EntryOut
)
(
*
Inode
,
syscall
.
Errno
)
{
p
:=
filepath
.
Join
(
n
.
path
(),
name
)
p
:=
filepath
.
Join
(
n
.
path
(),
name
)
...
@@ -135,6 +118,8 @@ func (n *LoopbackNode) preserveOwner(ctx context.Context, path string) error {
...
@@ -135,6 +118,8 @@ func (n *LoopbackNode) preserveOwner(ctx context.Context, path string) error {
return
syscall
.
Lchown
(
path
,
int
(
caller
.
Uid
),
int
(
caller
.
Gid
))
return
syscall
.
Lchown
(
path
,
int
(
caller
.
Uid
),
int
(
caller
.
Gid
))
}
}
var
_
=
(
NodeMknoder
)((
*
LoopbackNode
)(
nil
))
func
(
n
*
LoopbackNode
)
Mknod
(
ctx
context
.
Context
,
name
string
,
mode
,
rdev
uint32
,
out
*
fuse
.
EntryOut
)
(
*
Inode
,
syscall
.
Errno
)
{
func
(
n
*
LoopbackNode
)
Mknod
(
ctx
context
.
Context
,
name
string
,
mode
,
rdev
uint32
,
out
*
fuse
.
EntryOut
)
(
*
Inode
,
syscall
.
Errno
)
{
p
:=
filepath
.
Join
(
n
.
path
(),
name
)
p
:=
filepath
.
Join
(
n
.
path
(),
name
)
err
:=
syscall
.
Mknod
(
p
,
mode
,
int
(
rdev
))
err
:=
syscall
.
Mknod
(
p
,
mode
,
int
(
rdev
))
...
@@ -156,6 +141,8 @@ func (n *LoopbackNode) Mknod(ctx context.Context, name string, mode, rdev uint32
...
@@ -156,6 +141,8 @@ func (n *LoopbackNode) Mknod(ctx context.Context, name string, mode, rdev uint32
return
ch
,
0
return
ch
,
0
}
}
var
_
=
(
NodeMkdirer
)((
*
LoopbackNode
)(
nil
))
func
(
n
*
LoopbackNode
)
Mkdir
(
ctx
context
.
Context
,
name
string
,
mode
uint32
,
out
*
fuse
.
EntryOut
)
(
*
Inode
,
syscall
.
Errno
)
{
func
(
n
*
LoopbackNode
)
Mkdir
(
ctx
context
.
Context
,
name
string
,
mode
uint32
,
out
*
fuse
.
EntryOut
)
(
*
Inode
,
syscall
.
Errno
)
{
p
:=
filepath
.
Join
(
n
.
path
(),
name
)
p
:=
filepath
.
Join
(
n
.
path
(),
name
)
err
:=
os
.
Mkdir
(
p
,
os
.
FileMode
(
mode
))
err
:=
os
.
Mkdir
(
p
,
os
.
FileMode
(
mode
))
...
@@ -177,18 +164,24 @@ func (n *LoopbackNode) Mkdir(ctx context.Context, name string, mode uint32, out
...
@@ -177,18 +164,24 @@ func (n *LoopbackNode) Mkdir(ctx context.Context, name string, mode uint32, out
return
ch
,
0
return
ch
,
0
}
}
var
_
=
(
NodeRmdirer
)((
*
LoopbackNode
)(
nil
))
func
(
n
*
LoopbackNode
)
Rmdir
(
ctx
context
.
Context
,
name
string
)
syscall
.
Errno
{
func
(
n
*
LoopbackNode
)
Rmdir
(
ctx
context
.
Context
,
name
string
)
syscall
.
Errno
{
p
:=
filepath
.
Join
(
n
.
path
(),
name
)
p
:=
filepath
.
Join
(
n
.
path
(),
name
)
err
:=
syscall
.
Rmdir
(
p
)
err
:=
syscall
.
Rmdir
(
p
)
return
ToErrno
(
err
)
return
ToErrno
(
err
)
}
}
var
_
=
(
NodeUnlinker
)((
*
LoopbackNode
)(
nil
))
func
(
n
*
LoopbackNode
)
Unlink
(
ctx
context
.
Context
,
name
string
)
syscall
.
Errno
{
func
(
n
*
LoopbackNode
)
Unlink
(
ctx
context
.
Context
,
name
string
)
syscall
.
Errno
{
p
:=
filepath
.
Join
(
n
.
path
(),
name
)
p
:=
filepath
.
Join
(
n
.
path
(),
name
)
err
:=
syscall
.
Unlink
(
p
)
err
:=
syscall
.
Unlink
(
p
)
return
ToErrno
(
err
)
return
ToErrno
(
err
)
}
}
var
_
=
(
NodeRenamer
)((
*
LoopbackNode
)(
nil
))
func
(
n
*
LoopbackNode
)
Rename
(
ctx
context
.
Context
,
name
string
,
newParent
InodeEmbedder
,
newName
string
,
flags
uint32
)
syscall
.
Errno
{
func
(
n
*
LoopbackNode
)
Rename
(
ctx
context
.
Context
,
name
string
,
newParent
InodeEmbedder
,
newName
string
,
flags
uint32
)
syscall
.
Errno
{
if
flags
&
RENAME_EXCHANGE
!=
0
{
if
flags
&
RENAME_EXCHANGE
!=
0
{
return
n
.
renameExchange
(
name
,
newParent
,
newName
)
return
n
.
renameExchange
(
name
,
newParent
,
newName
)
...
@@ -225,6 +218,8 @@ func (n *LoopbackNode) Create(ctx context.Context, name string, flags uint32, mo
...
@@ -225,6 +218,8 @@ func (n *LoopbackNode) Create(ctx context.Context, name string, flags uint32, mo
return
ch
,
lf
,
0
,
0
return
ch
,
lf
,
0
,
0
}
}
var
_
=
(
NodeSymlinker
)((
*
LoopbackNode
)(
nil
))
func
(
n
*
LoopbackNode
)
Symlink
(
ctx
context
.
Context
,
target
,
name
string
,
out
*
fuse
.
EntryOut
)
(
*
Inode
,
syscall
.
Errno
)
{
func
(
n
*
LoopbackNode
)
Symlink
(
ctx
context
.
Context
,
target
,
name
string
,
out
*
fuse
.
EntryOut
)
(
*
Inode
,
syscall
.
Errno
)
{
p
:=
filepath
.
Join
(
n
.
path
(),
name
)
p
:=
filepath
.
Join
(
n
.
path
(),
name
)
err
:=
syscall
.
Symlink
(
target
,
p
)
err
:=
syscall
.
Symlink
(
target
,
p
)
...
@@ -244,6 +239,8 @@ func (n *LoopbackNode) Symlink(ctx context.Context, target, name string, out *fu
...
@@ -244,6 +239,8 @@ func (n *LoopbackNode) Symlink(ctx context.Context, target, name string, out *fu
return
ch
,
0
return
ch
,
0
}
}
var
_
=
(
NodeLinker
)((
*
LoopbackNode
)(
nil
))
func
(
n
*
LoopbackNode
)
Link
(
ctx
context
.
Context
,
target
InodeEmbedder
,
name
string
,
out
*
fuse
.
EntryOut
)
(
*
Inode
,
syscall
.
Errno
)
{
func
(
n
*
LoopbackNode
)
Link
(
ctx
context
.
Context
,
target
InodeEmbedder
,
name
string
,
out
*
fuse
.
EntryOut
)
(
*
Inode
,
syscall
.
Errno
)
{
p
:=
filepath
.
Join
(
n
.
path
(),
name
)
p
:=
filepath
.
Join
(
n
.
path
(),
name
)
...
@@ -263,6 +260,8 @@ func (n *LoopbackNode) Link(ctx context.Context, target InodeEmbedder, name stri
...
@@ -263,6 +260,8 @@ func (n *LoopbackNode) Link(ctx context.Context, target InodeEmbedder, name stri
return
ch
,
0
return
ch
,
0
}
}
var
_
=
(
NodeReadlinker
)((
*
LoopbackNode
)(
nil
))
func
(
n
*
LoopbackNode
)
Readlink
(
ctx
context
.
Context
)
([]
byte
,
syscall
.
Errno
)
{
func
(
n
*
LoopbackNode
)
Readlink
(
ctx
context
.
Context
)
([]
byte
,
syscall
.
Errno
)
{
p
:=
n
.
path
()
p
:=
n
.
path
()
...
@@ -279,6 +278,8 @@ func (n *LoopbackNode) Readlink(ctx context.Context) ([]byte, syscall.Errno) {
...
@@ -279,6 +278,8 @@ func (n *LoopbackNode) Readlink(ctx context.Context) ([]byte, syscall.Errno) {
}
}
}
}
var
_
=
(
NodeOpener
)((
*
LoopbackNode
)(
nil
))
func
(
n
*
LoopbackNode
)
Open
(
ctx
context
.
Context
,
flags
uint32
)
(
fh
FileHandle
,
fuseFlags
uint32
,
errno
syscall
.
Errno
)
{
func
(
n
*
LoopbackNode
)
Open
(
ctx
context
.
Context
,
flags
uint32
)
(
fh
FileHandle
,
fuseFlags
uint32
,
errno
syscall
.
Errno
)
{
flags
=
flags
&^
syscall
.
O_APPEND
flags
=
flags
&^
syscall
.
O_APPEND
p
:=
n
.
path
()
p
:=
n
.
path
()
...
@@ -290,6 +291,8 @@ func (n *LoopbackNode) Open(ctx context.Context, flags uint32) (fh FileHandle, f
...
@@ -290,6 +291,8 @@ func (n *LoopbackNode) Open(ctx context.Context, flags uint32) (fh FileHandle, f
return
lf
,
0
,
0
return
lf
,
0
,
0
}
}
var
_
=
(
NodeOpendirer
)((
*
LoopbackNode
)(
nil
))
func
(
n
*
LoopbackNode
)
Opendir
(
ctx
context
.
Context
)
syscall
.
Errno
{
func
(
n
*
LoopbackNode
)
Opendir
(
ctx
context
.
Context
)
syscall
.
Errno
{
fd
,
err
:=
syscall
.
Open
(
n
.
path
(),
syscall
.
O_DIRECTORY
,
0755
)
fd
,
err
:=
syscall
.
Open
(
n
.
path
(),
syscall
.
O_DIRECTORY
,
0755
)
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -299,10 +302,14 @@ func (n *LoopbackNode) Opendir(ctx context.Context) syscall.Errno {
...
@@ -299,10 +302,14 @@ func (n *LoopbackNode) Opendir(ctx context.Context) syscall.Errno {
return
OK
return
OK
}
}
var
_
=
(
NodeReaddirer
)((
*
LoopbackNode
)(
nil
))
func
(
n
*
LoopbackNode
)
Readdir
(
ctx
context
.
Context
)
(
DirStream
,
syscall
.
Errno
)
{
func
(
n
*
LoopbackNode
)
Readdir
(
ctx
context
.
Context
)
(
DirStream
,
syscall
.
Errno
)
{
return
NewLoopbackDirStream
(
n
.
path
())
return
NewLoopbackDirStream
(
n
.
path
())
}
}
var
_
=
(
NodeGetattrer
)((
*
LoopbackNode
)(
nil
))
func
(
n
*
LoopbackNode
)
Getattr
(
ctx
context
.
Context
,
f
FileHandle
,
out
*
fuse
.
AttrOut
)
syscall
.
Errno
{
func
(
n
*
LoopbackNode
)
Getattr
(
ctx
context
.
Context
,
f
FileHandle
,
out
*
fuse
.
AttrOut
)
syscall
.
Errno
{
if
f
!=
nil
{
if
f
!=
nil
{
return
f
.
(
FileGetattrer
)
.
Getattr
(
ctx
,
out
)
return
f
.
(
FileGetattrer
)
.
Getattr
(
ctx
,
out
)
...
...
fs/loopback_darwin.go
View file @
144cb541
...
@@ -17,18 +17,26 @@ import (
...
@@ -17,18 +17,26 @@ import (
"github.com/hanwen/go-fuse/v2/internal/utimens"
"github.com/hanwen/go-fuse/v2/internal/utimens"
)
)
var
_
=
(
NodeGetxattrer
)((
*
LoopbackNode
)(
nil
))
func
(
n
*
LoopbackNode
)
Getxattr
(
ctx
context
.
Context
,
attr
string
,
dest
[]
byte
)
(
uint32
,
syscall
.
Errno
)
{
func
(
n
*
LoopbackNode
)
Getxattr
(
ctx
context
.
Context
,
attr
string
,
dest
[]
byte
)
(
uint32
,
syscall
.
Errno
)
{
return
0
,
syscall
.
ENOSYS
return
0
,
syscall
.
ENOSYS
}
}
var
_
=
(
NodeSetxattrer
)((
*
LoopbackNode
)(
nil
))
func
(
n
*
LoopbackNode
)
Setxattr
(
ctx
context
.
Context
,
attr
string
,
data
[]
byte
,
flags
uint32
)
syscall
.
Errno
{
func
(
n
*
LoopbackNode
)
Setxattr
(
ctx
context
.
Context
,
attr
string
,
data
[]
byte
,
flags
uint32
)
syscall
.
Errno
{
return
syscall
.
ENOSYS
return
syscall
.
ENOSYS
}
}
var
_
=
(
NodeRemovexattrer
)((
*
LoopbackNode
)(
nil
))
func
(
n
*
LoopbackNode
)
Removexattr
(
ctx
context
.
Context
,
attr
string
)
syscall
.
Errno
{
func
(
n
*
LoopbackNode
)
Removexattr
(
ctx
context
.
Context
,
attr
string
)
syscall
.
Errno
{
return
syscall
.
ENOSYS
return
syscall
.
ENOSYS
}
}
var
_
=
(
NodeListxattrer
)((
*
LoopbackNode
)(
nil
))
func
(
n
*
LoopbackNode
)
Listxattr
(
ctx
context
.
Context
,
dest
[]
byte
)
(
uint32
,
syscall
.
Errno
)
{
func
(
n
*
LoopbackNode
)
Listxattr
(
ctx
context
.
Context
,
dest
[]
byte
)
(
uint32
,
syscall
.
Errno
)
{
return
0
,
syscall
.
ENOSYS
return
0
,
syscall
.
ENOSYS
}
}
...
@@ -112,6 +120,8 @@ func (f *loopbackFile) utimens(a *time.Time, m *time.Time) syscall.Errno {
...
@@ -112,6 +120,8 @@ func (f *loopbackFile) utimens(a *time.Time, m *time.Time) syscall.Errno {
return
ToErrno
(
err
)
return
ToErrno
(
err
)
}
}
var
_
=
(
NodeCopyFileRanger
)((
*
LoopbackNode
)(
nil
))
func
(
n
*
LoopbackNode
)
CopyFileRange
(
ctx
context
.
Context
,
fhIn
FileHandle
,
func
(
n
*
LoopbackNode
)
CopyFileRange
(
ctx
context
.
Context
,
fhIn
FileHandle
,
offIn
uint64
,
out
*
Inode
,
fhOut
FileHandle
,
offOut
uint64
,
offIn
uint64
,
out
*
Inode
,
fhOut
FileHandle
,
offOut
uint64
,
len
uint64
,
flags
uint64
)
(
uint32
,
syscall
.
Errno
)
{
len
uint64
,
flags
uint64
)
(
uint32
,
syscall
.
Errno
)
{
...
...
fs/loopback_linux.go
View file @
144cb541
...
@@ -15,21 +15,29 @@ import (
...
@@ -15,21 +15,29 @@ import (
"golang.org/x/sys/unix"
"golang.org/x/sys/unix"
)
)
var
_
=
(
NodeGetxattrer
)((
*
LoopbackNode
)(
nil
))
func
(
n
*
LoopbackNode
)
Getxattr
(
ctx
context
.
Context
,
attr
string
,
dest
[]
byte
)
(
uint32
,
syscall
.
Errno
)
{
func
(
n
*
LoopbackNode
)
Getxattr
(
ctx
context
.
Context
,
attr
string
,
dest
[]
byte
)
(
uint32
,
syscall
.
Errno
)
{
sz
,
err
:=
unix
.
Lgetxattr
(
n
.
path
(),
attr
,
dest
)
sz
,
err
:=
unix
.
Lgetxattr
(
n
.
path
(),
attr
,
dest
)
return
uint32
(
sz
),
ToErrno
(
err
)
return
uint32
(
sz
),
ToErrno
(
err
)
}
}
var
_
=
(
NodeSetxattrer
)((
*
LoopbackNode
)(
nil
))
func
(
n
*
LoopbackNode
)
Setxattr
(
ctx
context
.
Context
,
attr
string
,
data
[]
byte
,
flags
uint32
)
syscall
.
Errno
{
func
(
n
*
LoopbackNode
)
Setxattr
(
ctx
context
.
Context
,
attr
string
,
data
[]
byte
,
flags
uint32
)
syscall
.
Errno
{
err
:=
unix
.
Lsetxattr
(
n
.
path
(),
attr
,
data
,
int
(
flags
))
err
:=
unix
.
Lsetxattr
(
n
.
path
(),
attr
,
data
,
int
(
flags
))
return
ToErrno
(
err
)
return
ToErrno
(
err
)
}
}
var
_
=
(
NodeRemovexattrer
)((
*
LoopbackNode
)(
nil
))
func
(
n
*
LoopbackNode
)
Removexattr
(
ctx
context
.
Context
,
attr
string
)
syscall
.
Errno
{
func
(
n
*
LoopbackNode
)
Removexattr
(
ctx
context
.
Context
,
attr
string
)
syscall
.
Errno
{
err
:=
unix
.
Lremovexattr
(
n
.
path
(),
attr
)
err
:=
unix
.
Lremovexattr
(
n
.
path
(),
attr
)
return
ToErrno
(
err
)
return
ToErrno
(
err
)
}
}
var
_
=
(
NodeListxattrer
)((
*
LoopbackNode
)(
nil
))
func
(
n
*
LoopbackNode
)
Listxattr
(
ctx
context
.
Context
,
dest
[]
byte
)
(
uint32
,
syscall
.
Errno
)
{
func
(
n
*
LoopbackNode
)
Listxattr
(
ctx
context
.
Context
,
dest
[]
byte
)
(
uint32
,
syscall
.
Errno
)
{
sz
,
err
:=
unix
.
Llistxattr
(
n
.
path
(),
dest
)
sz
,
err
:=
unix
.
Llistxattr
(
n
.
path
(),
dest
)
return
uint32
(
sz
),
ToErrno
(
err
)
return
uint32
(
sz
),
ToErrno
(
err
)
...
@@ -70,6 +78,8 @@ func (n *LoopbackNode) renameExchange(name string, newparent InodeEmbedder, newN
...
@@ -70,6 +78,8 @@ func (n *LoopbackNode) renameExchange(name string, newparent InodeEmbedder, newN
return
ToErrno
(
unix
.
Renameat2
(
fd1
,
name
,
fd2
,
newName
,
unix
.
RENAME_EXCHANGE
))
return
ToErrno
(
unix
.
Renameat2
(
fd1
,
name
,
fd2
,
newName
,
unix
.
RENAME_EXCHANGE
))
}
}
var
_
=
(
NodeCopyFileRanger
)((
*
LoopbackNode
)(
nil
))
func
(
n
*
LoopbackNode
)
CopyFileRange
(
ctx
context
.
Context
,
fhIn
FileHandle
,
func
(
n
*
LoopbackNode
)
CopyFileRange
(
ctx
context
.
Context
,
fhIn
FileHandle
,
offIn
uint64
,
out
*
Inode
,
fhOut
FileHandle
,
offOut
uint64
,
offIn
uint64
,
out
*
Inode
,
fhOut
FileHandle
,
offOut
uint64
,
len
uint64
,
flags
uint64
)
(
uint32
,
syscall
.
Errno
)
{
len
uint64
,
flags
uint64
)
(
uint32
,
syscall
.
Errno
)
{
...
...
Kirill Smelkov
@kirr
mentioned in commit
06a252bd
·
Jun 12, 2023
mentioned in commit
06a252bd
mentioned in commit 06a252bd417250597a7c0e8eb2ea2eb8ab62cb2d
Toggle commit list
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