-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathinode.go
318 lines (279 loc) · 9.23 KB
/
inode.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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
package gexto
import (
"github.com/lunixbochs/struc"
"encoding/binary"
"log"
"io"
"bytes"
)
type MoveExtent struct {
Reserved uint32 `struc:"uint32,little"`
Donor_fd uint32 `struc:"uint32,little"`
Orig_start uint64 `struc:"uint64,little"`
Donor_start uint64 `struc:"uint64,little"`
Len uint64 `struc:"uint64,little"`
Moved_len uint64 `struc:"uint64,little"`
};
type ExtentHeader struct {
Magic uint16 `struc:"uint16,little"`
Entries uint16 `struc:"uint16,little"`
Max uint16 `struc:"uint16,little"`
Depth uint16 `struc:"uint16,little"`
Generation uint32 `struc:"uint32,little"`
}
type ExtentInternal struct {
Block uint32 `struc:"uint32,little"`
Leaf_low uint32 `struc:"uint32,little"`
Leaf_high uint16 `struc:"uint16,little"`
Unused uint16 `struc:"uint16,little"`
}
type Extent struct {
Block uint32 `struc:"uint32,little"`
Len uint16 `struc:"uint16,little"`
Start_hi uint16 `struc:"uint16,little"`
Start_lo uint32 `struc:"uint32,little"`
}
type DirectoryEntry2 struct {
Inode uint32 `struc:"uint32,little"`
Rec_len uint16 `struc:"uint16,little"`
Name_len uint8 `struc:"uint8,sizeof=Name"`
Flags uint8 `struc:"uint8"`
Name string `struc:"[]byte"`
}
type DirectoryEntryCsum struct {
FakeInodeZero uint32 `struc:"uint32,little"`
Rec_len uint16 `struc:"uint16,little"`
FakeName_len uint8 `struc:"uint8"`
FakeFileType uint8 `struc:"uint8"`
Checksum uint32 `struc:"uint32,little"`
}
type Inode struct {
Mode uint16 `struc:"uint16,little"`
Uid uint16 `struc:"uint16,little"`
Size_lo uint32 `struc:"uint32,little"`
Atime uint32 `struc:"uint32,little"`
Ctime uint32 `struc:"uint32,little"`
Mtime uint32 `struc:"uint32,little"`
Dtime uint32 `struc:"uint32,little"`
Gid uint16 `struc:"uint16,little"`
Links_count uint16 `struc:"uint16,little"`
Blocks_lo uint32 `struc:"uint32,little"`
Flags uint32 `struc:"uint32,little"`
Osd1 uint32 `struc:"uint32,little"`
BlockOrExtents [60]byte `struc:"[60]byte,little"`
Generation uint32 `struc:"uint32,little"`
File_acl_lo uint32 `struc:"uint32,little"`
Size_high uint32 `struc:"uint32,little"`
Obso_faddr uint32 `struc:"uint32,little"`
// OSD2 - linux only starts
Blocks_high uint16 `struc:"uint16,little"`
File_acl_high uint16 `struc:"uint16,little"`
Uid_high uint16 `struc:"uint16,little"`
Gid_high uint16 `struc:"uint16,little"`
Checksum_low uint16 `struc:"uint16,little"`
Unused uint16 `struc:"uint16,little"`
// OSD2 - linux only ends
Extra_isize uint16 `struc:"uint16,little"`
Checksum_hi uint16 `struc:"uint16,little"`
Ctime_extra uint32 `struc:"uint32,little"`
Mtime_extra uint32 `struc:"uint32,little"`
Atime_extra uint32 `struc:"uint32,little"`
Crtime uint32 `struc:"uint32,little"`
Crtime_extra uint32 `struc:"uint32,little"`
Version_hi uint32 `struc:"uint32,little"`
Projid uint32 `struc:"uint32,little"`
fs *fs
address int64
num int64
};
func (inode *Inode) UsesExtents() bool {
return (inode.Flags & EXTENTS_FL) != 0
}
func (inode *Inode) UsesDirectoryHashTree() bool {
return (inode.Flags & INDEX_FL) != 0
}
func (inode *Inode) ReadDirectory() []DirectoryEntry2 {
if inode.UsesDirectoryHashTree() {
log.Fatalf("Not implemented")
}
f := &File{extFile{
fs: inode.fs,
inode: inode,
pos: 0,
}}
ret := []DirectoryEntry2{}
for {
start, _ := f.Seek(0, 1)
dirEntry := DirectoryEntry2{}
err := struc.Unpack(f, &dirEntry)
if err == io.EOF {
break
} else if err != nil {
log.Fatalf(err.Error())
}
//log.Printf("dirEntry %s: %+v", string(dirEntry.Name), dirEntry)
f.Seek(int64(dirEntry.Rec_len) + start, 0)
if dirEntry.Rec_len < 9 {
log.Fatalf("corrupt direntry")
}
ret = append(ret, dirEntry)
}
return ret
}
func (inode *Inode) AddBlocks(n int64) (blockNum int64, contiguousBlocks int64) {
if !inode.UsesExtents() {
log.Fatalf("Not implemented")
}
r := inode.fs.dev
r.Seek(inode.address + 40, 0)
for {
headerPos, _ := r.Seek(0,1)
extentHeader := &ExtentHeader{}
struc.Unpack(r, &extentHeader)
//log.Printf("extent header: %+v", extentHeader)
if extentHeader.Depth == 0 { // Leaf
max := int64(0)
for i := uint16(0); i < extentHeader.Entries; i++ {
extent := &Extent{}
struc.Unpack(r, &extent)
upper := int64(extent.Block) + int64(extent.Len)
if upper > max {
max = upper
}
}
if extentHeader.Entries < extentHeader.Max {
savePos, _ := r.Seek(0, 1)
blockNum, numBlocks := inode.fs.GetFreeBlocks(int(n))
newExtent := &Extent{
Block: uint32(max),
Len: uint16(numBlocks),
Start_hi: uint16(blockNum >> 32),
Start_lo: uint32(blockNum & 0xFFFFFFFF),
}
r.Seek(savePos, 0)
struc.Pack(r, &newExtent)
extentHeader.Entries++
//log.Println("Extended to", extentHeader.Entries, headerPos)
r.Seek(headerPos, 0)
struc.Pack(r, extentHeader)
r.Seek(inode.address, 0)
struc.Unpack(r, inode)
inode.Blocks_lo += uint32(numBlocks*inode.fs.sb.GetBlockSize()/512)
inode.UpdateCsumAndWriteback()
//log.Println("AddBlocks", n, numBlocks)
return blockNum, numBlocks
} else {
log.Fatalf("Unable to extend no room")
}
} else {
max := uint32(0)
var best *ExtentInternal
for i := uint16(0); i < extentHeader.Entries; i++ {
extent := &ExtentInternal{}
struc.Unpack(r, &extent)
//log.Printf("extent internal: %+v", extent)
if extent.Block > max {
best = extent
}
}
newBlock := int64(best.Leaf_high<<32) + int64(best.Leaf_low)
r.Seek(newBlock*inode.fs.sb.GetBlockSize(), 0)
}
}
//log.Println("AddBlocks", n, 0)
return 0,0
}
func (inode *Inode) UpdateCsumAndWriteback() {
if inode.fs.sb.Inode_size != 128 {
log.Fatalln("Unsupported inode size", inode.fs.sb.Inode_size)
}
cs := NewChecksummer(inode.fs.sb)
cs.Write(inode.fs.sb.Uuid[:])
cs.WriteUint32(uint32(inode.num))
cs.WriteUint32(uint32(inode.Generation))
inode.Checksum_low = 0
struc.Pack(LimitWriter(cs, 128), inode)
inode.Checksum_low = uint16(cs.Get() & 0xFFFF)
inode.fs.dev.Seek(inode.address, 0)
struc.Pack(LimitWriter(inode.fs.dev, 128), inode)
}
// Returns the blockId of the file block, and the number of contiguous blocks
func (inode *Inode) GetBlockPtr(num int64) (int64, int64, bool) {
if inode.UsesExtents() {
//log.Println("Finding", num)
r := io.Reader(bytes.NewReader(inode.BlockOrExtents[:]))
for {
extentHeader := &ExtentHeader{}
struc.Unpack(r, &extentHeader)
//log.Printf("extent header: %+v", extentHeader)
if extentHeader.Depth == 0 { // Leaf
for i := uint16(0); i < extentHeader.Entries; i++ {
extent := &Extent{}
struc.Unpack(r, &extent)
//log.Printf("extent leaf: %+v", extent)
if int64(extent.Block) <= num && int64(extent.Block)+int64(extent.Len) > num {
//log.Println("Found")
return int64(extent.Start_hi<<32) + int64(extent.Start_lo) + num - int64(extent.Block), int64(extent.Block) + int64(extent.Len) - num, true
}
}
return 0, 0, false
} else {
found := false
for i := uint16(0); i < extentHeader.Entries; i++ {
extent := &ExtentInternal{}
struc.Unpack(r, &extent)
//log.Printf("extent internal: %+v", extent)
if int64(extent.Block) <= num {
newBlock := int64(extent.Leaf_high<<32) + int64(extent.Leaf_low)
inode.fs.dev.Seek(newBlock * inode.fs.sb.GetBlockSize(), 0)
r = inode.fs.dev
found = true
break
}
}
if !found {
return 0,0, false
}
}
}
}
if num < 12 {
return int64(binary.LittleEndian.Uint32(inode.BlockOrExtents[4*num:])), 1, true
}
num -= 12
indirectsPerBlock := inode.fs.sb.GetBlockSize() / 4
if num < indirectsPerBlock {
ptr := int64(binary.LittleEndian.Uint32(inode.BlockOrExtents[4*12:]))
return inode.getIndirectBlockPtr(ptr, num),1, true
}
num -= indirectsPerBlock
if num < indirectsPerBlock * indirectsPerBlock {
ptr := int64(binary.LittleEndian.Uint32(inode.BlockOrExtents[4*13:]))
l1 := inode.getIndirectBlockPtr(ptr, num / indirectsPerBlock)
return inode.getIndirectBlockPtr(l1, num % indirectsPerBlock),1, true
}
num -= indirectsPerBlock * indirectsPerBlock
if num < indirectsPerBlock * indirectsPerBlock * indirectsPerBlock {
log.Println("Triple indirection")
ptr := int64(binary.LittleEndian.Uint32(inode.BlockOrExtents[4*14:]))
l1 := inode.getIndirectBlockPtr(ptr, num / (indirectsPerBlock * indirectsPerBlock))
l2 := inode.getIndirectBlockPtr(l1, (num / indirectsPerBlock) % indirectsPerBlock)
return inode.getIndirectBlockPtr(l2, num % (indirectsPerBlock * indirectsPerBlock)),1, true
}
log.Fatalf("Exceeded maximum possible block count")
return 0,0,false
}
func (inode *Inode) getIndirectBlockPtr(blockNum int64, offset int64) int64 {
inode.fs.dev.Seek(blockNum * inode.fs.sb.GetBlockSize() + offset * 4, 0)
x := make([]byte, 4)
inode.fs.dev.Read(x)
return int64(binary.LittleEndian.Uint32(x))
}
func (inode *Inode) GetSize() int64 {
return (int64(inode.Size_high) << 32) | int64(inode.Size_lo)
}
func (inode *Inode) SetSize(i int64) {
inode.Size_high = uint32(i >> 32)
inode.Size_lo = uint32(i & 0xFFFFFFFF)
inode.UpdateCsumAndWriteback()
}