github.com/mgoltzsche/ctnr@v0.7.1-alpha/pkg/fs/tree/fsnodestring.go (about)

     1  package tree
     2  
     3  import (
     4  	"io"
     5  	"net/url"
     6  	"path/filepath"
     7  	"strings"
     8  
     9  	"github.com/mgoltzsche/ctnr/pkg/fs"
    10  	"github.com/mgoltzsche/ctnr/pkg/fs/writer"
    11  	"github.com/pkg/errors"
    12  )
    13  
    14  func ParseFsSpec(b []byte) (r fs.FsNode, err error) {
    15  	r = newFS()
    16  	current := r
    17  	pathMap := map[string]*fs.NodeAttrs{}
    18  	for i, line := range strings.Split(string(b), "\n") {
    19  		line := strings.TrimSpace(line)
    20  		sp := strings.Index(line, " ")
    21  		if sp == -1 {
    22  			// Handle '..'
    23  			if current, err = current.Node(line); err != nil {
    24  				return nil, errors.Wrapf(err, "parse fs spec: line %d", i)
    25  			}
    26  		} else {
    27  			// Add entry
    28  			name, err := url.PathUnescape(line[:sp])
    29  			if err != nil {
    30  				return nil, errors.Wrapf(err, "parse fs spec: line %d", i)
    31  			}
    32  			attrStr := strings.TrimSpace(line[sp:])
    33  			if strings.HasPrefix(attrStr, "hlink=") {
    34  				// hardlink
    35  				linkDest := filepath.Clean(attrStr[6:])
    36  				linkSrc := pathMap[linkDest]
    37  				path := filepath.Join(current.Path(), name)
    38  				if linkSrc == nil {
    39  					return nil, errors.Errorf("parse fs spec: line %d: link %s destination %q does not exist", i, path, linkDest)
    40  				}
    41  				if _, err = current.AddLower(name, linkSrc); err != nil {
    42  					return nil, errors.Wrapf(err, "parse fs spec: line %d", i)
    43  				}
    44  			} else if attrStr == "type="+string(fs.TypeDir) {
    45  				// implicit parent dir
    46  				if current, err = current.AddLower(name, srcParentDir); err != nil {
    47  					return nil, errors.Wrapf(err, "parse fs spec: line %d", i)
    48  				}
    49  				current.SetSource(srcParentDir)
    50  			} else {
    51  				// any other node
    52  				attrs, err := fs.ParseNodeAttrs(attrStr)
    53  				if err != nil {
    54  					return nil, errors.Wrapf(err, "parse fs spec: line %d", i)
    55  				}
    56  				var newNode fs.FsNode
    57  				newNode, err = current.AddLower(name, &attrs)
    58  				if err != nil {
    59  					return nil, errors.Wrapf(err, "parse fs spec: line %d", i)
    60  				}
    61  				src := srcWhiteout
    62  				if attrs.NodeType != fs.TypeWhiteout {
    63  					src = &attrs
    64  					pathMap[newNode.Path()] = &attrs
    65  				}
    66  				newNode.SetSource(src)
    67  				if src.Attrs().NodeType == fs.TypeDir {
    68  					// TODO: maybe remove ugly type assertion
    69  					current = newNode.(*FsNode).pathNode
    70  				}
    71  			}
    72  		}
    73  	}
    74  	return
    75  }
    76  
    77  func (f *FsNode) WriteTo(w io.Writer, attrs fs.AttrSet) (err error) {
    78  	return f.writeTo(w, attrs, map[fs.Source]string{})
    79  }
    80  
    81  func (f *FsNode) writeTo(w io.Writer, attrs fs.AttrSet, written map[fs.Source]string) (err error) {
    82  	sw := writer.NewStringWriter(w, attrs)
    83  	if err = f.writeSpecLine(sw, attrs, written); err != nil {
    84  		return
    85  	}
    86  	if f.child != nil {
    87  		if err = f.child.genFileSpecLines(sw, attrs, written); err != nil {
    88  			return
    89  		}
    90  		if err = f.child.genDirSpecLines(sw, attrs, written); err != nil {
    91  			return
    92  		}
    93  	}
    94  	return
    95  }
    96  
    97  func (f *FsNode) genFileSpecLines(w fs.Writer, attrs fs.AttrSet, written map[fs.Source]string) (err error) {
    98  	if f.NodeType != fs.TypeDir && f.NodeType != fs.TypeOverlay {
    99  		err = f.writeSpecLine(w, attrs, written)
   100  	}
   101  	if f.next != nil && err == nil {
   102  		err = f.next.genFileSpecLines(w, attrs, written)
   103  	}
   104  	return
   105  }
   106  
   107  func (f *FsNode) genDirSpecLines(w fs.Writer, attrs fs.AttrSet, written map[fs.Source]string) (err error) {
   108  	if f.NodeType == fs.TypeDir || f.NodeType == fs.TypeOverlay {
   109  		if err = f.writeSpecLine(w, attrs, written); err != nil {
   110  			return
   111  		}
   112  		if f.child != nil {
   113  			if err = f.child.genFileSpecLines(w, attrs, written); err != nil {
   114  				return
   115  			}
   116  			if err = f.child.genDirSpecLines(w, attrs, written); err != nil {
   117  				return
   118  			}
   119  		}
   120  		// Generate '..' line
   121  		if f.name != "." {
   122  			if err = w.Parent(); err != nil {
   123  				return
   124  			}
   125  		}
   126  	}
   127  	if f.next != nil {
   128  		err = f.next.genDirSpecLines(w, attrs, written)
   129  	}
   130  	return
   131  }
   132  
   133  func (f *FsNode) writeSpecLine(w fs.Writer, attrs fs.AttrSet, written map[fs.Source]string) error {
   134  	return f.source.Write(f.Path(), f.name, w, written)
   135  }