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 }