-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathdirectory_walk.go
128 lines (101 loc) · 3.23 KB
/
directory_walk.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package ext4
import (
"io"
"path"
"github.com/dsoprea/go-logging"
)
type directoryWalkQueueItem struct {
fullDirectoryPath string
inode *Inode
directoryBrowser *DirectoryBrowser
}
// DirectoryWalk provides full directory-structure recursion.
type DirectoryWalk struct {
rs io.ReadSeeker
blockGroupDescriptor *BlockGroupDescriptor
inodeQueue []directoryWalkQueueItem
}
func NewDirectoryWalk(rs io.ReadSeeker, bgd *BlockGroupDescriptor, rootInodeNumber int) (dw *DirectoryWalk, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
dw = &DirectoryWalk{
rs: rs,
blockGroupDescriptor: bgd,
}
inode, db, err := dw.openInode(rootInodeNumber)
log.PanicIf(err)
dwqi := directoryWalkQueueItem{
inode: inode,
directoryBrowser: db,
}
dw.inodeQueue = []directoryWalkQueueItem{dwqi}
return dw, nil
}
func (dw *DirectoryWalk) openInode(inodeNumber int) (inode *Inode, db *DirectoryBrowser, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
inode, err = NewInodeWithReadSeeker(dw.blockGroupDescriptor, dw.rs, inodeNumber)
log.PanicIf(err)
db = NewDirectoryBrowser(dw.rs, inode)
return inode, db, nil
}
// Next steps through the entire tree starting at the given root inode, one
// entry at a time. We guarantee that all adjacent entries will be processed
// adjacently. This will not return the "." and ".." entries.
func (dw *DirectoryWalk) Next() (fullPath string, de *DirectoryEntry, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
for {
if len(dw.inodeQueue) == 0 {
return "", nil, io.EOF
}
// Keep popping entries off the current directory until we've read
// everything.
dwqi := dw.inodeQueue[0]
de, err := dwqi.directoryBrowser.Next()
if err == io.EOF {
// No more entries.
dw.inodeQueue = dw.inodeQueue[1:]
continue
} else if err != nil {
log.Panic(err)
}
// There was at least one more entry.
filename := de.Name()
// Skip the special files. We have a handle on things and they're not
// especially useful since they don't actually contain the directory
// names.
if filename == "." || filename == ".." {
continue
}
var fullFilepath string
if dwqi.fullDirectoryPath == "" {
fullFilepath = filename
} else {
fullFilepath = path.Join(dwqi.fullDirectoryPath, filename)
}
// If it's a directory, enqueue it).
// TODO(dustin): We get the impression that the "lost+found" inode isn't necessarily always in inode (11), so we only do a string match. Use `(superblock).SLpfIno` instead.
// TODO(dustin): "lost+found" produces some empty entries for our tiny, mostly untouched, mostly vanilla test image, which doesn't make sense to us. Just skipping for now. Revisit.
if de.IsDirectory() && filename != "lost+found" {
childInode, childDb, err := dw.openInode(int(de.data.Inode))
log.PanicIf(err)
newDwqi := directoryWalkQueueItem{
fullDirectoryPath: fullFilepath,
inode: childInode,
directoryBrowser: childDb,
}
dw.inodeQueue = append(dw.inodeQueue, newDwqi)
}
return fullFilepath, de, nil
}
}