func (fs *MemFS) UpdateFromTarPath(source string, untar bool) error {
	reader, err := os.Open(source)
	if err != nil {
		return fmt.Errorf("open tar file: %s", err)
	}
	defer reader.Close()
	gzipReader, err := tario.NewGzipReader(reader)
	if err != nil {
		return fmt.Errorf("new gzip reader: %s", err)
	}
	return fs.UpdateFromTarReader(tar.NewReader(gzipReader), untar)
}
UpdateFromTarPath updates MemFS with the contents of the tarball at the given
path. untars the tarball onto the root of MemFS 'untar' specifies if the
contents should also be written to disk at the MemFS root or not.
package snapshot
import (
	"archive/tar"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"syscall"
	"time"
	"github.com/andres-erbsen/clock"
	"github.com/uber/makisu/lib/docker/image"
	"github.com/uber/makisu/lib/fileio"
	"github.com/uber/makisu/lib/log"
	"github.com/uber/makisu/lib/mountutils"
	"github.com/uber/makisu/lib/pathutils"
	"github.com/uber/makisu/lib/tario"
	"github.com/uber/makisu/lib/utils"
)
type memFSNode struct {
	*contentMemFile                        
	children        map[string]*memFSNode  
}
func newMemFSNode(mf *contentMemFile) *memFSNode {
	return &memFSNode{mf, make(map[string]*memFSNode)}
}
func (n *memFSNode) isOnDisk() (bool, error) {
	if _, err := os.Lstat(n.src); err == nil {
		return true, nil
	} else if os.IsNotExist(err) {
		return false, nil
	} else {
		return false, err
	}
}
type MemFS struct {
	clk  clock.Clock
	tree *memFSNode
	blacklist []string
	layers    []*memLayer
}
func NewMemFS(clk clock.Clock, root string, blacklist []string) (*MemFS, error) {
	fi, err := os.Lstat(root)
	if err != nil {
		return nil, fmt.Errorf("unable to stat root dir: %s", root)
	}
	hdr, err := newMemLayer().createHeader(root, root, "/", fi)
	if err != nil {
		return nil, fmt.Errorf("unable to create root header")
	}
	return &MemFS{
		clk:       clk,
		tree:      newMemFSNode(newContentMemFile(root, "/", hdr)),
		blacklist: blacklist,
	}, nil
}
func (fs *MemFS) Reset() {
	fs.tree.children = make(map[string]*memFSNode)
}
func (fs *MemFS) Checkpoint(newRoot string, sources []string) error {
	if len(sources) == 0 {
		return nil
	}
	resolvedSources := []string{}
	for _, src := range sources {
		if matches, err := filepath.Glob(src); err != nil || len(matches) == 0 {
			resolvedSources = append(resolvedSources, src)
		} else {
			resolvedSources = append(resolvedSources, matches...)
		}
	}
	log.Infof("* Moving directories %v to %s", sources, newRoot)
	for _, src := range resolvedSources {
		if !filepath.IsAbs(src) {
			src = filepath.Join(fs.tree.src, src)
		}
		trimmedSrc, err := pathutils.TrimRoot(src, fs.tree.src)
		if err != nil {
			return fmt.Errorf("trim src %s: %s", src, err)
		}
		dst := filepath.Join(newRoot, trimmedSrc)
		sourceInfo, err := os.Stat(src)
		if err != nil {
			return fmt.Errorf("stat %s: %s", src, err)
		}
		fi, err := os.Lstat(src)
		if err != nil {
			return fmt.Errorf("lstat %s: %s", src, err)
		}
		stat := utils.FileInfoStat(fi)
		copier := fileio.NewCopier(fs.blacklist,
			fileio.WithDstDirOwner(int(stat.Uid), int(stat.Gid), false))
		if sourceInfo.IsDir() {
			if err := copier.CopyDir(src, dst); err != nil {
				return fmt.Errorf("copy dir %s: %s", src, err)
			}
		} else {
			if err := copier.CopyFile(src, dst); err != nil {
				return fmt.Errorf("copy file %s: %s", src, err)
			}
		}
	}
	return nil
}
func (fs *MemFS) Remove() error {
	return removeAllChildren(fs.tree.src, fs.blacklist)
}