• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

cinode / go / 15633973488

13 Jun 2025 11:57AM UTC coverage: 95.318%. Remained the same
15633973488

Pull #45

github

byo
fixup
Pull Request #45: Fix github action issues

3013 of 3161 relevant lines covered (95.32%)

1.07 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.95
/pkg/cinodefs/node_directory.go
1
/*
2
Copyright © 2023 Bartłomiej Święcki (byo)
3

4
Licensed under the Apache License, Version 2.0 (the "License");
5
you may not use this file except in compliance with the License.
6
You may obtain a copy of the License at
7

8
    http://www.apache.org/licenses/LICENSE-2.0
9

10
Unless required by applicable law or agreed to in writing, software
11
distributed under the License is distributed on an "AS IS" BASIS,
12
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
See the License for the specific language governing permissions and
14
limitations under the License.
15
*/
16

17
package cinodefs
18

19
import (
20
        "context"
21
        "sort"
22

23
        "github.com/cinode/go/pkg/blobtypes"
24
        "github.com/cinode/go/pkg/cinodefs/protobuf"
25
        "github.com/cinode/go/pkg/utilities/golang"
26
)
27

28
// nodeDirectory holds a directory entry loaded into memory
29
type nodeDirectory struct {
30
        entries map[string]node
31
        stored  *Entrypoint // current entrypoint, will be nil if directory was modified
32
        dState  dirtyState  // true if any subtree is dirty
33
}
34

35
func (d *nodeDirectory) dirty() dirtyState {
×
36
        return d.dState
×
37
}
×
38

39
func (d *nodeDirectory) flush(ctx context.Context, gc *graphContext) (node, *Entrypoint, error) {
1✔
40
        if d.dState == dsClean {
2✔
41
                // all clear, nothing to flush here or in sub-trees
1✔
42
                return d, d.stored, nil
1✔
43
        }
1✔
44

45
        if d.dState == dsSubDirty {
2✔
46
                // Some sub-nodes are dirty, need to propagate flush to
1✔
47
                flushedEntries := make(map[string]node, len(d.entries))
1✔
48
                for name, entry := range d.entries {
2✔
49
                        target, _, err := entry.flush(ctx, gc)
1✔
50
                        if err != nil {
1✔
51
                                return nil, nil, err
×
52
                        }
×
53

54
                        flushedEntries[name] = target
1✔
55
                }
56

57
                // directory itself was not modified and does not need flush, don't bother
58
                // saving it to datastore
59
                return &nodeDirectory{
1✔
60
                        entries: flushedEntries,
1✔
61
                        stored:  d.stored,
1✔
62
                        dState:  dsClean,
1✔
63
                }, d.stored, nil
1✔
64
        }
65

66
        golang.Assert(d.dState == dsDirty, "ensure correct dirtiness state")
1✔
67

1✔
68
        // Directory has changed, have to recalculate its blob and save it in data store
1✔
69
        dir := protobuf.Directory{
1✔
70
                Entries: make([]*protobuf.Directory_Entry, 0, len(d.entries)),
1✔
71
        }
1✔
72
        flushedEntries := make(map[string]node, len(d.entries))
1✔
73
        for name, entry := range d.entries {
2✔
74
                target, targetEP, err := entry.flush(ctx, gc)
1✔
75
                if err != nil {
2✔
76
                        return nil, nil, err
1✔
77
                }
1✔
78

79
                flushedEntries[name] = target
1✔
80
                dir.Entries = append(dir.Entries, &protobuf.Directory_Entry{
1✔
81
                        Name: name,
1✔
82
                        Ep:   &targetEP.ep,
1✔
83
                })
1✔
84
        }
85

86
        // Sort by name - that way we gain deterministic order during
87
        // serialization od the directory
88
        sort.Slice(dir.Entries, func(i, j int) bool {
2✔
89
                return dir.Entries[i].Name < dir.Entries[j].Name
1✔
90
        })
1✔
91

92
        ep, err := gc.createProtobufMessage(ctx, blobtypes.Static, &dir)
1✔
93
        if err != nil {
2✔
94
                return nil, nil, err
1✔
95
        }
1✔
96
        ep.ep.MimeType = CinodeDirMimeType
1✔
97

1✔
98
        return &nodeDirectory{
1✔
99
                entries: flushedEntries,
1✔
100
                stored:  ep,
1✔
101
                dState:  dsClean,
1✔
102
        }, ep, nil
1✔
103
}
104

