1
2
3
4 package filesys
5
6 import (
7 "bytes"
8 "fmt"
9 "io"
10 "log"
11 "os"
12 "path/filepath"
13 "regexp"
14 "sort"
15 "strings"
16
17 "sigs.k8s.io/kustomize/kyaml/errors"
18 )
19
20 var _ File = &fsNode{}
21 var _ FileSystem = &fsNode{}
22
23
24 type fsNode struct {
25
26 parent *fsNode
27
28
29
30 nilParentName string
31
32
33
34
35
36 dir map[string]*fsNode
37
38
39 content []byte
40
41
42
43 offset *int
44 }
45
46
47
48
49
50
51 func MakeEmptyDirInMemory() *fsNode {
52 return &fsNode{
53 dir: make(map[string]*fsNode),
54 }
55 }
56
57
58
59
60
61
62
63
64 func MakeFsInMemory() FileSystem {
65 return &fsNode{
66 nilParentName: Separator,
67 dir: make(map[string]*fsNode),
68 }
69 }
70
71
72 func (n *fsNode) Name() string {
73 if n.parent == nil {
74
75 return n.nilParentName
76 }
77 if !n.parent.isNodeADir() {
78 log.Fatal("parent not a dir")
79 }
80 for key, value := range n.parent.dir {
81 if value == n {
82 return key
83 }
84 }
85 log.Fatal("unable to find fsNode name")
86 return ""
87 }
88
89
90 func (n *fsNode) Path() string {
91 if n.parent == nil {
92 return n.nilParentName
93 }
94 if !n.parent.isNodeADir() {
95 log.Fatal("parent not a dir, structural error")
96 }
97 return filepath.Join(n.parent.Path(), n.Name())
98 }
99
100
101
102 func mySplit(s string) (string, string) {
103 dName, fName := filepath.Split(s)
104 return StripTrailingSeps(dName), fName
105 }
106
107 func (n *fsNode) addFile(name string, c []byte) (result *fsNode, err error) {
108 parent := n
109 dName, fileName := mySplit(name)
110 if dName != "" {
111 parent, err = parent.addDir(dName)
112 if err != nil {
113 return nil, err
114 }
115 }
116 if !isLegalFileNameForCreation(fileName) {
117 return nil, fmt.Errorf(
118 "illegal name '%s' in file creation", fileName)
119 }
120 result, ok := parent.dir[fileName]
121 if ok {
122
123 if result.offset != nil {
124 return nil, fmt.Errorf("cannot add already opened file '%s'", n.Path())
125 }
126 result.content = append(result.content[:0], c...)
127 return result, nil
128 }
129 result = &fsNode{
130 content: append([]byte(nil), c...),
131 parent: parent,
132 }
133 parent.dir[fileName] = result
134 return result, nil
135 }
136
137
138
139 func (n *fsNode) Create(path string) (result File, err error) {
140 f, err := n.AddFile(path, nil)
141 if err != nil {
142 return f, err
143 }
144 f.offset = new(int)
145 return f, nil
146 }
147
148
149 func (n *fsNode) WriteFile(path string, d []byte) error {
150 _, err := n.AddFile(path, d)
151 return err
152 }
153
154
155
156 func (n *fsNode) AddFile(
157 name string, c []byte) (result *fsNode, err error) {
158 if n.dir == nil {
159 return nil, fmt.Errorf(
160 "cannot add a file to a non-directory '%s'", n.Name())
161 }
162 return n.addFile(cleanQueryPath(name), c)
163 }
164
165 func (n *fsNode) addDir(path string) (result *fsNode, err error) {
166 parent := n
167 dName, subDirName := mySplit(path)
168 if dName != "" {
169 parent, err = n.addDir(dName)
170 if err != nil {
171 return nil, err
172 }
173 }
174 switch subDirName {
175 case "", SelfDir:
176 return n, nil
177 case ParentDir:
178 if n.parent == nil {
179 return nil, fmt.Errorf(
180 "cannot add a directory above '%s'", n.Path())
181 }
182 return n.parent, nil
183 default:
184 if !isLegalFileNameForCreation(subDirName) {
185 return nil, fmt.Errorf(
186 "illegal name '%s' in directory creation", subDirName)
187 }
188 result, ok := parent.dir[subDirName]
189 if ok {
190 if result.isNodeADir() {
191
192 return result, nil
193 }
194 return nil, fmt.Errorf(
195 "cannot make dir '%s'; a file of that name already exists in '%s'",
196 subDirName, parent.Name())
197 }
198 result = &fsNode{
199 dir: make(map[string]*fsNode),
200 parent: parent,
201 }
202 parent.dir[subDirName] = result
203 return result, nil
204 }
205 }
206
207
208
209 func (n *fsNode) Mkdir(path string) error {
210 _, err := n.AddDir(path)
211 return err
212 }
213
214
215
216 func (n *fsNode) MkdirAll(path string) error {
217 _, err := n.AddDir(path)
218 return err
219 }
220
221
222
223 func (n *fsNode) AddDir(path string) (result *fsNode, err error) {
224 if n.dir == nil {
225 return nil, fmt.Errorf(
226 "cannot add a directory to file node '%s'", n.Name())
227 }
228 return n.addDir(cleanQueryPath(path))
229 }
230
231
232 func (n *fsNode) CleanedAbs(path string) (ConfirmedDir, string, error) {
233 node, err := n.Find(path)
234 if err != nil {
235 return "", "", errors.WrapPrefixf(err, "unable to clean")
236 }
237 if node == nil {
238 return "", "", notExistError(path)
239 }
240 if node.isNodeADir() {
241 return ConfirmedDir(node.Path()), "", nil
242 }
243 return ConfirmedDir(node.parent.Path()), node.Name(), nil
244 }
245
246
247
248 func (n *fsNode) Exists(path string) bool {
249 if !n.isNodeADir() {
250 return n.Name() == path
251 }
252 result, err := n.Find(path)
253 if err != nil {
254 return false
255 }
256 return result != nil
257 }
258
259 func cleanQueryPath(path string) string {
260
261
262
263 return filepath.Clean(StripLeadingSeps(path))
264 }
265
266
267
268 func (n *fsNode) Find(path string) (*fsNode, error) {
269 if !n.isNodeADir() {
270 return nil, fmt.Errorf("can only find inside a dir")
271 }
272 if path == "" {
273
274
275 return nil, nil
276 }
277 if (n.parent == nil && path == n.nilParentName) || path == SelfDir {
278
279 return n, nil
280 }
281 return n.findIt(cleanQueryPath(path))
282 }
283
284 func (n *fsNode) findIt(path string) (result *fsNode, err error) {
285 parent := n
286 dName, item := mySplit(path)
287 if dName != "" {
288 parent, err = n.findIt(dName)
289 if err != nil {
290 return nil, err
291 }
292 if parent == nil {
293
294 return nil, nil
295 }
296 }
297 if !parent.isNodeADir() {
298 return nil, fmt.Errorf("'%s' is not a directory", parent.Path())
299 }
300 return parent.dir[item], nil
301 }
302
303
304
305 func (n *fsNode) RemoveAll(path string) error {
306 result, err := n.Find(path)
307 if err != nil {
308 return err
309 }
310 if result == nil {
311
312 return nil
313 }
314 return result.Remove()
315 }
316
317
318 func (n *fsNode) Remove() error {
319 if n.parent == nil {
320 return fmt.Errorf("cannot remove a root node")
321 }
322 if !n.parent.isNodeADir() {
323 log.Fatal("parent not a dir")
324 }
325 for key, value := range n.parent.dir {
326 if value == n {
327 delete(n.parent.dir, key)
328 return nil
329 }
330 }
331 log.Fatal("unable to find self in parent")
332 return nil
333 }
334
335
336
337 func (n *fsNode) isNodeADir() bool {
338 return n.dir != nil
339 }
340
341
342
343
344 func (n *fsNode) IsDir(path string) bool {
345 result, err := n.Find(path)
346 if err != nil || result == nil {
347 return false
348 }
349 return result.isNodeADir()
350 }
351
352
353 func (n *fsNode) ReadDir(path string) ([]string, error) {
354 if !n.Exists(path) {
355 return nil, notExistError(path)
356 }
357 if !n.IsDir(path) {
358 return nil, fmt.Errorf("%s is not a directory", path)
359 }
360
361 dir, err := n.Find(path)
362 if err != nil {
363 return nil, err
364 }
365 if dir == nil {
366 return nil, fmt.Errorf("could not find directory %s", path)
367 }
368
369 keys := make([]string, len(dir.dir))
370 i := 0
371 for k := range dir.dir {
372 keys[i] = k
373 i++
374 }
375 return keys, nil
376 }
377
378
379 func (n *fsNode) Size() int64 {
380 if n.isNodeADir() {
381 return int64(len(n.dir))
382 }
383 return int64(len(n.content))
384 }
385
386
387
388
389
390
391
392
393
394
395
396
397
398 func (n *fsNode) Open(path string) (File, error) {
399 result, err := n.Find(path)
400 if err != nil {
401 return nil, err
402 }
403 if result == nil {
404 return nil, notExistError(path)
405 }
406 if result.offset != nil {
407 return nil, fmt.Errorf("cannot open previously opened file '%s'", path)
408 }
409 result.offset = new(int)
410 return result, nil
411 }
412
413
414 func (n *fsNode) Close() error {
415 if n.offset == nil {
416 return fmt.Errorf("cannot close already closed file '%s'", n.Path())
417 }
418 n.offset = nil
419 return nil
420 }
421
422
423 func (n *fsNode) ReadFile(path string) (c []byte, err error) {
424 result, err := n.Find(path)
425 if err != nil {
426 return nil, err
427 }
428 if result == nil {
429 return nil, notExistError(path)
430 }
431 if result.isNodeADir() {
432 return nil, fmt.Errorf("cannot read content from non-file '%s'", n.Path())
433 }
434 c = make([]byte, len(result.content))
435 copy(c, result.content)
436 return c, nil
437 }
438
439
440 func (n *fsNode) Read(d []byte) (c int, err error) {
441 if n.isNodeADir() {
442 return 0, fmt.Errorf(
443 "cannot read content from non-file '%s'", n.Path())
444 }
445 if n.offset == nil {
446 return 0, fmt.Errorf("cannot read from closed file '%s'", n.Path())
447 }
448
449 rest := n.content[*n.offset:]
450 if len(d) < len(rest) {
451 rest = rest[:len(d)]
452 } else {
453 err = io.EOF
454 }
455 copy(d, rest)
456 *n.offset += len(rest)
457 return len(rest), err
458 }
459
460
461 func (n *fsNode) Write(p []byte) (c int, err error) {
462 if n.isNodeADir() {
463 return 0, fmt.Errorf(
464 "cannot write content to non-file '%s'", n.Path())
465 }
466 if n.offset == nil {
467 return 0, fmt.Errorf("cannot write to closed file '%s'", n.Path())
468 }
469 n.content = append(n.content[:*n.offset], p...)
470 *n.offset = len(n.content)
471 return len(p), nil
472 }
473
474
475 func (n *fsNode) ContentMatches(v []byte) bool {
476 return bytes.Equal(v, n.content)
477 }
478
479
480 func (n *fsNode) GetContent() []byte {
481 return n.content
482 }
483
484
485 func (n *fsNode) Stat() (os.FileInfo, error) {
486 return fileInfo{node: n}, nil
487 }
488
489
490 func (n *fsNode) Walk(path string, walkFn filepath.WalkFunc) error {
491 result, err := n.Find(path)
492 if err != nil {
493 return err
494 }
495 if result == nil {
496 return notExistError(path)
497 }
498 return result.WalkMe(walkFn)
499 }
500
501
502 func (n *fsNode) WalkMe(walkFn filepath.WalkFunc) error {
503 fi, err := n.Stat()
504
505 err = walkFn(n.Path(), fi, err)
506 if !n.isNodeADir() {
507
508 return err
509 }
510
511 if err == filepath.SkipDir {
512 return nil
513 }
514
515 for _, k := range n.sortedDirEntries() {
516 if err := n.dir[k].WalkMe(walkFn); err != nil {
517 if err == filepath.SkipDir {
518
519 break
520 }
521
522 return err
523 }
524 }
525 return nil
526 }
527
528 func (n *fsNode) sortedDirEntries() []string {
529 keys := make([]string, len(n.dir))
530 i := 0
531 for k := range n.dir {
532 keys[i] = k
533 i++
534 }
535 sort.Strings(keys)
536 return keys
537 }
538
539
540
541 func (n *fsNode) FileCount() int {
542 count := 0
543 n.WalkMe(func(path string, info os.FileInfo, err error) error {
544 if err != nil {
545 return err
546 }
547 if !info.IsDir() {
548 count++
549 }
550 return nil
551 })
552 return count
553 }
554
555 func (n *fsNode) DebugPrint() {
556 n.WalkMe(func(path string, info os.FileInfo, err error) error {
557 if err != nil {
558 fmt.Printf("err '%v' at path %q\n", err, path)
559 return nil
560 }
561 if info.IsDir() {
562 if info.Size() == 0 {
563 fmt.Println("empty dir: " + path)
564 }
565 } else {
566 fmt.Println(" file: " + path)
567 }
568 return nil
569 })
570 }
571
572 var legalFileNamePattern = regexp.MustCompile("^[a-zA-Z0-9-_.:]+$")
573
574
575
576
577 func isLegalFileNameForCreation(n string) bool {
578 if n == "" || n == SelfDir || !legalFileNamePattern.MatchString(n) {
579 return false
580 }
581 return !strings.Contains(n, ParentDir)
582 }
583
584
585
586 func (n *fsNode) RegExpGlob(pattern string) ([]string, error) {
587 var result []string
588 var expression = regexp.MustCompile(pattern)
589 err := n.WalkMe(func(path string, info os.FileInfo, err error) error {
590 if err != nil {
591 return err
592 }
593 if !info.IsDir() {
594 if expression.MatchString(path) {
595 result = append(result, path)
596 }
597 }
598 return nil
599 })
600 if err != nil {
601 return nil, err
602 }
603 sort.Strings(result)
604 return result, nil
605 }
606
607
608
609
610
611
612 func (n *fsNode) Glob(pattern string) ([]string, error) {
613 var result []string
614 var allFiles []string
615 err := n.WalkMe(func(path string, info os.FileInfo, err error) error {
616 if err != nil {
617 return err
618 }
619 if !info.IsDir() {
620 match, err := filepath.Match(pattern, path)
621 if err != nil {
622 return err
623 }
624 if match {
625 allFiles = append(allFiles, path)
626 }
627 }
628 return nil
629 })
630 if err != nil {
631 return nil, err
632 }
633 if IsHiddenFilePath(pattern) {
634 result = allFiles
635 } else {
636 result = RemoveHiddenFiles(allFiles)
637 }
638 sort.Strings(result)
639 return result, nil
640 }
641
642
643
644 type notExistError string
645
646 func (err notExistError) Error() string { return fmt.Sprintf("'%s' doesn't exist", string(err)) }
647 func (err notExistError) Unwrap() error { return os.ErrNotExist }
648
View as plain text