Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e8a8c531a9 | |||
| 80ff4466ae | |||
| 64055a8a63 | |||
| 0402b0a2ee | |||
| 305f261d10 | |||
| 70e3d81427 | |||
| 6ad6857d8d | |||
| 7cf15d48c7 | |||
| 28f39cf315 | |||
| 7f5fa3ba1f | |||
| 2a8310a724 | |||
| 8ad4d30bc7 | |||
| b6fbd63ba4 | |||
| 9913b848c6 | |||
| 65bc4a5d78 |
@@ -4,15 +4,25 @@
|
||||
|
||||
A PURE Go library to read and write squashfs.
|
||||
|
||||
Currently has support for reading squashfs files and extracting files and folders. Supports all compression types except LZO, but additional compression options are hit or miss.
|
||||
|
||||
The only major thing missing from squashfs reading is Xattr parsing.
|
||||
Currently has support for reading squashfs files and extracting files and folders.
|
||||
|
||||
Special thanks to <https://dr-emann.github.io/squashfs/> for some VERY important information in an easy to understand format.
|
||||
Thanks also to [distri's squashfs library](https://github.com/distr1/distri/tree/master/internal/squashfs) as I referenced it to figure some things out (and double check others).
|
||||
|
||||
## [TODO](https://github.com/CalebQ42/squashfs/projects/1?fullscreen=true)
|
||||
|
||||
## Limitations
|
||||
|
||||
This library is pure Go (including external libraries) which can cause some issues, which are listed below. Right now this library is also not feature complete, so check out the TODO list above for what I'm still planning on adding.
|
||||
|
||||
* No LZO compression. This is purely due to a lack of a good LZO pure golang library. If one is made, it would be a simple job to add it in.
|
||||
* GZIP Compression
|
||||
* Strategies might or might not work.
|
||||
* Custom window sizes might or might not work.
|
||||
* XZ Compression
|
||||
* LZMA executable filters are NOT supported.
|
||||
* No Xattr parsing. This is simply because I haven't done any research on it and how to apply these in a pure go way.
|
||||
|
||||
## Performane
|
||||
|
||||
This library, decompressing the Firefox AppImage and using go tests, takes about twice as long as `unsquashfs` on my quad core laptop. (~1 second with the library and about half a second with `unsquashfs`)
|
||||
|
||||
## [TODO](https://github.com/CalebQ42/squashfs/projects/1?fullscreen=true)
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
package squashfs
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
type bufferedBytes struct {
|
||||
data []byte
|
||||
r offsetRange
|
||||
}
|
||||
|
||||
type offsetRange struct {
|
||||
beg int
|
||||
end int
|
||||
}
|
||||
|
||||
func (o *offsetRange) offset(off int) {
|
||||
o.beg += off
|
||||
o.end += off
|
||||
}
|
||||
|
||||
func (o offsetRange) within(check int) bool {
|
||||
return check >= o.beg || check <= o.end
|
||||
}
|
||||
|
||||
type bufferedWriter struct {
|
||||
w io.Writer
|
||||
buffer []bufferedBytes
|
||||
mainOffset int
|
||||
}
|
||||
|
||||
func newBufferedWriter(w io.Writer) *bufferedWriter {
|
||||
var out bufferedWriter
|
||||
out.w = w
|
||||
return &out
|
||||
}
|
||||
|
||||
func (b *bufferedWriter) WriteTo(data []byte, offset int64) (n int, err error) {
|
||||
if int(offset) == b.mainOffset {
|
||||
n, err = b.Write(data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
newBuff := bufferedBytes{
|
||||
data: data,
|
||||
r: offsetRange{
|
||||
beg: int(offset),
|
||||
end: int(offset) + len(data),
|
||||
},
|
||||
}
|
||||
b.buffer = append(b.buffer, newBuff)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (b *bufferedWriter) Write(data []byte) (int, error) {
|
||||
n, err := b.w.Write(data)
|
||||
b.mainOffset += n
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
+2
-2
@@ -12,7 +12,7 @@ import (
|
||||
type fragmentEntry struct {
|
||||
Start uint64
|
||||
Size uint32
|
||||
// Unused uint32
|
||||
_ uint32 //unused
|
||||
}
|
||||
|
||||
//GetFragmentDataFromInode returns the fragment data for a given inode.
|
||||
@@ -46,7 +46,7 @@ func (r *Reader) getFragmentDataFromInode(in *inode.Inode) ([]byte, error) {
|
||||
fragIndex = bf.FragmentIndex
|
||||
fragOffset = bf.FragmentOffset
|
||||
} else {
|
||||
return nil, errors.New("Inode type not supported")
|
||||
return nil, errors.New("inode type not supported")
|
||||
}
|
||||
//reading the fragment entry first
|
||||
fragEntryRdr, err := r.newMetadataReader(int64(r.fragOffsets[int(fragIndex/512)]))
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
module github.com/CalebQ42/squashfs
|
||||
|
||||
go 1.15
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/CalebQ42/GoAppImage v0.5.0
|
||||
github.com/adrg/xdg v0.2.3 // indirect
|
||||
github.com/google/go-cmp v0.5.4 // indirect
|
||||
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect
|
||||
github.com/klauspost/compress v1.11.6
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.3
|
||||
github.com/adrg/xdg v0.3.3 // indirect
|
||||
github.com/gopherjs/gopherjs v0.0.0-20210420193930-a4630ec28c79 // indirect
|
||||
github.com/klauspost/compress v1.12.2
|
||||
github.com/pierrec/lz4/v4 v4.1.6
|
||||
github.com/smartystreets/assertions v1.2.0 // indirect
|
||||
github.com/stretchr/testify v1.7.0 // indirect
|
||||
github.com/ulikunitz/xz v0.5.9
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
github.com/ulikunitz/xz v0.5.10
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/golang/snappy v0.0.3 // indirect
|
||||
github.com/google/go-cmp v0.5.5 // indirect
|
||||
github.com/rasky/go-lzo v0.0.0-20200203143853-96a758eda86e
|
||||
github.com/therootcompany/xz v1.0.1
|
||||
go.lsp.dev/uri v0.3.0 // indirect
|
||||
gopkg.in/ini.v1 v1.62.0 // indirect
|
||||
)
|
||||
|
||||
@@ -1,76 +1,348 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/CalebQ42/GoAppImage v0.5.0 h1:znoKNXtliH754tS9sYwyOIg/0wFDjFN5Twc7PAh1rSM=
|
||||
github.com/CalebQ42/GoAppImage v0.5.0/go.mod h1:qHudJKAn/dlkNWNnH4h1YKXp29EZ7Bppsn7sNP2HuvU=
|
||||
github.com/adrg/xdg v0.2.2 h1:A7ZHKRz5KGOLJX/bg7IPzStryhvCzAE1wX+KWawPiAo=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/adrg/xdg v0.2.2/go.mod h1:7I2hH/IT30IsupOpKZ5ue7/qNi3CoKzD6tL3HwpaRMQ=
|
||||
github.com/adrg/xdg v0.2.3 h1:GxXngdYxNDkoUvZXjNJGwqZxWXi43MKbOOlA/00qZi4=
|
||||
github.com/adrg/xdg v0.2.3/go.mod h1:7I2hH/IT30IsupOpKZ5ue7/qNi3CoKzD6tL3HwpaRMQ=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/adrg/xdg v0.3.3 h1:s/tV7MdqQnzB1nKY8aqHvAMD+uCiuEDzVB5HLRY849U=
|
||||
github.com/adrg/xdg v0.3.3/go.mod h1:61xAR2VZcggl2St4O9ohF5qCKe08+JDmE4VNzPFQvOQ=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 h1:l5lAOZEym3oK3SQ2HBHWsJUfbNBiTXJDeW2QDxw9AQ0=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20210420193930-a4630ec28c79 h1:ATVz3rDvK4xX0nHx57zYSHRVIK/+lFwln9KJr8wvuk0=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20210420193930-a4630ec28c79/go.mod h1:Opf9rtYVq0eTyX+aRVmRO9hE8ERAozcdrBxWG9Q6mkQ=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/klauspost/compress v1.11.6 h1:EgWPCW6O3n1D5n99Zq3xXBt9uCwRGvpwGOusOLNBRSQ=
|
||||
github.com/klauspost/compress v1.11.6/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.12.2 h1:2KCfW3I9M7nSc5wOqXAlW2v2U6v+w6cbjvbfp+OykW8=
|
||||
github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||
github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pierrec/lz4/v4 v4.1.3 h1:/dvQpkb0o1pVlSgKNQqfkavlnXaIK+hJ0LXsKRUN9D4=
|
||||
github.com/pierrec/lz4/v4 v4.1.3/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pierrec/lz4/v4 v4.1.6 h1:ueMTcBBFrbT8K4uGDNNZPa8Z7LtPV7Cl0TDjaeHxP44=
|
||||
github.com/pierrec/lz4/v4 v4.1.6/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rasky/go-lzo v0.0.0-20200203143853-96a758eda86e h1:dCWirM5F3wMY+cmRda/B1BiPsFtmzXqV9b0hLWtVBMs=
|
||||
github.com/rasky/go-lzo v0.0.0-20200203143853-96a758eda86e/go.mod h1:9leZcVcItj6m9/CfHY5Em/iBrCz7js8LcRQGTKEEv2M=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs=
|
||||
github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/ulikunitz/xz v0.5.9 h1:RsKRIA2MO8x56wkkcd3LbtcE/uMszhb6DpRf+3uwa3I=
|
||||
github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
|
||||
github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
|
||||
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.lsp.dev/uri v0.3.0 h1:KcZJmh6nFIBeJzTugn5JTU6OOyG0lDOo3R9KwTxTYbo=
|
||||
go.lsp.dev/uri v0.3.0/go.mod h1:P5sbO1IQR+qySTWOCnhnK7phBx+W3zbLqSMDJNTw88I=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384 h1:TFlARGu6Czu1z7q93HTxcP1P+/ZFC/IKythI5RzrnRg=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
|
||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
|
||||
@@ -2,9 +2,10 @@ package compression
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
"github.com/klauspost/compress/zlib"
|
||||
)
|
||||
|
||||
type gzipInit struct {
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package compression
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
lzo "github.com/rasky/go-lzo"
|
||||
)
|
||||
|
||||
type Lzo struct {
|
||||
Algorithm int32
|
||||
Level int32
|
||||
}
|
||||
|
||||
func NewLzoCompressorWithOptions(rdr io.Reader) (*Lzo, error) {
|
||||
var lz Lzo
|
||||
err := binary.Read(rdr, binary.LittleEndian, &lz)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &lz, nil
|
||||
}
|
||||
|
||||
func (l Lzo) Decompress(rdr io.Reader) ([]byte, error) {
|
||||
byt, err := lzo.Decompress1X(rdr, 0, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return byt, nil
|
||||
}
|
||||
@@ -5,44 +5,29 @@ import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
"github.com/ulikunitz/xz"
|
||||
"github.com/therootcompany/xz"
|
||||
|
||||
wrtXz "github.com/ulikunitz/xz"
|
||||
)
|
||||
|
||||
type xzInit struct {
|
||||
DictionarySize int32
|
||||
Filters int32
|
||||
}
|
||||
|
||||
//Xz is a Xz decompressor.
|
||||
type Xz struct {
|
||||
DictionarySize int32
|
||||
HasFilters bool
|
||||
Filters int32
|
||||
}
|
||||
|
||||
//NewXzCompressorWithOptions creates a new Xz compressor/decompressor that reads the compressor options from the given reader.
|
||||
func NewXzCompressorWithOptions(rdr io.Reader) (*Xz, error) {
|
||||
var x Xz
|
||||
var init xzInit
|
||||
err := binary.Read(rdr, binary.LittleEndian, &init)
|
||||
err := binary.Read(rdr, binary.LittleEndian, &x)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x.DictionarySize = init.DictionarySize
|
||||
//TODO: When I can do filters, parse the filters
|
||||
if init.Filters != 0 {
|
||||
x.HasFilters = true
|
||||
}
|
||||
return &x, nil
|
||||
}
|
||||
|
||||
//Decompress decompresses all the data from the rdr and returns the uncompressed bytes.
|
||||
func (x *Xz) Decompress(rdr io.Reader) ([]byte, error) {
|
||||
r, err := xz.NewReader(rdr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.DictCap = int(x.DictionarySize)
|
||||
err = r.Verify()
|
||||
r, err := xz.NewReader(rdr, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -57,7 +42,7 @@ func (x *Xz) Decompress(rdr io.Reader) ([]byte, error) {
|
||||
//Compress implements compression.Compress
|
||||
func (x *Xz) Compress(data []byte) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
w, err := xz.NewWriter(&buf)
|
||||
w, err := wrtXz.NewWriter(&buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
//
|
||||
//Info holds the actual Inode. Due to each inode type being a different type, it's store as an interface{}
|
||||
type Inode struct {
|
||||
Info interface{} //Info is the parsed specific data. It's type is defined by Type.
|
||||
Header
|
||||
Info interface{} //Info is the parsed specific data. It's type is defined by Type.
|
||||
}
|
||||
|
||||
//ProcessInode tries to read an inode from the BlockReader
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
package rawreader
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
func ConvertReader(r io.Reader) (RawReader, error) {
|
||||
if rr, ok := r.(RawReader); ok {
|
||||
return rr, nil
|
||||
}
|
||||
if rs, is := r.(io.ReadSeeker); is {
|
||||
return &fromReadSeeker{
|
||||
ReadSeeker: rs,
|
||||
}, nil
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
_, err := io.Copy(buf, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &fromReader{
|
||||
data: buf.Bytes(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ConvertReaderAt(r io.ReaderAt) RawReader {
|
||||
if rr, ok := r.(RawReader); ok {
|
||||
return rr
|
||||
}
|
||||
return &fromReaderAt{
|
||||
ReaderAt: r,
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Add way to discard data from fromReader
|
||||
//RawReader implements the needed interfaces for reading a squashfs archive.
|
||||
type RawReader interface {
|
||||
io.ReadSeeker
|
||||
io.ReaderAt
|
||||
}
|
||||
|
||||
type fromReader struct {
|
||||
data []byte
|
||||
off int
|
||||
}
|
||||
|
||||
func (r *fromReader) ReadAt(p []byte, off int64) (n int, err error) {
|
||||
n = len(p)
|
||||
if int(off)+len(p) > len(r.data) {
|
||||
n = len(r.data) - int(off)
|
||||
err = io.EOF
|
||||
}
|
||||
if n < 0 {
|
||||
n = 0
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
p[i] = r.data[int(off)+i]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (r *fromReader) Seek(off int64, whence int) (n int64, err error) {
|
||||
switch whence {
|
||||
case io.SeekEnd:
|
||||
r.off = len(r.data) - int(off)
|
||||
if r.off < 0 {
|
||||
r.off = 0
|
||||
err = io.EOF
|
||||
}
|
||||
case io.SeekCurrent:
|
||||
r.off += int(off)
|
||||
case io.SeekStart:
|
||||
r.off = int(off)
|
||||
}
|
||||
if r.off > len(r.data) {
|
||||
r.off = len(r.data)
|
||||
return int64(r.off), io.EOF
|
||||
}
|
||||
return int64(r.off), err
|
||||
}
|
||||
|
||||
func (r *fromReader) Read(p []byte) (n int, err error) {
|
||||
n = len(p)
|
||||
if r.off+len(p) > len(r.data) {
|
||||
n = len(r.data) - r.off
|
||||
err = io.EOF
|
||||
}
|
||||
if n < 0 {
|
||||
n = 0
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
p[i] = r.data[r.off+i]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type fromReadSeeker struct {
|
||||
io.ReadSeeker
|
||||
}
|
||||
|
||||
func (r *fromReadSeeker) ReadAt(p []byte, off int64) (n int, err error) {
|
||||
tmp, _ := r.Seek(0, io.SeekCurrent)
|
||||
defer r.Seek(tmp, io.SeekStart)
|
||||
_, err = r.Seek(off, io.SeekStart)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.Read(p)
|
||||
}
|
||||
|
||||
type fromReaderAt struct {
|
||||
io.ReaderAt
|
||||
|
||||
off int
|
||||
}
|
||||
|
||||
func (r *fromReaderAt) Read(p []byte) (n int, err error) {
|
||||
n, err = r.ReadAt(p, int64(r.off))
|
||||
r.off += n
|
||||
return
|
||||
}
|
||||
|
||||
func (r *fromReaderAt) Seek(off int64, whence int) (n int64, err error) {
|
||||
switch whence {
|
||||
case io.SeekEnd:
|
||||
return 0, errors.New("cannot SeekEnd RawReader")
|
||||
case io.SeekCurrent:
|
||||
r.off += int(off)
|
||||
case io.SeekStart:
|
||||
r.off = int(off)
|
||||
}
|
||||
return int64(r.off), nil
|
||||
}
|
||||
+2
-2
@@ -133,7 +133,7 @@ func (br *metadataReader) Read(p []byte) (int, error) {
|
||||
}
|
||||
br.readOffset += read
|
||||
if read != len(p) {
|
||||
return read, errors.New("Didn't read enough data")
|
||||
return read, errors.New("didn't read enough data")
|
||||
}
|
||||
return read, nil
|
||||
}
|
||||
@@ -181,7 +181,7 @@ func (br *metadataReader) Seek(offset int64, whence int) (int64, error) {
|
||||
br.readOffset = len(br.data) - int(offset)
|
||||
if br.readOffset < 0 {
|
||||
br.readOffset = 0
|
||||
return int64(br.readOffset), errors.New("Trying to seek to a negative value")
|
||||
return int64(br.readOffset), errors.New("trying to seek to a negative value")
|
||||
}
|
||||
}
|
||||
return int64(br.readOffset), nil
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/CalebQ42/squashfs/internal/compression"
|
||||
"github.com/CalebQ42/squashfs/internal/inode"
|
||||
"github.com/CalebQ42/squashfs/internal/rawreader"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -17,23 +18,16 @@ const (
|
||||
|
||||
var (
|
||||
//ErrNoMagic is returned if the magic number in the superblock isn't correct.
|
||||
errNoMagic = errors.New("Magic number doesn't match. Either isn't a squashfs or corrupted")
|
||||
errNoMagic = errors.New("magic number doesn't match. Either isn't a squashfs or corrupted")
|
||||
//ErrIncompatibleCompression is returned if the compression type in the superblock doesn't work.
|
||||
errIncompatibleCompression = errors.New("Compression type unsupported")
|
||||
//ErrCompressorOptions is returned if compressor options is present. It's not currently supported.
|
||||
errCompressorOptions = errors.New("Compressor options is not currently supported")
|
||||
//ErrOptions is returned when compression options that I haven't tested is set. When this is returned, the Reader is also returned.
|
||||
ErrOptions = errors.New("Possibly incompatible compressor options")
|
||||
errIncompatibleCompression = errors.New("compression type unsupported")
|
||||
)
|
||||
|
||||
//TODO: implement fs.FS, possibly more FS types for compatibility. Most of this work will mostly be handed off to root anyway so this shouldn't be too difficult.
|
||||
|
||||
//Reader processes and reads a squashfs archive.
|
||||
type Reader struct {
|
||||
FS
|
||||
r *io.SectionReader
|
||||
r rawreader.RawReader
|
||||
decompressor compression.Decompressor
|
||||
root *File
|
||||
fragOffsets []uint64
|
||||
idTable []uint32
|
||||
super superblock
|
||||
@@ -43,134 +37,163 @@ type Reader struct {
|
||||
//NewSquashfsReader returns a new squashfs.Reader from an io.ReaderAt
|
||||
func NewSquashfsReader(r io.ReaderAt) (*Reader, error) {
|
||||
var rdr Reader
|
||||
err := binary.Read(io.NewSectionReader(r, 0, int64(binary.Size(rdr.super))), binary.LittleEndian, &rdr.super)
|
||||
rdr.r = rawreader.ConvertReaderAt(r)
|
||||
err := rdr.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rdr.r = io.NewSectionReader(r, 0, int64(rdr.super.BytesUsed))
|
||||
if rdr.super.Magic != magic {
|
||||
return nil, errNoMagic
|
||||
return &rdr, nil
|
||||
}
|
||||
|
||||
//NewSquashfsReaderFromReader returns a new squashfs.Reader from an io.Reader.
|
||||
//If the io.Reader implements io.Seeker, the seek functions are used.
|
||||
//It is NOT recommended to use a pure io.Reader as due to how squashfs
|
||||
//archives are formatted, the ENTIRETY of the io.Reader's data is loaded into
|
||||
//memory first before it can be used.
|
||||
func NewSquashfsReaderFromReader(r io.Reader) (*Reader, error) {
|
||||
var rdr Reader
|
||||
var err error
|
||||
rdr.r, err = rawreader.ConvertReader(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if rdr.super.BlockLog != uint16(math.Log2(float64(rdr.super.BlockSize))) {
|
||||
return nil, errors.New("BlockSize and BlockLog doesn't match. The archive is probably corrupt")
|
||||
err = rdr.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rdr.r.Seek(96, io.SeekStart)
|
||||
hasUnsupportedOptions := false
|
||||
rdr.flags = rdr.super.GetFlags()
|
||||
if rdr.flags.compressorOptions {
|
||||
switch rdr.super.CompressionType {
|
||||
return &rdr, nil
|
||||
}
|
||||
|
||||
func (r *Reader) Init() error {
|
||||
err := binary.Read(r.r, binary.LittleEndian, &r.super)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if r.super.Magic != magic {
|
||||
return errNoMagic
|
||||
}
|
||||
if r.super.BlockLog != uint16(math.Log2(float64(r.super.BlockSize))) {
|
||||
return errors.New("BlockSize and BlockLog doesn't match. The archive is probably corrupt")
|
||||
}
|
||||
r.r.Seek(96, io.SeekStart)
|
||||
r.flags = r.super.GetFlags()
|
||||
if r.flags.compressorOptions {
|
||||
switch r.super.CompressionType {
|
||||
case GzipCompression:
|
||||
var gzip *compression.Gzip
|
||||
gzip, err = compression.NewGzipCompressorWithOptions(rdr.r)
|
||||
gzip, err = compression.NewGzipCompressorWithOptions(r.r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
if gzip.HasCustomWindow || gzip.HasStrategies {
|
||||
hasUnsupportedOptions = true
|
||||
}
|
||||
rdr.decompressor = gzip
|
||||
r.decompressor = gzip
|
||||
case XzCompression:
|
||||
var xz *compression.Xz
|
||||
xz, err = compression.NewXzCompressorWithOptions(rdr.r)
|
||||
xz, err = compression.NewXzCompressorWithOptions(r.r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
if xz.HasFilters {
|
||||
return nil, errors.New("XZ compression options has filters. These are not yet supported")
|
||||
r.decompressor = xz
|
||||
case LzoCompression:
|
||||
var lz *compression.Lzo
|
||||
lz, err = compression.NewLzoCompressorWithOptions(r.r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rdr.decompressor = xz
|
||||
r.decompressor = lz
|
||||
case Lz4Compression:
|
||||
var lz4 *compression.Lz4
|
||||
lz4, err = compression.NewLz4CompressorWithOptions(rdr.r)
|
||||
lz4, err = compression.NewLz4CompressorWithOptions(r.r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
rdr.decompressor = lz4
|
||||
r.decompressor = lz4
|
||||
case ZstdCompression:
|
||||
var zstd *compression.Zstd
|
||||
zstd, err = compression.NewZstdCompressorWithOptions(rdr.r)
|
||||
zstd, err = compression.NewZstdCompressorWithOptions(r.r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
rdr.decompressor = zstd
|
||||
r.decompressor = zstd
|
||||
default:
|
||||
return nil, errIncompatibleCompression
|
||||
return errIncompatibleCompression
|
||||
}
|
||||
} else {
|
||||
switch rdr.super.CompressionType {
|
||||
switch r.super.CompressionType {
|
||||
case GzipCompression:
|
||||
rdr.decompressor = &compression.Gzip{}
|
||||
r.decompressor = &compression.Gzip{}
|
||||
case LzmaCompression:
|
||||
rdr.decompressor = &compression.Lzma{}
|
||||
r.decompressor = &compression.Lzma{}
|
||||
case LzoCompression:
|
||||
r.decompressor = &compression.Lzo{}
|
||||
case XzCompression:
|
||||
rdr.decompressor = &compression.Xz{}
|
||||
r.decompressor = &compression.Xz{}
|
||||
case Lz4Compression:
|
||||
rdr.decompressor = &compression.Lz4{}
|
||||
r.decompressor = &compression.Lz4{}
|
||||
case ZstdCompression:
|
||||
rdr.decompressor = &compression.Zstd{}
|
||||
r.decompressor = &compression.Zstd{}
|
||||
default:
|
||||
//TODO: all compression types.
|
||||
return nil, errIncompatibleCompression
|
||||
return errIncompatibleCompression
|
||||
}
|
||||
}
|
||||
fragBlocks := int(math.Ceil(float64(rdr.super.FragCount) / 512))
|
||||
fragBlocks := int(math.Ceil(float64(r.super.FragCount) / 512))
|
||||
if fragBlocks > 0 {
|
||||
offset := int64(rdr.super.FragTableStart)
|
||||
offset := int64(r.super.FragTableStart)
|
||||
for i := 0; i < fragBlocks; i++ {
|
||||
tmp := make([]byte, 8)
|
||||
_, err = r.ReadAt(tmp, offset)
|
||||
_, err = r.r.ReadAt(tmp, offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
rdr.fragOffsets = append(rdr.fragOffsets, binary.LittleEndian.Uint64(tmp))
|
||||
r.fragOffsets = append(r.fragOffsets, binary.LittleEndian.Uint64(tmp))
|
||||
offset += 8
|
||||
}
|
||||
}
|
||||
unread := rdr.super.IDCount
|
||||
blockOffsets := make([]uint64, int(math.Ceil(float64(rdr.super.IDCount)/2048)))
|
||||
rdr.r.Seek(int64(rdr.super.IDTableStart), io.SeekStart)
|
||||
unread := r.super.IDCount
|
||||
blockOffsets := make([]uint64, int(math.Ceil(float64(r.super.IDCount)/2048)))
|
||||
_, err = r.r.Seek(int64(r.super.IDTableStart), io.SeekStart)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range blockOffsets {
|
||||
err = binary.Read(rdr.r, binary.LittleEndian, &blockOffsets[i])
|
||||
err = binary.Read(r.r, binary.LittleEndian, &blockOffsets[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
var idRdr *metadataReader
|
||||
idRdr, err = rdr.newMetadataReader(int64(blockOffsets[i]))
|
||||
idRdr, err = r.newMetadataReader(int64(blockOffsets[i]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
read := uint16(math.Min(float64(unread), 2048))
|
||||
for i := uint16(0); i < read; i++ {
|
||||
var tmp uint32
|
||||
err = binary.Read(idRdr, binary.LittleEndian, &tmp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
rdr.idTable = append(rdr.idTable, tmp)
|
||||
r.idTable = append(r.idTable, tmp)
|
||||
}
|
||||
unread -= read
|
||||
}
|
||||
metaRdr, err := rdr.newMetadataReaderFromInodeRef(rdr.super.RootInodeRef)
|
||||
metaRdr, err := r.newMetadataReaderFromInodeRef(r.super.RootInodeRef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
i, err := inode.ProcessInode(metaRdr, rdr.super.BlockSize)
|
||||
i, err := inode.ProcessInode(metaRdr, r.super.BlockSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
entries, err := rdr.readDirFromInode(i)
|
||||
entries, err := r.readDirFromInode(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
rdr.FS = FS{
|
||||
r: &rdr,
|
||||
r.FS = FS{
|
||||
r: r,
|
||||
name: "/",
|
||||
entries: entries,
|
||||
}
|
||||
if hasUnsupportedOptions {
|
||||
return &rdr, ErrOptions
|
||||
}
|
||||
return &rdr, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
//ModTime is the last time the file was modified/created.
|
||||
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
|
||||
var (
|
||||
//ErrInodeNotFile is given when giving an inode, but the function requires a file inode.
|
||||
errInodeNotFile = errors.New("Given inode is NOT a file type")
|
||||
errInodeNotFile = errors.New("given inode is NOT a file type")
|
||||
//ErrInodeOnlyFragment is given when trying to make a DataReader from an inode, but the inode only had data in a fragment
|
||||
errInodeOnlyFragment = errors.New("Given inode ONLY has fragment data")
|
||||
errInodeOnlyFragment = errors.New("given inode ONLY has fragment data")
|
||||
)
|
||||
|
||||
//DataReader reads data from data blocks.
|
||||
@@ -49,9 +49,7 @@ func (r *Reader) newDataReaderFromInode(i *inode.Inode) (*dataReader, error) {
|
||||
return nil, errInodeOnlyFragment
|
||||
}
|
||||
rdr.offset = int64(fil.BlockStart)
|
||||
for _, sizes := range fil.BlockSizes {
|
||||
rdr.sizes = append(rdr.sizes, sizes)
|
||||
}
|
||||
rdr.sizes = append(rdr.sizes, fil.BlockSizes...)
|
||||
if fil.Fragmented {
|
||||
rdr.sizes = rdr.sizes[:len(rdr.sizes)-1]
|
||||
}
|
||||
@@ -61,9 +59,7 @@ func (r *Reader) newDataReaderFromInode(i *inode.Inode) (*dataReader, error) {
|
||||
return nil, errInodeOnlyFragment
|
||||
}
|
||||
rdr.offset = int64(fil.BlockStart)
|
||||
for _, sizes := range fil.BlockSizes {
|
||||
rdr.sizes = append(rdr.sizes, sizes)
|
||||
}
|
||||
rdr.sizes = append(rdr.sizes, fil.BlockSizes...)
|
||||
if fil.Fragmented {
|
||||
rdr.sizes = rdr.sizes[:len(rdr.sizes)-1]
|
||||
}
|
||||
@@ -173,7 +169,7 @@ func (d *dataReader) Read(p []byte) (int, error) {
|
||||
}
|
||||
}
|
||||
if read != len(p) {
|
||||
return read, errors.New("Didn't read enough data")
|
||||
return read, errors.New("didn't read enough data")
|
||||
}
|
||||
return read, nil
|
||||
}
|
||||
@@ -199,7 +195,6 @@ func (d *dataReader) WriteTo(w io.Writer) (int64, error) {
|
||||
return
|
||||
}
|
||||
cache.data = data
|
||||
return
|
||||
}(i, dataChan)
|
||||
}
|
||||
curIndex := 0
|
||||
@@ -78,7 +78,7 @@ func (f File) Read(p []byte) (int, error) {
|
||||
}
|
||||
return f.reader.Read(p)
|
||||
}
|
||||
return 0, errors.New("Can only read files")
|
||||
return 0, errors.New("can only read files")
|
||||
}
|
||||
|
||||
//WriteTo writes all data from the file to the writer. This is multi-threaded.
|
||||
@@ -89,7 +89,7 @@ func (f File) WriteTo(w io.Writer) (int64, error) {
|
||||
}
|
||||
return f.reader.WriteTo(w)
|
||||
}
|
||||
return 0, errors.New("Can only read files")
|
||||
return 0, errors.New("can only read files")
|
||||
}
|
||||
|
||||
//Close simply nils the underlying reader. Here mostly to satisfy fs.File
|
||||
@@ -236,6 +236,9 @@ func (f File) ExtractWithOptions(folder string, op ExtractionOptions) error {
|
||||
}
|
||||
}
|
||||
stat, err := f.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if f.IsDir() {
|
||||
if op.notBase {
|
||||
err = os.Mkdir(folder+"/"+f.name, stat.Mode())
|
||||
@@ -264,7 +267,6 @@ func (f File) ExtractWithOptions(folder string, op ExtractionOptions) error {
|
||||
}
|
||||
errChan <- fil.ExtractWithOptions(folder+"/"+f.name, op)
|
||||
fil.Close()
|
||||
return
|
||||
}(ents[i].(*DirEntry))
|
||||
}
|
||||
for i := 0; i < len(ents); i++ {
|
||||
@@ -301,7 +303,7 @@ func (f File) ExtractWithOptions(folder string, op ExtractionOptions) error {
|
||||
if op.Verbose {
|
||||
log.Println("Symlink path(", symPath, ") is unobtainable:", folder+"/"+f.name)
|
||||
}
|
||||
return errors.New("Cannot get symlink target")
|
||||
return errors.New("cannot get symlink target")
|
||||
}
|
||||
fil.name = f.name
|
||||
err = fil.ExtractWithOptions(folder, op)
|
||||
@@ -318,7 +320,7 @@ func (f File) ExtractWithOptions(folder string, op ExtractionOptions) error {
|
||||
if op.Verbose {
|
||||
log.Println("Symlink path(", symPath, ") is unobtainable:", folder+"/"+f.name)
|
||||
}
|
||||
return errors.New("Cannot get symlink target")
|
||||
return errors.New("cannot get symlink target")
|
||||
}
|
||||
extractLoc := path.Clean(folder + "/" + path.Dir(symPath))
|
||||
err = fil.ExtractWithOptions(extractLoc, op)
|
||||
@@ -361,7 +363,7 @@ func (r *Reader) readDirFromInode(i *inode.Inode) ([]*directory.Entry, error) {
|
||||
metaOffset = i.Info.(inode.ExtDir).DirectoryOffset
|
||||
size = i.Info.(inode.ExtDir).DirectorySize
|
||||
default:
|
||||
return nil, errors.New("Not a directory inode")
|
||||
return nil, errors.New("not a directory inode")
|
||||
}
|
||||
br, err := r.newMetadataReader(int64(r.super.DirTableStart + uint64(offset)))
|
||||
if err != nil {
|
||||
@@ -22,7 +22,7 @@ type fileReader struct {
|
||||
|
||||
var (
|
||||
//ErrPathIsNotFile returns when trying to read from a file, but the given path is NOT a file.
|
||||
errPathIsNotFile = errors.New("The given path is not a file")
|
||||
errPathIsNotFile = errors.New("the given path is not a file")
|
||||
)
|
||||
|
||||
//ReadFile provides a squashfs.FileReader for the file at the given location.
|
||||
@@ -53,6 +53,9 @@ func (r *Reader) newFileReader(in *inode.Inode) (*fileReader, error) {
|
||||
}
|
||||
if !rdr.fragOnly {
|
||||
rdr.data, err = r.newDataReaderFromInode(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &rdr, nil
|
||||
}
|
||||
@@ -72,6 +75,9 @@ func (f *fileReader) Read(p []byte) (int, error) {
|
||||
if f.fragged && err == io.EOF {
|
||||
if f.fragmentData == nil {
|
||||
f.fragmentData, err = f.r.getFragmentDataFromInode(f.in)
|
||||
if err != nil {
|
||||
return read, err
|
||||
}
|
||||
}
|
||||
n, err = bytes.NewBuffer(f.fragmentData).Read(p[read:])
|
||||
read += n
|
||||
+10
-6
@@ -39,7 +39,7 @@ func (f FS) Open(name string) (fs.File, error) {
|
||||
Op: "open",
|
||||
Path: name,
|
||||
//TODO: make error clearer
|
||||
Err: errors.New("Trying to get file outside of squashfs"),
|
||||
Err: errors.New("trying to get file outside of squashfs"),
|
||||
}
|
||||
}
|
||||
return f.parent.Open(strings.Join(split[1:], "/"))
|
||||
@@ -107,7 +107,7 @@ func (f FS) Glob(pattern string) (out []string, err error) {
|
||||
Op: "readdir",
|
||||
Path: pattern,
|
||||
//TODO: make error clearer
|
||||
Err: errors.New("Trying to get file outside of squashfs"),
|
||||
Err: errors.New("trying to get file outside of squashfs"),
|
||||
}
|
||||
}
|
||||
return f.parent.Glob(strings.Join(split[1:], "/"))
|
||||
@@ -178,7 +178,7 @@ func (f FS) ReadDir(name string) ([]fs.DirEntry, error) {
|
||||
Op: "readdir",
|
||||
Path: name,
|
||||
//TODO: make error clearer
|
||||
Err: errors.New("Trying to get file outside of squashfs"),
|
||||
Err: errors.New("trying to get file outside of squashfs"),
|
||||
}
|
||||
}
|
||||
return f.parent.ReadDir(strings.Join(split[1:], "/"))
|
||||
@@ -294,7 +294,7 @@ func (f FS) Stat(name string) (fs.FileInfo, error) {
|
||||
Op: "stat",
|
||||
Path: name,
|
||||
//TODO: make error clearer
|
||||
Err: errors.New("Trying to get file outside of squashfs"),
|
||||
Err: errors.New("trying to get file outside of squashfs"),
|
||||
}
|
||||
}
|
||||
return f.parent.Stat(strings.Join(split[1:], "/"))
|
||||
@@ -377,7 +377,7 @@ func (f FS) Sub(dir string) (fs.FS, error) {
|
||||
Op: "sub",
|
||||
Path: dir,
|
||||
//TODO: make error clearer
|
||||
Err: errors.New("Trying to get file outside of squashfs"),
|
||||
Err: errors.New("trying to get file outside of squashfs"),
|
||||
}
|
||||
}
|
||||
return f.parent.Sub(strings.Join(split[1:], "/"))
|
||||
@@ -435,6 +435,11 @@ func (f FS) Sub(dir string) (fs.FS, error) {
|
||||
}
|
||||
|
||||
func (f FS) path() string {
|
||||
if f.name == "/" {
|
||||
return f.name
|
||||
} else if f.parent.name == "/" {
|
||||
return f.name
|
||||
}
|
||||
return f.parent.path() + "/" + f.name
|
||||
}
|
||||
|
||||
@@ -472,7 +477,6 @@ func (f FS) ExtractWithOptions(folder string, op ExtractionOptions) error {
|
||||
}
|
||||
errChan <- fil.ExtractWithOptions(folder, op)
|
||||
fil.Close()
|
||||
return
|
||||
}(&DirEntry{
|
||||
en: f.entries[i],
|
||||
parent: &f,
|
||||
+65
-16
@@ -14,9 +14,10 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
downloadURL = "https://github.com/srevinsaju/Firefox-Appimage/releases/download/firefox-v84.0.r20201221152838/firefox-84.0.r20201221152838-x86_64.AppImage"
|
||||
appImageURL = "https://github.com/srevinsaju/Firefox-Appimage/releases/download/firefox-v84.0.r20201221152838/firefox-84.0.r20201221152838-x86_64.AppImage"
|
||||
appImageName = "firefox-84.0.r20201221152838-x86_64.AppImage"
|
||||
squashfsName = "balenaEtcher-1.5.113-x64.AppImage.sfs"
|
||||
squashfsURL = "https://darkstorm.tech/LinuxPATest.sfs"
|
||||
squashfsName = "LinuxPATest.sfs"
|
||||
)
|
||||
|
||||
func TestSquashfs(t *testing.T) {
|
||||
@@ -25,6 +26,13 @@ func TestSquashfs(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
squashFil, err := os.Open(wd + "/testing/" + squashfsName)
|
||||
if os.IsNotExist(err) {
|
||||
err = downloadTestSquash(wd + "/testing")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
squashFil, err = os.Open(wd + "/testing/" + squashfsName)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -32,19 +40,33 @@ func TestSquashfs(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println("stuff", rdr.super.CompressionType)
|
||||
// fil := rdr.GetFileAtPath("*.desktop")
|
||||
// if fil == nil {
|
||||
// t.Fatal("Can't find desktop fil")
|
||||
// }
|
||||
// errs := fil.ExtractTo(wd + "/testing")
|
||||
// if len(errs) > 0 {
|
||||
// t.Fatal(errs)
|
||||
// }
|
||||
// errs = rdr.ExtractTo(wd + "/testing/" + squashfsName + ".d")
|
||||
// if len(errs) > 0 {
|
||||
// t.Fatal(errs)
|
||||
// }
|
||||
os.RemoveAll(wd + "/testing/" + squashfsName + ".d")
|
||||
op := DefaultOptions()
|
||||
op.Verbose = true
|
||||
err = rdr.ExtractWithOptions(wd+"/testing/"+squashfsName+".d", op)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Fatal("No Problems")
|
||||
}
|
||||
|
||||
func TestSquashfsFromReader(t *testing.T) {
|
||||
resp, err := http.DefaultClient.Get(squashfsURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
rdr, err := NewSquashfsReaderFromReader(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
os.RemoveAll("testing/" + squashfsName + ".d")
|
||||
op := DefaultOptions()
|
||||
op.Verbose = true
|
||||
err = rdr.ExtractWithOptions("testing/"+squashfsName+".d", op)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Fatal("No Problems")
|
||||
}
|
||||
|
||||
@@ -154,6 +176,7 @@ func BenchmarkDragRace(b *testing.B) {
|
||||
b.Log("Unsqushfs:", unsquashTime.Round(time.Millisecond))
|
||||
b.Log("Library:", libTime.Round(time.Millisecond))
|
||||
b.Log("unsquashfs is", strconv.FormatFloat(float64(libTime.Milliseconds())/float64(unsquashTime.Milliseconds()), 'f', 2, 64)+"x faster")
|
||||
b.Error("STOP ALREADY!")
|
||||
}
|
||||
|
||||
func downloadTestAppImage(dir string) error {
|
||||
@@ -170,7 +193,7 @@ func downloadTestAppImage(dir string) error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
resp, err := check.Get(downloadURL)
|
||||
resp, err := check.Get(appImageURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -182,6 +205,32 @@ func downloadTestAppImage(dir string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func downloadTestSquash(dir string) error {
|
||||
//seems to time out on slow connections. Might fix that at some point... or not. It's just a test...
|
||||
os.Mkdir(dir, os.ModePerm)
|
||||
sfs, err := os.Create(dir + "/" + squashfsName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer sfs.Close()
|
||||
check := http.Client{
|
||||
CheckRedirect: func(r *http.Request, _ []*http.Request) error {
|
||||
r.URL.Opaque = r.URL.Path
|
||||
return nil
|
||||
},
|
||||
}
|
||||
resp, err := check.Get(squashfsURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
_, err = io.Copy(sfs, resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestCreateSquashFromAppImage(t *testing.T) {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
|
||||
@@ -1,390 +0,0 @@
|
||||
package squashfs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/CalebQ42/squashfs/internal/compression"
|
||||
)
|
||||
|
||||
//Writer is used to creaste squashfs archives. Currently unusable.
|
||||
//TODO: Make usable
|
||||
type Writer struct {
|
||||
compressor compression.Compressor
|
||||
structure map[string][]*fileHolder
|
||||
symlinkTable map[string]string
|
||||
folders []string
|
||||
uidGUIDTable []int
|
||||
frags []fragment
|
||||
superblock superblock
|
||||
compressionType int
|
||||
//BlockSize is how large the data blocks are. Can be between 4096 (4KB) and 1048576 (1 MB).
|
||||
//If BlockSize is not inside that range, it will be set to within the range before writing.
|
||||
//Default is 1048576.
|
||||
BlockSize uint32
|
||||
//Flags are the SuperblockFlags used when writing the archive.
|
||||
//Currently Duplicates, Exportable, UncompressedXattr, NoXattr values are ignored
|
||||
Flags SuperblockFlags
|
||||
allowErrors bool
|
||||
}
|
||||
|
||||
//NewWriter creates a new with the default options (Gzip compression and allow errors)
|
||||
func NewWriter() (*Writer, error) {
|
||||
return NewWriterWithOptions(GzipCompression, true)
|
||||
}
|
||||
|
||||
//NewWriterWithOptions creates a new squashfs.Writer with the given options.
|
||||
//compressionType can be of any types, except LZO (which this library doesn't have support for yet)
|
||||
//allowErrors determines if, when adding folders, it allows errors encountered with it's sub-directories and instead logs the errors.
|
||||
func NewWriterWithOptions(compressionType int, allowErrors bool) (*Writer, error) {
|
||||
if compressionType < 0 || compressionType > 6 {
|
||||
return nil, errors.New("Incorrect compression type")
|
||||
}
|
||||
if compressionType == 3 {
|
||||
return nil, errors.New("Lzo compression is not (currently) supported")
|
||||
}
|
||||
writer := &Writer{
|
||||
structure: map[string][]*fileHolder{
|
||||
"/": make([]*fileHolder, 0),
|
||||
},
|
||||
folders: []string{
|
||||
"/",
|
||||
},
|
||||
symlinkTable: make(map[string]string),
|
||||
compressionType: compressionType,
|
||||
allowErrors: allowErrors,
|
||||
BlockSize: uint32(1048576),
|
||||
Flags: DefaultFlags,
|
||||
}
|
||||
switch compressionType {
|
||||
case 1:
|
||||
writer.compressor = &compression.Gzip{}
|
||||
case 2:
|
||||
writer.compressor = &compression.Lzma{}
|
||||
case 4:
|
||||
writer.compressor = &compression.Xz{}
|
||||
case 5:
|
||||
writer.compressor = &compression.Lz4{}
|
||||
case 6:
|
||||
writer.compressor = &compression.Zstd{}
|
||||
}
|
||||
return writer, nil
|
||||
}
|
||||
|
||||
//fileHolder holds the necessary information about a given file inside of a squashfs
|
||||
type fileHolder struct {
|
||||
reader io.Reader
|
||||
path string
|
||||
name string
|
||||
symLocation string
|
||||
blockSizes []uint32
|
||||
GUID int
|
||||
perm int
|
||||
size uint64
|
||||
UID int
|
||||
folder bool
|
||||
symlink bool
|
||||
|
||||
fragIndex int
|
||||
fragOffset int
|
||||
}
|
||||
|
||||
//AddFile attempts to add an os.File to the archive's root directory.
|
||||
func (w *Writer) AddFile(file *os.File) error {
|
||||
return w.AddFileToFolder("/", file)
|
||||
}
|
||||
|
||||
//AddFileToFolder adds the given file to the squashfs archive, placing it inside the given folder.
|
||||
func (w *Writer) AddFileToFolder(folder string, file *os.File) error {
|
||||
name := path.Base(file.Name())
|
||||
if !strings.HasSuffix(folder, "/") {
|
||||
folder += "/"
|
||||
}
|
||||
return w.AddFileTo(folder+name, file)
|
||||
}
|
||||
|
||||
//AddFileTo adds the given file to the squashfs archive at the given filepath.
|
||||
func (w *Writer) AddFileTo(filepath string, file *os.File) error {
|
||||
filepath = path.Clean(filepath)
|
||||
if !strings.HasPrefix(filepath, "/") {
|
||||
filepath = "/" + filepath
|
||||
}
|
||||
if w.Contains(filepath) {
|
||||
return errors.New("File already exists at " + filepath)
|
||||
}
|
||||
var holder fileHolder
|
||||
holder.path = path.Dir(filepath)
|
||||
holder.name = path.Base(filepath)
|
||||
holder.reader = file
|
||||
stat, err := file.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
holder.folder = stat.IsDir()
|
||||
holder.symlink = (stat.Mode()&os.ModeSymlink == os.ModeSymlink)
|
||||
holder.perm = int(stat.Mode().Perm())
|
||||
//Thanks to https://stackoverflow.com/questions/58179647/getting-uid-and-gid-of-a-file for uid and guid getting
|
||||
if stat, ok := stat.Sys().(*syscall.Stat_t); ok {
|
||||
holder.UID = int(stat.Uid)
|
||||
holder.GUID = int(stat.Gid)
|
||||
}
|
||||
if sort.SearchInts(w.uidGUIDTable, holder.UID) == len(w.uidGUIDTable) {
|
||||
w.uidGUIDTable = append(w.uidGUIDTable, holder.UID)
|
||||
sort.Ints(w.uidGUIDTable)
|
||||
}
|
||||
if sort.SearchInts(w.uidGUIDTable, holder.GUID) == len(w.uidGUIDTable) {
|
||||
w.uidGUIDTable = append(w.uidGUIDTable, holder.GUID)
|
||||
sort.Ints(w.uidGUIDTable)
|
||||
}
|
||||
if holder.symlink {
|
||||
holder.reader = file
|
||||
target, err := os.Readlink(file.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
holder.symLocation = target
|
||||
} else if holder.folder {
|
||||
subDirNames, err := file.Readdirnames(-1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dirsAdded := make([]string, 0)
|
||||
for _, subDir := range subDirNames {
|
||||
fil, err := os.Open(file.Name() + subDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = w.AddFileToFolder(holder.path+"/"+holder.name, fil)
|
||||
if err != nil && !w.allowErrors {
|
||||
for _, dir := range dirsAdded {
|
||||
w.Remove(dir)
|
||||
}
|
||||
return err
|
||||
} else if err != nil {
|
||||
log.Println("Error while adding", fil.Name())
|
||||
log.Println(err)
|
||||
}
|
||||
if !w.allowErrors {
|
||||
dirsAdded = append(dirsAdded, holder.path+"/"+holder.name)
|
||||
}
|
||||
}
|
||||
} else if stat.Mode().IsRegular() {
|
||||
holder.reader = file
|
||||
} else {
|
||||
return errors.New("Unsupported file type " + file.Name())
|
||||
}
|
||||
if _, ok := w.structure[holder.path]; ok {
|
||||
w.folders = append(w.folders, holder.path)
|
||||
sort.Strings(w.folders)
|
||||
}
|
||||
w.structure[holder.path] = append(w.structure[holder.path], &holder)
|
||||
return nil
|
||||
}
|
||||
|
||||
//AddReaderTo adds the data from the given reader to the archive as a file located at the given filepath.
|
||||
//Data from the reader is not read until the squashfs archive is writen.
|
||||
//If the given reader implements io.Closer, it will be closed after it is fully read.
|
||||
func (w *Writer) AddReaderTo(filepath string, reader io.Reader, size uint64) error {
|
||||
filepath = path.Clean(filepath)
|
||||
if !strings.HasPrefix(filepath, "/") {
|
||||
filepath = "/" + filepath
|
||||
}
|
||||
if w.Contains(filepath) {
|
||||
return errors.New("File already exists at " + filepath)
|
||||
}
|
||||
var holder fileHolder
|
||||
holder.path = path.Dir(filepath)
|
||||
holder.name = path.Base(filepath)
|
||||
holder.size = size
|
||||
holder.reader = reader
|
||||
if _, ok := w.structure[holder.path]; ok {
|
||||
w.folders = append(w.folders, holder.path)
|
||||
sort.Strings(w.folders)
|
||||
}
|
||||
w.structure[holder.path] = append(w.structure[holder.path], &holder)
|
||||
return nil
|
||||
}
|
||||
|
||||
//AddFolderTo adds a folder at the given path. IF the folder is already present, it sets the folder's permissions.
|
||||
//If the path points to a non-folder (such as a file or symlink), an error is returned
|
||||
func (w *Writer) AddFolderTo(folderpath string, permission fs.FileMode) error {
|
||||
folderpath = path.Clean(folderpath)
|
||||
tmp := w.holderAt(folderpath)
|
||||
if tmp != nil {
|
||||
if !tmp.folder {
|
||||
return errors.New("Path is not a folder: " + folderpath)
|
||||
}
|
||||
tmp.perm = int(permission.Perm())
|
||||
return nil
|
||||
}
|
||||
file := fileHolder{
|
||||
path: path.Dir(folderpath),
|
||||
name: path.Base(folderpath),
|
||||
perm: int(permission | fs.ModePerm),
|
||||
folder: true,
|
||||
}
|
||||
w.structure[file.path] = append(w.structure[file.path], &file)
|
||||
return nil
|
||||
}
|
||||
|
||||
//Remove tries to remove the file(s) at the given filepath. If wildcards are used, it will remove all files that match.
|
||||
//Returns true if one or more files are removed.
|
||||
func (w *Writer) Remove(filepath string) bool {
|
||||
var matchFound bool
|
||||
filepath = path.Clean(filepath)
|
||||
if !strings.HasPrefix(filepath, "/") {
|
||||
filepath = "/" + filepath
|
||||
}
|
||||
dir, name := path.Split(filepath)
|
||||
for structDir, files := range w.structure {
|
||||
if match, _ := path.Match(dir, structDir); match {
|
||||
for i, fil := range files {
|
||||
if match, _ = path.Match(name, fil.name); match {
|
||||
matchFound = true
|
||||
if len(w.structure[structDir]) > 1 {
|
||||
w.structure[structDir][i] = w.structure[structDir][len(w.structure[structDir])-1]
|
||||
w.structure[structDir] = w.structure[structDir][:len(w.structure[structDir])-1]
|
||||
} else {
|
||||
w.structure[structDir] = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return matchFound
|
||||
}
|
||||
|
||||
//FixSymlinks will scan through the squashfs archive and try to find broken symlinks and fix them.
|
||||
//This done by replacing the symlink with the target file and then pointing other symlinks to that file.
|
||||
//If all symlinks can be resolved, the error slice will be nil, and the bool false, otherwise all errors occured will be in the slice.
|
||||
func (w *Writer) FixSymlinks() (errs []error, problems bool) {
|
||||
for dir, holderSlice := range w.structure {
|
||||
for i, holder := range holderSlice {
|
||||
if !holder.symlink {
|
||||
continue
|
||||
}
|
||||
sym := holder.symLocation
|
||||
if !path.IsAbs(holder.symLocation) {
|
||||
sym = path.Join(dir, holder.symLocation)
|
||||
}
|
||||
if path, ok := w.symlinkTable[sym]; ok {
|
||||
w.structure[dir][i].symLocation = path
|
||||
continue
|
||||
}
|
||||
if path.IsAbs(sym) || strings.HasPrefix(sym, "../") {
|
||||
var symFil *os.File
|
||||
var err error
|
||||
if strings.HasPrefix(sym, "../") {
|
||||
holderFil, ok := holder.reader.(*os.File)
|
||||
if !ok {
|
||||
problems = true
|
||||
errs = append(errs, errors.New("Cannot resolve symlink at "+dir+holder.name))
|
||||
continue
|
||||
}
|
||||
symFilPath := path.Dir(holderFil.Name())
|
||||
symFilPath = path.Join(symFilPath, holder.symLocation)
|
||||
symFil, err = os.Open(symFilPath)
|
||||
} else {
|
||||
symFil, err = os.Open(sym)
|
||||
}
|
||||
if err != nil {
|
||||
problems = true
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
suc := w.Remove(dir + holder.name)
|
||||
if !suc {
|
||||
problems = true
|
||||
errs = append(errs, errors.New("Cannot resolve symlink at "+dir+holder.name))
|
||||
continue
|
||||
}
|
||||
err = w.AddFileTo(dir+holder.name, symFil)
|
||||
if err != nil {
|
||||
w.structure[dir] = append(w.structure[dir], holder)
|
||||
problems = true
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
w.symlinkTable[sym] = dir + holder.name
|
||||
} else {
|
||||
symHolder := w.holderAt(sym)
|
||||
if symHolder != nil {
|
||||
w.symlinkTable[sym] = sym
|
||||
continue
|
||||
}
|
||||
holderFil, ok := holder.reader.(*os.File)
|
||||
if !ok {
|
||||
problems = true
|
||||
errs = append(errs, errors.New("Cannot resolve symlink at "+dir+holder.name))
|
||||
continue
|
||||
}
|
||||
symFilPath := path.Dir(holderFil.Name())
|
||||
symFilPath = path.Join(symFilPath, holder.symLocation)
|
||||
symFil, err := os.Open(symFilPath)
|
||||
if err != nil {
|
||||
problems = true
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
err = w.AddFileTo(sym, symFil)
|
||||
if err != nil {
|
||||
problems = true
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
w.symlinkTable[sym] = sym
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (w *Writer) holderAt(filepath string) *fileHolder {
|
||||
filepath = path.Clean(filepath)
|
||||
if !strings.HasPrefix(filepath, "/") {
|
||||
filepath = "/" + filepath
|
||||
}
|
||||
dir, name := path.Split(filepath)
|
||||
if holderSlice, ok := w.structure[dir]; ok {
|
||||
for _, holder := range holderSlice {
|
||||
if holder.name == name {
|
||||
return holder
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//Contains returns whether a file is present at the given filepath
|
||||
func (w *Writer) Contains(filepath string) bool {
|
||||
filepath = path.Clean(filepath)
|
||||
if !strings.HasPrefix(filepath, "/") {
|
||||
filepath = "/" + filepath
|
||||
}
|
||||
dir, name := path.Split(filepath)
|
||||
if holderSlice, ok := w.structure[dir]; ok {
|
||||
for _, holder := range holderSlice {
|
||||
if holder.name == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//WriteToFilename creates the squashfs archive with the given filepath.
|
||||
func (w *Writer) WriteToFilename(filepath string) error {
|
||||
newFil, err := os.Create(filepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.WriteTo(newFil)
|
||||
return err
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package squashfs
|
||||
|
||||
import "reflect"
|
||||
|
||||
func (w *Writer) compressData(data []byte) ([]byte, error) {
|
||||
if reflect.DeepEqual(data, make([]byte, len(data))) {
|
||||
return nil, nil
|
||||
}
|
||||
compressedData, err := w.compressor.Compress(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(data) <= len(compressedData) {
|
||||
return data, nil
|
||||
}
|
||||
return compressedData, nil
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package squashfs
|
||||
|
||||
//TODO: Allow settings the options
|
||||
|
||||
// func (w *Writer) SetGzipOptions() error {}
|
||||
|
||||
// func (w *Writer) SetLzmaOptions() error {}
|
||||
|
||||
// func (w *Writer) SetLzoOptions() error {}
|
||||
|
||||
// func (w *Writer) SetXzOptions() error {}
|
||||
|
||||
// func (w *Writer) SetLz4Options() error {}
|
||||
|
||||
// func (w *Writer) SetZstdOptions() error {}
|
||||
@@ -1,69 +0,0 @@
|
||||
package squashfs
|
||||
|
||||
type fragment struct {
|
||||
w *Writer
|
||||
files []*fileHolder
|
||||
sizes []uint32
|
||||
}
|
||||
|
||||
func (f *fragment) SizeLeft() uint32 {
|
||||
totalSize := uint32(0)
|
||||
for _, siz := range f.sizes {
|
||||
totalSize += siz
|
||||
}
|
||||
return f.w.BlockSize - uint32(totalSize)
|
||||
}
|
||||
|
||||
func (f *fragment) AddFragment(fil *fileHolder) {
|
||||
//SizeLeft should already be checked
|
||||
fil.fragOffset = len(f.files)
|
||||
f.files = append(f.files, fil)
|
||||
f.sizes = append(f.sizes, fil.blockSizes[len(fil.blockSizes)-1])
|
||||
}
|
||||
|
||||
func (w *Writer) addToFragments(fil *fileHolder) {
|
||||
fragSize := fil.blockSizes[len(fil.blockSizes)-1]
|
||||
//only fragment if the final block is less then 80% of a full block or AlwaysFragment
|
||||
if w.Flags.AlwaysFragment || fragSize < uint32(float32(w.BlockSize)*0.8) {
|
||||
var possibleFrags []int
|
||||
for i := range w.frags {
|
||||
left := w.frags[i].SizeLeft()
|
||||
if left == fragSize {
|
||||
fil.fragIndex = i
|
||||
w.frags[i].AddFragment(fil)
|
||||
return
|
||||
} else if left > fragSize {
|
||||
possibleFrags = append(possibleFrags, i)
|
||||
}
|
||||
}
|
||||
if len(possibleFrags) > 0 {
|
||||
fil.fragIndex = possibleFrags[0]
|
||||
} else {
|
||||
fil.fragIndex = len(w.frags)
|
||||
w.frags = append(w.frags, fragment{
|
||||
w: w,
|
||||
files: []*fileHolder{fil},
|
||||
sizes: []uint32{fragSize},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Writer) calculateFragsAndBlockSizes() {
|
||||
for _, files := range w.structure {
|
||||
for i := range files {
|
||||
files[i].fragIndex = -1
|
||||
files[i].blockSizes = make([]uint32, files[i].size/uint64(w.BlockSize))
|
||||
for j := range files[i].blockSizes {
|
||||
files[i].blockSizes[j] = w.BlockSize
|
||||
}
|
||||
fragSize := uint32(files[i].size % uint64(w.BlockSize))
|
||||
if fragSize > 0 {
|
||||
files[i].blockSizes = append(files[i].blockSizes, fragSize)
|
||||
if !w.Flags.NoFragments {
|
||||
w.addToFragments(files[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package squashfs
|
||||
|
||||
func (w *Writer) countInodes() (out uint32) {
|
||||
for _, files := range w.structure {
|
||||
out++
|
||||
out += uint32(len(files))
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package squashfs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"io/fs"
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (w *Writer) fixFolders() error {
|
||||
for folder := range w.structure {
|
||||
if folder == "/" || w.Contains(folder) {
|
||||
continue
|
||||
}
|
||||
err := w.AddFolderTo(folder, fs.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//WriteTo attempts to write the archive to the given io.Writer.
|
||||
//Folder that aren't present (such as if you add a file at /folder/file, but not the folder /folder)
|
||||
//are added with full permission (777).
|
||||
//
|
||||
//Not working. Yet.
|
||||
func (w *Writer) WriteTo(write io.Writer) (int64, error) {
|
||||
err := w.fixFolders()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if w.BlockSize > 1048576 {
|
||||
w.BlockSize = 1048576
|
||||
} else if w.BlockSize < 4096 {
|
||||
w.BlockSize = 4096
|
||||
}
|
||||
w.Flags.RemoveDuplicates = false
|
||||
w.Flags.Exportable = false
|
||||
w.Flags.NoXattr = true
|
||||
w.calculateFragsAndBlockSizes()
|
||||
w.superblock = superblock{
|
||||
Magic: magic,
|
||||
InodeCount: w.countInodes(),
|
||||
CreationTime: uint32(time.Now().Unix()),
|
||||
BlockSize: w.BlockSize,
|
||||
CompressionType: uint16(w.compressionType),
|
||||
BlockLog: uint16(math.Log2(float64(w.BlockSize))),
|
||||
Flags: w.Flags.ToUint(),
|
||||
IDCount: uint16(len(w.uidGUIDTable)),
|
||||
MajorVersion: 4,
|
||||
MinorVersion: 0,
|
||||
}
|
||||
return 0, errors.New("I SAID DON'T")
|
||||
}
|
||||
Reference in New Issue
Block a user