105
func (c *nodeDirectory) traverse(
106
        ctx context.Context,
107
        gc *graphContext,
108
        path []string,
109
        pathPosition int,
110
        linkDepth int,
111
        isWritable bool,
112
        opts traverseOptions,
113
        whenReached traverseGoalFunc,
114
) (
115
        node,
116
        dirtyState,
117
        error,
118
) {
1✔
119
        if pathPosition == len(path) {
2✔
120
                return whenReached(ctx, c, isWritable)
1✔
121
        }
1✔
122

123
        subNode, found := c.entries[path[pathPosition]]
1✔
124
        if !found {
2✔
125
                if !opts.createNodes {
2✔
126
                        return nil, 0, ErrEntryNotFound
1✔
127
                }
1✔
128
                if !isWritable {
2✔
129
                        return nil, 0, ErrMissingWriterInfo
1✔
130
                }
1✔
131
                // create new sub-path
132
                newNode, err := c.traverseRecursiveNewPath(
1✔
133
                        ctx,
1✔
134
                        path,
1✔
135
                        pathPosition+1,
1✔
136
                        opts,
1✔
137
                        whenReached,
1✔
138
                )
1✔
139
                if err != nil {
1✔
140
                        return nil, 0, err
×
141
                }
×
142
                c.entries[path[pathPosition]] = newNode
1✔
143
                c.dState = dsDirty
1✔
144
                return c, dsDirty, nil
1✔
145
        }
146

147
        // found path entry, descend to sub-node
148
        replacement, replacementState, err := subNode.traverse(
1✔
149
                ctx,
1✔
150
                gc,
1✔
151
                path,
1✔
152
                pathPosition+1,
1✔
153
                0,
1✔
154
                isWritable,
1✔
155
                opts,
1✔
156
                whenReached,
1✔
157
        )
1✔
158
        if err != nil {
2✔
159
                return nil, 0, err
1✔
160
        }
1✔
161
        if opts.doNotCache {
2✔
162
                return c, dsClean, nil
1✔
163
        }
1✔
164

165
        c.entries[path[pathPosition]] = replacement
1✔
166
        if replacementState == dsDirty {
2✔
167
                // child is dirty, this propagates down to the current node
1✔
168
                c.dState = dsDirty
1✔
169
                return c, dsDirty, nil
1✔
170
        }
1✔
171

172
        if replacementState == dsSubDirty {
2✔
173
                // child itself is not dirty, but some sub-node is, sub-dirtiness
1✔
174
                // propagates to the current node, but if the directory is
1✔
175
                // already directly dirty (stronger dirtiness), keep it as it is
1✔
176
                if c.dState != dsDirty {
2✔
177
                        c.dState = dsSubDirty
1✔
178
                }
1✔
179
                return c, dsSubDirty, nil
1✔
180
        }
181

182
        golang.Assert(replacementState == dsClean, "ensure correct dirtiness state")
×
183
        // leave current state as it is
×
184
        return c, dsClean, nil
×
185

186
}
187

188
func (c *nodeDirectory) traverseRecursiveNewPath(
189
        ctx context.Context,
190
        path []string,
191
        pathPosition int,
192
        opts traverseOptions,
193
        whenReached traverseGoalFunc,
194
) (
195
        node,
196
        error,
197
) {
1✔
198
        if len(path) == pathPosition {
2✔
199
                replacement, _, err := whenReached(ctx, nil, true)
1✔
200
                return replacement, err
1✔
201
        }
1✔
202

203
        sub, err := c.traverseRecursiveNewPath(
1✔
204
                ctx,
1✔
205
                path,
1✔
206
                pathPosition+1,
1✔
207
                opts,
1✔
208
                whenReached,
1✔
209
        )
1✔
210
        if err != nil {
1✔
211
                return nil, err
×
212
        }
×
213

214
        return &nodeDirectory{
1✔
215
                entries: map[string]node{
1✔
216
                        path[pathPosition]: sub,
1✔
217
                },
1✔
218
                dState: dsDirty,
1✔
219
        }, nil
1✔
220
}
221

222
func (c *nodeDirectory) entrypoint() (*Entrypoint, error) {
1✔
223
        if c.dState == dsDirty {
2✔
224
                return nil, ErrModifiedDirectory
1✔
225
        }
1✔
226

227
        golang.Assert(
1✔
228
                c.dState == dsClean || c.dState == dsSubDirty,
1✔
229
                "ensure dirtiness state is valid",
1✔
230
        )
1✔
231

1✔
232
        return c.stored, nil
1✔
233
}
234

235
func (c *nodeDirectory) deleteEntry(name string) bool {
1✔
236
        if _, hasEntry := c.entries[name]; !hasEntry {
2✔
237
                return false
1✔
238
        }
1✔
239
        delete(c.entries, name)
1✔
240
        c.dState = dsDirty
1✔
241
        return true
1✔
242
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc