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
Levin Zimmermann
go-fuse
Commits
eac61bbc
Commit
eac61bbc
authored
Mar 29, 2019
by
Han-Wen Nienhuys
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
zipfs: rewrite using new nodefs API.
Remove memtree.
parent
dddd77b8
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
219 additions
and
369 deletions
+219
-369
nodefs/README.md
nodefs/README.md
+2
-0
zipfs/memtree.go
zipfs/memtree.go
+0
-140
zipfs/multizip.go
zipfs/multizip.go
+38
-160
zipfs/multizip_test.go
zipfs/multizip_test.go
+13
-15
zipfs/tarfs.go
zipfs/tarfs.go
+53
-15
zipfs/zipfs.go
zipfs/zipfs.go
+103
-24
zipfs/zipfs_test.go
zipfs/zipfs_test.go
+10
-15
No files found.
nodefs/README.md
View file @
eac61bbc
...
@@ -60,3 +60,5 @@ To do/To decide
...
@@ -60,3 +60,5 @@ To do/To decide
*
decide on a final package name
*
decide on a final package name
*
handle less open/create.
*
handle less open/create.
*
Symlink []byte vs string.
zipfs/memtree.go
deleted
100644 → 0
View file @
dddd77b8
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package
zipfs
import
(
"fmt"
"strings"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/fuse/nodefs"
)
type
MemFile
interface
{
Stat
(
out
*
fuse
.
Attr
)
Data
()
[]
byte
}
type
memNode
struct
{
nodefs
.
Node
file
MemFile
fs
*
MemTreeFs
}
// memTreeFs creates a tree of internal Inodes. Since the tree is
// loaded in memory completely at startup, it does not need inode
// discovery through Lookup() at serve time.
type
MemTreeFs
struct
{
root
*
memNode
files
map
[
string
]
MemFile
Name
string
}
func
NewMemTreeFs
(
files
map
[
string
]
MemFile
)
*
MemTreeFs
{
fs
:=
&
MemTreeFs
{
root
:
&
memNode
{
Node
:
nodefs
.
NewDefaultNode
()},
files
:
files
,
}
fs
.
root
.
fs
=
fs
return
fs
}
func
(
fs
*
MemTreeFs
)
String
()
string
{
return
fs
.
Name
}
func
(
fs
*
MemTreeFs
)
Root
()
nodefs
.
Node
{
return
fs
.
root
}
func
(
fs
*
MemTreeFs
)
onMount
()
{
for
k
,
v
:=
range
fs
.
files
{
fs
.
addFile
(
k
,
v
)
}
fs
.
files
=
nil
}
func
(
n
*
memNode
)
OnMount
(
c
*
nodefs
.
FileSystemConnector
)
{
n
.
fs
.
onMount
()
}
func
(
n
*
memNode
)
Print
(
indent
int
)
{
s
:=
""
for
i
:=
0
;
i
<
indent
;
i
++
{
s
=
s
+
" "
}
children
:=
n
.
Inode
()
.
Children
()
for
k
,
v
:=
range
children
{
if
v
.
IsDir
()
{
fmt
.
Println
(
s
+
k
+
":"
)
mn
,
ok
:=
v
.
Node
()
.
(
*
memNode
)
if
ok
{
mn
.
Print
(
indent
+
2
)
}
}
else
{
fmt
.
Println
(
s
+
k
)
}
}
}
func
(
n
*
memNode
)
OpenDir
(
context
*
fuse
.
Context
)
(
stream
[]
fuse
.
DirEntry
,
code
fuse
.
Status
)
{
children
:=
n
.
Inode
()
.
Children
()
stream
=
make
([]
fuse
.
DirEntry
,
0
,
len
(
children
))
for
k
,
v
:=
range
children
{
mode
:=
fuse
.
S_IFREG
|
0666
if
v
.
IsDir
()
{
mode
=
fuse
.
S_IFDIR
|
0777
}
stream
=
append
(
stream
,
fuse
.
DirEntry
{
Name
:
k
,
Mode
:
uint32
(
mode
),
})
}
return
stream
,
fuse
.
OK
}
func
(
n
*
memNode
)
Open
(
flags
uint32
,
context
*
fuse
.
Context
)
(
fuseFile
nodefs
.
File
,
code
fuse
.
Status
)
{
if
flags
&
fuse
.
O_ANYWRITE
!=
0
{
return
nil
,
fuse
.
EPERM
}
return
nodefs
.
NewDataFile
(
n
.
file
.
Data
()),
fuse
.
OK
}
func
(
n
*
memNode
)
Deletable
()
bool
{
return
false
}
func
(
n
*
memNode
)
GetAttr
(
out
*
fuse
.
Attr
,
file
nodefs
.
File
,
context
*
fuse
.
Context
)
fuse
.
Status
{
if
n
.
Inode
()
.
IsDir
()
{
out
.
Mode
=
fuse
.
S_IFDIR
|
0777
return
fuse
.
OK
}
n
.
file
.
Stat
(
out
)
out
.
Blocks
=
(
out
.
Size
+
511
)
/
512
return
fuse
.
OK
}
func
(
n
*
MemTreeFs
)
addFile
(
name
string
,
f
MemFile
)
{
comps
:=
strings
.
Split
(
name
,
"/"
)
node
:=
n
.
root
.
Inode
()
for
i
,
c
:=
range
comps
{
child
:=
node
.
GetChild
(
c
)
if
child
==
nil
{
fsnode
:=
&
memNode
{
Node
:
nodefs
.
NewDefaultNode
(),
fs
:
n
,
}
if
i
==
len
(
comps
)
-
1
{
fsnode
.
file
=
f
}
child
=
node
.
NewChild
(
c
,
fsnode
.
file
==
nil
,
fsnode
)
}
node
=
child
}
}
zipfs/multizip.go
View file @
eac61bbc
...
@@ -15,191 +15,69 @@ symlinking path/to/zipfile to /config/zipmount
...
@@ -15,191 +15,69 @@ symlinking path/to/zipfile to /config/zipmount
*/
*/
import
(
import
(
"context"
"log"
"log"
"path/filepath"
"syscall"
"sync"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/fuse/nodefs"
"github.com/hanwen/go-fuse/nodefs"
"github.com/hanwen/go-fuse/fuse/pathfs"
)
)
const
(
// MultiZipFs is a filesystem that mounts zipfiles.
CONFIG_PREFIX
=
"config/"
)
////////////////////////////////////////////////////////////////
// MultiZipFs is a path filesystem that mounts zipfiles.
type
MultiZipFs
struct
{
type
MultiZipFs
struct
{
lock
sync
.
RWMutex
nodefs
.
Inode
zips
map
[
string
]
nodefs
.
Node
dirZipFileMap
map
[
string
]
string
// zip files that we are in the process of unmounting.
zombie
map
[
string
]
bool
nodeFs
*
pathfs
.
PathNodeFs
pathfs
.
FileSystem
}
}
func
NewMultiZipFs
()
*
MultiZipFs
{
func
(
fs
*
MultiZipFs
)
OnAdd
(
ctx
context
.
Context
)
{
m
:=
&
MultiZipFs
{
n
:=
fs
.
NewPersistentInode
(
ctx
,
&
configRoot
{},
nodefs
.
NodeAttr
{
Mode
:
syscall
.
S_IFDIR
})
zips
:
make
(
map
[
string
]
nodefs
.
Node
),
zombie
:
make
(
map
[
string
]
bool
),
dirZipFileMap
:
make
(
map
[
string
]
string
),
FileSystem
:
pathfs
.
NewDefaultFileSystem
(),
}
return
m
}
func
(
fs
*
MultiZipFs
)
String
()
string
{
fs
.
AddChild
(
"config"
,
n
,
false
)
return
"MultiZipFs"
}
}
func
(
fs
*
MultiZipFs
)
OnMount
(
nodeFs
*
pathfs
.
PathNodeFs
)
{
type
configRoot
struct
{
fs
.
nodeFs
=
nodeFs
nodefs
.
Inode
}
}
func
(
fs
*
MultiZipFs
)
OpenDir
(
name
string
,
context
*
fuse
.
Context
)
(
stream
[]
fuse
.
DirEntry
,
code
fuse
.
Status
)
{
var
_
=
(
nodefs
.
Unlinker
)((
*
configRoot
)(
nil
))
fs
.
lock
.
RLock
()
var
_
=
(
nodefs
.
Symlinker
)((
*
configRoot
)(
nil
))
defer
fs
.
lock
.
RUnlock
()
stream
=
make
([]
fuse
.
DirEntry
,
0
,
len
(
fs
.
zips
)
+
2
)
func
(
r
*
configRoot
)
Unlink
(
ctx
context
.
Context
,
basename
string
)
syscall
.
Errno
{
if
name
==
""
{
if
r
.
GetChild
(
basename
)
==
nil
{
var
d
fuse
.
DirEntry
return
syscall
.
ENOENT
d
.
Name
=
"config"
d
.
Mode
=
fuse
.
S_IFDIR
|
0700
stream
=
append
(
stream
,
fuse
.
DirEntry
(
d
))
}
}
if
name
==
"config"
{
// XXX RmChild should return Inode?
for
k
:=
range
fs
.
zips
{
var
d
fuse
.
DirEntry
d
.
Name
=
k
d
.
Mode
=
fuse
.
S_IFLNK
stream
=
append
(
stream
,
fuse
.
DirEntry
(
d
))
}
}
return
stream
,
fuse
.
OK
_
,
parent
:=
r
.
Parent
()
}
ch
:=
parent
.
GetChild
(
basename
)
if
ch
==
nil
{
func
(
fs
*
MultiZipFs
)
GetAttr
(
name
string
,
context
*
fuse
.
Context
)
(
*
fuse
.
Attr
,
fuse
.
Status
)
{
return
syscall
.
ENOENT
a
:=
&
fuse
.
Attr
{}
a
.
Owner
=
*
fuse
.
CurrentOwner
()
if
name
==
""
{
// Should not write in top dir.
a
.
Mode
=
fuse
.
S_IFDIR
|
0500
return
a
,
fuse
.
OK
}
if
name
==
"config"
{
a
.
Mode
=
fuse
.
S_IFDIR
|
0700
return
a
,
fuse
.
OK
}
dir
,
base
:=
filepath
.
Split
(
name
)
if
dir
!=
""
&&
dir
!=
CONFIG_PREFIX
{
return
nil
,
fuse
.
ENOENT
}
submode
:=
uint32
(
fuse
.
S_IFDIR
|
0700
)
if
dir
==
CONFIG_PREFIX
{
submode
=
fuse
.
S_IFLNK
|
0600
}
}
success
,
_
:=
parent
.
RmChild
(
basename
)
fs
.
lock
.
RLock
()
if
!
success
{
defer
fs
.
lock
.
RUnlock
()
return
syscall
.
EIO
a
.
Mode
=
submode
_
,
hasDir
:=
fs
.
zips
[
base
]
if
hasDir
{
return
a
,
fuse
.
OK
}
}
return
nil
,
fuse
.
ENOENT
ch
.
RmAllChildren
()
parent
.
RmChild
(
basename
)
parent
.
NotifyEntry
(
basename
)
return
0
}
}
func
(
fs
*
MultiZipFs
)
Unlink
(
name
string
,
context
*
fuse
.
Context
)
(
code
fuse
.
Status
)
{
func
(
r
*
configRoot
)
Symlink
(
ctx
context
.
Context
,
target
string
,
base
string
,
out
*
fuse
.
EntryOut
)
(
*
nodefs
.
Inode
,
syscall
.
Errno
)
{
dir
,
basename
:=
filepath
.
Split
(
name
)
root
,
err
:=
NewArchiveFileSystem
(
target
)
if
dir
==
CONFIG_PREFIX
{
fs
.
lock
.
Lock
()
defer
fs
.
lock
.
Unlock
()
if
fs
.
zombie
[
basename
]
{
return
fuse
.
ENOENT
}
root
,
ok
:=
fs
.
zips
[
basename
]
if
!
ok
{
return
fuse
.
ENOENT
}
name
:=
fs
.
dirZipFileMap
[
basename
]
fs
.
zombie
[
basename
]
=
true
delete
(
fs
.
zips
,
basename
)
delete
(
fs
.
dirZipFileMap
,
basename
)
// Drop the lock to ensure that notify doesn't cause a deadlock.
fs
.
lock
.
Unlock
()
code
=
fs
.
nodeFs
.
UnmountNode
(
root
.
Inode
())
fs
.
lock
.
Lock
()
delete
(
fs
.
zombie
,
basename
)
if
!
code
.
Ok
()
{
// Failed: reinstate
fs
.
zips
[
basename
]
=
root
fs
.
dirZipFileMap
[
basename
]
=
name
}
return
code
}
return
fuse
.
EPERM
}
func
(
fs
*
MultiZipFs
)
Readlink
(
path
string
,
context
*
fuse
.
Context
)
(
val
string
,
code
fuse
.
Status
)
{
dir
,
base
:=
filepath
.
Split
(
path
)
if
dir
!=
CONFIG_PREFIX
{
return
""
,
fuse
.
ENOENT
}
fs
.
lock
.
Lock
()
defer
fs
.
lock
.
Unlock
()
if
fs
.
zombie
[
base
]
{
return
""
,
fuse
.
ENOENT
}
zipfile
,
ok
:=
fs
.
dirZipFileMap
[
base
]
if
!
ok
{
return
""
,
fuse
.
ENOENT
}
return
zipfile
,
fuse
.
OK
}
func
(
fs
*
MultiZipFs
)
Symlink
(
value
string
,
linkName
string
,
context
*
fuse
.
Context
)
(
code
fuse
.
Status
)
{
dir
,
base
:=
filepath
.
Split
(
linkName
)
if
dir
!=
CONFIG_PREFIX
{
return
fuse
.
EPERM
}
fs
.
lock
.
Lock
()
defer
fs
.
lock
.
Unlock
()
if
fs
.
zombie
[
base
]
{
return
fuse
.
EBUSY
}
_
,
ok
:=
fs
.
dirZipFileMap
[
base
]
if
ok
{
return
fuse
.
EBUSY
}
root
,
err
:=
NewArchiveFileSystem
(
value
)
if
err
!=
nil
{
if
err
!=
nil
{
log
.
Println
(
"NewZipArchiveFileSystem failed."
,
err
)
log
.
Println
(
"NewZipArchiveFileSystem failed."
,
err
)
return
fuse
.
EINVAL
return
nil
,
syscall
.
EINVAL
}
}
code
=
fs
.
nodeFs
.
Mount
(
base
,
root
,
nil
)
_
,
parent
:=
r
.
Parent
()
if
!
code
.
Ok
()
{
ch
:=
r
.
NewPersistentInode
(
ctx
,
root
,
nodefs
.
NodeAttr
{
Mode
:
syscall
.
S_IFDIR
})
return
code
parent
.
AddChild
(
base
,
ch
,
false
)
}
fs
.
dirZipFileMap
[
base
]
=
value
link
:=
r
.
NewPersistentInode
(
ctx
,
&
nodefs
.
MemSymlink
{
fs
.
zips
[
base
]
=
root
Data
:
[]
byte
(
target
),
return
fuse
.
OK
},
nodefs
.
NodeAttr
{
Mode
:
syscall
.
S_IFLNK
})
r
.
AddChild
(
base
,
link
,
false
)
return
link
,
0
}
}
zipfs/multizip_test.go
View file @
eac61bbc
...
@@ -11,31 +11,29 @@ import (
...
@@ -11,31 +11,29 @@ import (
"time"
"time"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/fuse/nodefs"
"github.com/hanwen/go-fuse/fuse/pathfs"
"github.com/hanwen/go-fuse/internal/testutil"
"github.com/hanwen/go-fuse/internal/testutil"
"github.com/hanwen/go-fuse/nodefs"
)
)
const
testTtl
=
100
*
time
.
Millisecond
const
testTtl
=
100
*
time
.
Millisecond
func
setupMzfs
(
t
*
testing
.
T
)
(
mountPoint
string
,
state
*
fuse
.
Server
,
cleanup
func
())
{
func
setupMzfs
(
t
*
testing
.
T
)
(
mountPoint
string
,
state
*
fuse
.
Server
,
cleanup
func
())
{
fs
:=
NewMultiZipFs
()
root
:=
&
MultiZipFs
{}
mountPoint
=
testutil
.
TempDir
()
mountPoint
=
testutil
.
TempDir
()
nfs
:=
pathfs
.
NewPathNodeFs
(
fs
,
nil
)
state
,
_
,
err
:=
nodefs
.
MountRoot
(
mountPoint
,
nfs
.
Root
(),
&
nodefs
.
Options
{
dt
:=
testTtl
EntryTimeout
:
testTtl
,
opts
:=
&
nodefs
.
Options
{
AttrTimeout
:
testTtl
,
EntryTimeout
:
&
dt
,
NegativeTimeout
:
0.0
,
AttrTimeout
:
&
dt
,
Debug
:
testutil
.
VerboseTest
(),
}
})
opts
.
Debug
=
testutil
.
VerboseTest
()
server
,
err
:=
nodefs
.
Mount
(
mountPoint
,
root
,
opts
)
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Fatalf
(
"MountNodeFileSystem failed: %v"
,
err
)
t
.
Fatalf
(
"MountNodeFileSystem failed: %v"
,
err
)
}
}
go
state
.
Serve
()
return
mountPoint
,
server
,
func
()
{
state
.
WaitMount
()
server
.
Unmount
()
return
mountPoint
,
state
,
func
()
{
state
.
Unmount
()
os
.
RemoveAll
(
mountPoint
)
os
.
RemoveAll
(
mountPoint
)
}
}
}
}
...
...
zipfs/tarfs.go
View file @
eac61bbc
...
@@ -9,11 +9,15 @@ import (
...
@@ -9,11 +9,15 @@ import (
"bytes"
"bytes"
"compress/bzip2"
"compress/bzip2"
"compress/gzip"
"compress/gzip"
"
github.com/hanwen/go-fuse/fuse
"
"
context
"
"io"
"io"
"os"
"os"
"path/filepath"
"strings"
"strings"
"syscall"
"syscall"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/nodefs"
)
)
// TODO - handle symlinks.
// TODO - handle symlinks.
...
@@ -40,9 +44,14 @@ func (f *TarFile) Data() []byte {
...
@@ -40,9 +44,14 @@ func (f *TarFile) Data() []byte {
return
f
.
data
return
f
.
data
}
}
func
NewTarTree
(
r
io
.
Reader
)
map
[
string
]
MemFile
{
type
tarRoot
struct
{
files
:=
map
[
string
]
MemFile
{}
nodefs
.
Inode
tr
:=
tar
.
NewReader
(
r
)
rc
io
.
ReadCloser
}
func
(
r
*
tarRoot
)
OnAdd
(
ctx
context
.
Context
)
{
tr
:=
tar
.
NewReader
(
r
.
rc
)
defer
r
.
rc
.
Close
()
var
longName
*
string
var
longName
*
string
for
{
for
{
...
@@ -74,35 +83,64 @@ func NewTarTree(r io.Reader) map[string]MemFile {
...
@@ -74,35 +83,64 @@ func NewTarTree(r io.Reader) map[string]MemFile {
buf
:=
bytes
.
NewBuffer
(
make
([]
byte
,
0
,
hdr
.
Size
))
buf
:=
bytes
.
NewBuffer
(
make
([]
byte
,
0
,
hdr
.
Size
))
io
.
Copy
(
buf
,
tr
)
io
.
Copy
(
buf
,
tr
)
df
:=
&
nodefs
.
MemRegularFile
{
files
[
hdr
.
Name
]
=
&
TarFile
{
Data
:
buf
.
Bytes
(),
Header
:
*
hdr
,
}
data
:
buf
.
Bytes
(),
dir
,
base
:=
filepath
.
Split
(
filepath
.
Clean
(
hdr
.
Name
))
p
:=
r
.
EmbeddedInode
()
for
_
,
comp
:=
range
strings
.
Split
(
dir
,
"/"
)
{
if
len
(
comp
)
==
0
{
continue
}
ch
:=
p
.
GetChild
(
comp
)
if
ch
==
nil
{
p
.
AddChild
(
comp
,
p
.
NewPersistentInode
(
ctx
,
&
nodefs
.
Inode
{},
nodefs
.
NodeAttr
{
Mode
:
syscall
.
S_IFDIR
}),
false
)
}
p
=
ch
}
}
HeaderToFileInfo
(
&
df
.
Attr
,
hdr
)
p
.
AddChild
(
base
,
r
.
NewPersistentInode
(
ctx
,
df
,
nodefs
.
NodeAttr
{}),
false
)
}
}
return
files
}
}
func
NewTarCompressedTree
(
name
string
,
format
string
)
(
map
[
string
]
MemFile
,
error
)
{
type
readCloser
struct
{
io
.
Reader
close
func
()
error
}
func
(
rc
*
readCloser
)
Close
()
error
{
return
rc
.
close
()
}
func
NewTarCompressedTree
(
name
string
,
format
string
)
(
nodefs
.
InodeEmbedder
,
error
)
{
f
,
err
:=
os
.
Open
(
name
)
f
,
err
:=
os
.
Open
(
name
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
defer
f
.
Close
()
defer
f
.
Close
()
var
stream
io
.
Reader
var
stream
io
.
Read
Clos
er
switch
format
{
switch
format
{
case
"gz"
:
case
"gz"
:
unzip
,
err
:=
gzip
.
NewReader
(
f
)
unzip
,
err
:=
gzip
.
NewReader
(
f
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
defer
unzip
.
Close
()
stream
=
&
readCloser
{
stream
=
unzip
unzip
,
f
.
Close
,
}
case
"bz2"
:
case
"bz2"
:
unzip
:=
bzip2
.
NewReader
(
f
)
unzip
:=
bzip2
.
NewReader
(
f
)
stream
=
unzip
stream
=
&
readCloser
{
unzip
,
f
.
Close
,
}
}
}
return
NewTarTree
(
stream
)
,
nil
return
&
tarRoot
{
rc
:
stream
}
,
nil
}
}
zipfs/zipfs.go
View file @
eac61bbc
...
@@ -7,14 +7,18 @@ package zipfs
...
@@ -7,14 +7,18 @@ package zipfs
import
(
import
(
"archive/zip"
"archive/zip"
"bytes"
"bytes"
"context"
"fmt"
"fmt"
"io"
"io"
"io/ioutil"
"os"
"os"
"path/filepath"
"path/filepath"
"strings"
"strings"
"sync"
"syscall"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/
fuse/
nodefs"
"github.com/hanwen/go-fuse/nodefs"
)
)
type
ZipFile
struct
{
type
ZipFile
struct
{
...
@@ -22,11 +26,6 @@ type ZipFile struct {
...
@@ -22,11 +26,6 @@ type ZipFile struct {
}
}
func
(
f
*
ZipFile
)
Stat
(
out
*
fuse
.
Attr
)
{
func
(
f
*
ZipFile
)
Stat
(
out
*
fuse
.
Attr
)
{
out
.
Mode
=
fuse
.
S_IFREG
|
uint32
(
f
.
File
.
Mode
())
out
.
Size
=
uint64
(
f
.
File
.
UncompressedSize
)
out
.
Mtime
=
uint64
(
f
.
File
.
ModTime
()
.
Unix
())
out
.
Atime
=
out
.
Mtime
out
.
Ctime
=
out
.
Mtime
}
}
func
(
f
*
ZipFile
)
Data
()
[]
byte
{
func
(
f
*
ZipFile
)
Data
()
[]
byte
{
...
@@ -44,41 +43,123 @@ func (f *ZipFile) Data() []byte {
...
@@ -44,41 +43,123 @@ func (f *ZipFile) Data() []byte {
return
dest
.
Bytes
()
return
dest
.
Bytes
()
}
}
type
zipRoot
struct
{
nodefs
.
Inode
zr
*
zip
.
ReadCloser
}
var
_
=
(
nodefs
.
OnAdder
)((
*
zipRoot
)(
nil
))
func
(
zr
*
zipRoot
)
OnAdd
(
ctx
context
.
Context
)
{
for
_
,
f
:=
range
zr
.
zr
.
File
{
if
f
.
FileInfo
()
.
IsDir
()
{
continue
}
dir
,
base
:=
filepath
.
Split
(
filepath
.
Clean
(
f
.
Name
))
p
:=
&
zr
.
Inode
for
_
,
component
:=
range
strings
.
Split
(
dir
,
"/"
)
{
if
len
(
component
)
==
0
{
continue
}
ch
:=
p
.
GetChild
(
component
)
if
ch
==
nil
{
ch
=
p
.
NewPersistentInode
(
ctx
,
&
nodefs
.
Inode
{},
nodefs
.
NodeAttr
{
Mode
:
fuse
.
S_IFDIR
})
p
.
AddChild
(
component
,
ch
,
true
)
}
p
=
ch
}
ch
:=
p
.
NewPersistentInode
(
ctx
,
&
zipFile
{
file
:
f
},
nodefs
.
NodeAttr
{})
p
.
AddChild
(
base
,
ch
,
true
)
}
}
// NewZipTree creates a new file-system for the zip file named name.
// NewZipTree creates a new file-system for the zip file named name.
func
NewZipTree
(
name
string
)
(
map
[
string
]
MemFile
,
error
)
{
func
NewZipTree
(
name
string
)
(
nodefs
.
InodeEmbedder
,
error
)
{
r
,
err
:=
zip
.
OpenReader
(
name
)
r
,
err
:=
zip
.
OpenReader
(
name
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
out
:=
map
[
string
]
MemFile
{}
return
&
zipRoot
{
zr
:
r
},
nil
for
_
,
f
:=
range
r
.
File
{
}
if
strings
.
HasSuffix
(
f
.
Name
,
"/"
)
{
continue
// zipFile is a file read from a zip archive.
type
zipFile
struct
{
nodefs
.
Inode
file
*
zip
.
File
mu
sync
.
Mutex
data
[]
byte
}
var
_
=
(
nodefs
.
Opener
)((
*
zipFile
)(
nil
))
var
_
=
(
nodefs
.
Getattrer
)((
*
zipFile
)(
nil
))
// Getattr sets the minimum, which is the size. A more full-featured
// FS would also set timestamps and permissions.
func
(
zf
*
zipFile
)
Getattr
(
ctx
context
.
Context
,
f
nodefs
.
FileHandle
,
out
*
fuse
.
AttrOut
)
syscall
.
Errno
{
out
.
Mode
=
uint32
(
zf
.
file
.
Mode
())
&
07777
out
.
Nlink
=
1
out
.
Mtime
=
uint64
(
zf
.
file
.
ModTime
()
.
Unix
())
out
.
Atime
=
out
.
Mtime
out
.
Ctime
=
out
.
Mtime
out
.
Size
=
zf
.
file
.
UncompressedSize64
out
.
Blocks
=
(
out
.
Size
+
511
)
/
512
return
0
}
// Open lazily unpacks zip data
func
(
zf
*
zipFile
)
Open
(
ctx
context
.
Context
,
flags
uint32
)
(
nodefs
.
FileHandle
,
uint32
,
syscall
.
Errno
)
{
zf
.
mu
.
Lock
()
defer
zf
.
mu
.
Unlock
()
if
zf
.
data
==
nil
{
rc
,
err
:=
zf
.
file
.
Open
()
if
err
!=
nil
{
return
nil
,
0
,
syscall
.
EIO
}
content
,
err
:=
ioutil
.
ReadAll
(
rc
)
if
err
!=
nil
{
return
nil
,
0
,
syscall
.
EIO
}
}
n
:=
filepath
.
Clean
(
f
.
Name
)
zf
:=
&
ZipFile
{
f
}
zf
.
data
=
content
out
[
n
]
=
zf
}
// We don't return a filehandle since we don't really need
// one. The file content is immutable, so hint the kernel to
// cache the data.
return
nil
,
fuse
.
FOPEN_KEEP_CACHE
,
0
}
// Read simply returns the data that was already unpacked in the Open call
func
(
zf
*
zipFile
)
Read
(
ctx
context
.
Context
,
f
nodefs
.
FileHandle
,
dest
[]
byte
,
off
int64
)
(
fuse
.
ReadResult
,
syscall
.
Errno
)
{
end
:=
int
(
off
)
+
len
(
dest
)
if
end
>
len
(
zf
.
data
)
{
end
=
len
(
zf
.
data
)
}
}
return
out
,
nil
return
fuse
.
ReadResultData
(
zf
.
data
[
off
:
end
]),
0
}
}
func
NewArchiveFileSystem
(
name
string
)
(
root
nodefs
.
Node
,
err
error
)
{
var
_
=
(
nodefs
.
OnAdder
)((
*
zipRoot
)(
nil
))
var
files
map
[
string
]
MemFile
func
NewArchiveFileSystem
(
name
string
)
(
root
nodefs
.
InodeEmbedder
,
err
error
)
{
switch
{
switch
{
case
strings
.
HasSuffix
(
name
,
".zip"
)
:
case
strings
.
HasSuffix
(
name
,
".zip"
)
:
files
,
err
=
NewZipTree
(
name
)
root
,
err
=
NewZipTree
(
name
)
case
strings
.
HasSuffix
(
name
,
".tar.gz"
)
:
case
strings
.
HasSuffix
(
name
,
".tar.gz"
)
:
files
,
err
=
NewTarCompressedTree
(
name
,
"gz"
)
root
,
err
=
NewTarCompressedTree
(
name
,
"gz"
)
case
strings
.
HasSuffix
(
name
,
".tar.bz2"
)
:
case
strings
.
HasSuffix
(
name
,
".tar.bz2"
)
:
files
,
err
=
NewTarCompressedTree
(
name
,
"bz2"
)
root
,
err
=
NewTarCompressedTree
(
name
,
"bz2"
)
case
strings
.
HasSuffix
(
name
,
".tar"
)
:
case
strings
.
HasSuffix
(
name
,
".tar"
)
:
f
,
err
:=
os
.
Open
(
name
)
f
,
err
:=
os
.
Open
(
name
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
files
=
NewTarTree
(
f
)
root
=
&
tarRoot
{
rc
:
f
}
default
:
default
:
return
nil
,
fmt
.
Errorf
(
"unknown archive format %q"
,
name
)
return
nil
,
fmt
.
Errorf
(
"unknown archive format %q"
,
name
)
}
}
...
@@ -87,7 +168,5 @@ func NewArchiveFileSystem(name string) (root nodefs.Node, err error) {
...
@@ -87,7 +168,5 @@ func NewArchiveFileSystem(name string) (root nodefs.Node, err error) {
return
nil
,
err
return
nil
,
err
}
}
mfs
:=
NewMemTreeFs
(
files
)
return
root
,
nil
mfs
.
Name
=
fmt
.
Sprintf
(
"fs(%s)"
,
name
)
return
mfs
.
Root
(),
nil
}
}
zipfs/zipfs_test.go
View file @
eac61bbc
...
@@ -14,8 +14,8 @@ import (
...
@@ -14,8 +14,8 @@ import (
"time"
"time"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/fuse/nodefs"
"github.com/hanwen/go-fuse/internal/testutil"
"github.com/hanwen/go-fuse/internal/testutil"
"github.com/hanwen/go-fuse/nodefs"
)
)
func
testZipFile
()
string
{
func
testZipFile
()
string
{
...
@@ -34,15 +34,12 @@ func setupZipfs(t *testing.T) (mountPoint string, cleanup func()) {
...
@@ -34,15 +34,12 @@ func setupZipfs(t *testing.T) (mountPoint string, cleanup func()) {
}
}
mountPoint
=
testutil
.
TempDir
()
mountPoint
=
testutil
.
TempDir
()
state
,
_
,
err
:=
nodefs
.
MountRoot
(
mountPoint
,
root
,
&
nodefs
.
Options
{
opts
:=
&
nodefs
.
Options
{}
Debug
:
testutil
.
VerboseTest
(),
opts
.
Debug
=
testutil
.
VerboseTest
()
})
server
,
err
:=
nodefs
.
Mount
(
mountPoint
,
root
,
opts
)
go
state
.
Serve
()
state
.
WaitMount
()
return
mountPoint
,
func
()
{
return
mountPoint
,
func
()
{
s
tate
.
Unmount
()
s
erver
.
Unmount
()
os
.
RemoveAll
(
mountPoint
)
os
.
RemoveAll
(
mountPoint
)
}
}
}
}
...
@@ -70,19 +67,17 @@ func TestZipFs(t *testing.T) {
...
@@ -70,19 +67,17 @@ func TestZipFs(t *testing.T) {
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Fatalf
(
"Stat failed: %v"
,
err
)
t
.
Fatalf
(
"Stat failed: %v"
,
err
)
}
}
if
fi
.
Mode
()
!=
0664
{
if
got
,
want
:=
fi
.
Mode
(),
0664
;
int
(
got
)
!=
want
{
t
.
Fatalf
(
"File mode
0%o != 0664"
,
fi
.
Mode
()
)
t
.
Fatalf
(
"File mode
: got 0%o want 0%o"
,
got
,
want
)
}
}
if
st
:=
fi
.
Sys
()
.
(
*
syscall
.
Stat_t
);
st
.
Blocks
!=
1
{
if
st
:=
fi
.
Sys
()
.
(
*
syscall
.
Stat_t
);
st
.
Blocks
!=
1
{
t
.
Errorf
(
"got block count %d, want 1"
,
st
.
Blocks
)
t
.
Errorf
(
"got block count %d, want 1"
,
st
.
Blocks
)
}
}
mtime
,
err
:=
time
.
Parse
(
time
.
RFC3339
,
"2011-02-22T12:56:12Z"
)
if
want
,
err
:=
time
.
Parse
(
time
.
RFC3339
,
"2011-02-22T12:56:12Z"
);
err
!=
nil
{
if
err
!=
nil
{
panic
(
err
)
panic
(
err
)
}
}
else
if
!
fi
.
ModTime
()
.
Equal
(
want
)
{
if
!
fi
.
ModTime
()
.
Equal
(
mtime
)
{
t
.
Fatalf
(
"File mtime got %v, want %v"
,
fi
.
ModTime
(),
want
)
t
.
Fatalf
(
"File mtime %v != %v"
,
fi
.
ModTime
(),
mtime
)
}
}
if
fi
.
IsDir
()
{
if
fi
.
IsDir
()
{
...
...
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