cheat/vendor/github.com/go-git/go-billy/v5/helper/chroot/chroot.go
Christopher Allen Lane 80c91cbdee feat(installer): use go-git to clone
Integrate `go-git` into the application, and use it to `git clone`
cheatsheets when the installer runs.

Previously, the installer required that `git` be installed on the system
`PATH`, so this change has to big advantages:

1. It removes that system dependency on `git`
2. It paves the way for implementing the `--update` command

Additionally, `cheat` now performs a `--depth=1` clone when installing
cheatsheets, which should at least somewhat improve installation times
(especially on slow network connections).
2022-08-27 21:00:46 -04:00

243 lines
5.1 KiB
Go

package chroot
import (
"os"
"path/filepath"
"strings"
"github.com/go-git/go-billy/v5"
"github.com/go-git/go-billy/v5/helper/polyfill"
)
// ChrootHelper is a helper to implement billy.Chroot.
type ChrootHelper struct {
underlying billy.Filesystem
base string
}
// New creates a new filesystem wrapping up the given 'fs'.
// The created filesystem has its base in the given ChrootHelperectory of the
// underlying filesystem.
func New(fs billy.Basic, base string) billy.Filesystem {
return &ChrootHelper{
underlying: polyfill.New(fs),
base: base,
}
}
func (fs *ChrootHelper) underlyingPath(filename string) (string, error) {
if isCrossBoundaries(filename) {
return "", billy.ErrCrossedBoundary
}
return fs.Join(fs.Root(), filename), nil
}
func isCrossBoundaries(path string) bool {
path = filepath.ToSlash(path)
path = filepath.Clean(path)
return strings.HasPrefix(path, ".."+string(filepath.Separator))
}
func (fs *ChrootHelper) Create(filename string) (billy.File, error) {
fullpath, err := fs.underlyingPath(filename)
if err != nil {
return nil, err
}
f, err := fs.underlying.Create(fullpath)
if err != nil {
return nil, err
}
return newFile(fs, f, filename), nil
}
func (fs *ChrootHelper) Open(filename string) (billy.File, error) {
fullpath, err := fs.underlyingPath(filename)
if err != nil {
return nil, err
}
f, err := fs.underlying.Open(fullpath)
if err != nil {
return nil, err
}
return newFile(fs, f, filename), nil
}
func (fs *ChrootHelper) OpenFile(filename string, flag int, mode os.FileMode) (billy.File, error) {
fullpath, err := fs.underlyingPath(filename)
if err != nil {
return nil, err
}
f, err := fs.underlying.OpenFile(fullpath, flag, mode)
if err != nil {
return nil, err
}
return newFile(fs, f, filename), nil
}
func (fs *ChrootHelper) Stat(filename string) (os.FileInfo, error) {
fullpath, err := fs.underlyingPath(filename)
if err != nil {
return nil, err
}
return fs.underlying.Stat(fullpath)
}
func (fs *ChrootHelper) Rename(from, to string) error {
var err error
from, err = fs.underlyingPath(from)
if err != nil {
return err
}
to, err = fs.underlyingPath(to)
if err != nil {
return err
}
return fs.underlying.Rename(from, to)
}
func (fs *ChrootHelper) Remove(path string) error {
fullpath, err := fs.underlyingPath(path)
if err != nil {
return err
}
return fs.underlying.Remove(fullpath)
}
func (fs *ChrootHelper) Join(elem ...string) string {
return fs.underlying.Join(elem...)
}
func (fs *ChrootHelper) TempFile(dir, prefix string) (billy.File, error) {
fullpath, err := fs.underlyingPath(dir)
if err != nil {
return nil, err
}
f, err := fs.underlying.(billy.TempFile).TempFile(fullpath, prefix)
if err != nil {
return nil, err
}
return newFile(fs, f, fs.Join(dir, filepath.Base(f.Name()))), nil
}
func (fs *ChrootHelper) ReadDir(path string) ([]os.FileInfo, error) {
fullpath, err := fs.underlyingPath(path)
if err != nil {
return nil, err
}
return fs.underlying.(billy.Dir).ReadDir(fullpath)
}
func (fs *ChrootHelper) MkdirAll(filename string, perm os.FileMode) error {
fullpath, err := fs.underlyingPath(filename)
if err != nil {
return err
}
return fs.underlying.(billy.Dir).MkdirAll(fullpath, perm)
}
func (fs *ChrootHelper) Lstat(filename string) (os.FileInfo, error) {
fullpath, err := fs.underlyingPath(filename)
if err != nil {
return nil, err
}
return fs.underlying.(billy.Symlink).Lstat(fullpath)
}
func (fs *ChrootHelper) Symlink(target, link string) error {
target = filepath.FromSlash(target)
// only rewrite target if it's already absolute
if filepath.IsAbs(target) || strings.HasPrefix(target, string(filepath.Separator)) {
target = fs.Join(fs.Root(), target)
target = filepath.Clean(filepath.FromSlash(target))
}
link, err := fs.underlyingPath(link)
if err != nil {
return err
}
return fs.underlying.(billy.Symlink).Symlink(target, link)
}
func (fs *ChrootHelper) Readlink(link string) (string, error) {
fullpath, err := fs.underlyingPath(link)
if err != nil {
return "", err
}
target, err := fs.underlying.(billy.Symlink).Readlink(fullpath)
if err != nil {
return "", err
}
if !filepath.IsAbs(target) && !strings.HasPrefix(target, string(filepath.Separator)) {
return target, nil
}
target, err = filepath.Rel(fs.base, target)
if err != nil {
return "", err
}
return string(os.PathSeparator) + target, nil
}
func (fs *ChrootHelper) Chroot(path string) (billy.Filesystem, error) {
fullpath, err := fs.underlyingPath(path)
if err != nil {
return nil, err
}
return New(fs.underlying, fullpath), nil
}
func (fs *ChrootHelper) Root() string {
return fs.base
}
func (fs *ChrootHelper) Underlying() billy.Basic {
return fs.underlying
}
// Capabilities implements the Capable interface.
func (fs *ChrootHelper) Capabilities() billy.Capability {
return billy.Capabilities(fs.underlying)
}
type file struct {
billy.File
name string
}
func newFile(fs billy.Filesystem, f billy.File, filename string) billy.File {
filename = fs.Join(fs.Root(), filename)
filename, _ = filepath.Rel(fs.Root(), filename)
return &file{
File: f,
name: filename,
}
}
func (f *file) Name() string {
return f.name
}