Skip to content

Instantly share code, notes, and snippets.

@pjbgf
Last active July 8, 2023 21:06
Show Gist options
  • Save pjbgf/c0ee84bdf54fa18a736c2e185f1465f0 to your computer and use it in GitHub Desktop.
Save pjbgf/c0ee84bdf54fa18a736c2e185f1465f0 to your computer and use it in GitHub Desktop.
Filtered billy.Filesystem PoC
package filtered
import (
"io"
"os"
"time"
"github.com/go-git/go-billy/v5"
)
type discardFile struct {
name string
content *discardContent
position int64
flag int
mode os.FileMode
}
func newDiscardFile(name string) *discardFile {
return &discardFile{
name: name,
}
}
func (f *discardFile) Name() string {
return f.name
}
func (f *discardFile) Read(b []byte) (int, error) {
return 0, io.EOF
}
func (f *discardFile) ReadAt(b []byte, off int64) (int, error) {
return 0, io.EOF
}
func (f *discardFile) Seek(offset int64, whence int) (int64, error) {
switch whence {
case io.SeekCurrent:
f.position += offset
case io.SeekStart:
f.position = offset
}
return f.position, nil
}
func (f *discardFile) Write(p []byte) (int, error) {
return len(p), nil
}
func (f *discardFile) Close() error {
return nil
}
func (f *discardFile) Truncate(size int64) error {
return nil
}
func (f *discardFile) Duplicate(filename string, mode os.FileMode, flag int) billy.File {
return f
}
func (f *discardFile) Stat() (os.FileInfo, error) {
return &discardFileInfo{
name: f.Name(),
mode: f.mode,
}, nil
}
// Lock is a no-op in memfs.
func (f *discardFile) Lock() error {
return nil
}
// Unlock is a no-op in memfs.
func (f *discardFile) Unlock() error {
return nil
}
type discardFileInfo struct {
name string
size int
mode os.FileMode
}
func (fi *discardFileInfo) Name() string {
return fi.name
}
func (fi *discardFileInfo) Size() int64 {
return int64(fi.size)
}
func (fi *discardFileInfo) Mode() os.FileMode {
return fi.mode
}
func (*discardFileInfo) ModTime() time.Time {
return time.Now()
}
func (fi *discardFileInfo) IsDir() bool {
return fi.mode.IsDir()
}
func (*discardFileInfo) Sys() interface{} {
return nil
}
type discardContent struct {
}
func (c *discardContent) WriteAt(p []byte, off int64) (int, error) {
return len(p), nil
}
func (c *discardContent) ReadAt(b []byte, off int64) (n int, err error) {
return len(b) - int(off), nil
}
package main
import (
"fmt"
"os"
"runtime"
"strconv"
"strings"
"time"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing/cache"
"github.com/go-git/go-git/v5/storage/filesystem"
"github.com/go-git/go-billy/v5"
"github.com/go-git/go-billy/v5/osfs"
)
func main() {
CheckArgs("<url>", "<directory>", "<filtered>")
url := os.Args[1]
directory := os.Args[2]
filter, _ := strconv.ParseBool(os.Args[3])
// Clone the given repository to the given directory
Info("git clone %s %s", url, directory)
fs := osfs.New(directory)
dotgit, err := fs.Chroot(git.GitDirName)
CheckIfError(err)
storer := filesystem.NewStorage(dotgit, cache.NewObjectLRUDefault())
var wt billy.Filesystem
if filter {
fmt.Println("filter ON")
wt, _ = NewFilteredStorage(fs, `^(Makefile|.git.*)`)
} else {
fmt.Println("filter OFF")
wt = fs
}
CheckIfError(err)
fmt.Println("cloning...")
t := time.Now()
_, err = git.Clone(storer, wt, &git.CloneOptions{
URL: url,
Depth: 1,
Tags: git.NoTags,
SingleBranch: true,
})
CheckIfError(err)
d := time.Now().Sub(t)
fmt.Printf("total time: %.fm %.fs %dn\n", d.Minutes(), d.Seconds(), d.Nanoseconds())
}
package filtered
import (
"os"
"path/filepath"
"regexp"
"github.com/go-git/go-billy/v5"
)
type FilteredStorage struct {
pattern *regexp.Regexp
fs billy.Filesystem
fake billy.File
fakeInfo os.FileInfo
}
func NewFilteredStorage(fs billy.Filesystem, pattern string) (billy.Filesystem, error) {
re, err := regexp.Compile(pattern)
if err != nil {
return nil, err
}
return &FilteredStorage{
pattern: re,
fs: fs,
fake: &discardFile{},
fakeInfo: &discardFileInfo{},
}, nil
}
func (s *FilteredStorage) Create(filename string) (billy.File, error) {
if s.pattern.MatchString(filename) {
return s.fs.Create(filename)
}
return newDiscardFile(filename), nil
}
func (s *FilteredStorage) Open(filename string) (billy.File, error) {
return s.OpenFile(filename, os.O_RDONLY, 0o000)
}
func (s *FilteredStorage) OpenFile(filename string, flag int, perm os.FileMode) (billy.File, error) {
if s.pattern.MatchString(filename) {
return s.fs.OpenFile(filename, flag, perm)
}
return s.fake, nil
}
func (s *FilteredStorage) Stat(filename string) (os.FileInfo, error) {
if s.pattern.MatchString(filename) {
return s.fs.Stat(filename)
}
return s.fakeInfo, nil
}
func (s *FilteredStorage) Lstat(filename string) (os.FileInfo, error) {
if s.pattern.MatchString(filename) {
return s.fs.Lstat(filename)
}
return s.fakeInfo, nil
}
func (s *FilteredStorage) ReadDir(path string) ([]os.FileInfo, error) {
if path == "" || s.pattern.MatchString(path) {
return s.fs.ReadDir(path)
}
return nil, nil
}
func (s *FilteredStorage) MkdirAll(path string, perm os.FileMode) error {
if s.pattern.MatchString(path) {
return s.fs.MkdirAll(path, perm)
}
return nil
}
func (s *FilteredStorage) Rename(from, to string) error {
if s.pattern.MatchString(from) {
return s.fs.Rename(from, to)
}
return nil
}
func (s *FilteredStorage) Remove(filename string) error {
if s.pattern.MatchString(filename) {
return s.fs.Remove(filename)
}
return nil
}
func (s *FilteredStorage) Symlink(target, link string) error {
if s.pattern.MatchString(link) {
return s.fs.Symlink(target, link)
}
return nil
}
func (s *FilteredStorage) Readlink(link string) (string, error) {
if s.pattern.MatchString(link) {
return s.fs.Readlink(link)
}
return "", nil
}
// Funcs not filtered:
func (s *FilteredStorage) TempFile(dir, prefix string) (billy.File, error) {
return s.fs.TempFile(dir, prefix)
}
func (s *FilteredStorage) Chroot(path string) (billy.Filesystem, error) {
fs, err := s.fs.Chroot(path)
if err != nil {
return nil, err
}
return New(fs, s.pattern.String())
}
func (s *FilteredStorage) Root() string {
return s.fs.Root()
}
func (s *FilteredStorage) Join(elem ...string) string {
return filepath.Join(elem...)
}
// Capabilities implements the Capable interface.
func (s *FilteredStorage) Capabilities() billy.Capability {
return billy.WriteCapability |
billy.ReadCapability |
billy.ReadAndWriteCapability |
billy.SeekCapability |
billy.TruncateCapability
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment