-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinode.py
More file actions
263 lines (223 loc) · 9.45 KB
/
Copy pathinode.py
File metadata and controls
263 lines (223 loc) · 9.45 KB
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
from typing import Any, Generator
from construct import Container
import constants as C
from enum import Enum
from object_accessor import ObjectAccessor
from free_block_interface import FreeBlockInterface
from file_index_block import FileIndexBlock
from math import ceil
from utils import timestamp
from structures import InodeStruct
class FILE_TYPE(Enum):
FILE = 0
CHAR_DEVICE = 1
DIR = 2
BLOCK_DEVICE = 3
class Inode:
"""
注意:
需要手动flush
内部会维护一个块数,在init的时候根据文件大小进行初始化
(所以要保证在init的时候文件大小和块数是能对上的)
如果一个文件索引块是空的,就必须被移除;
不论是增加文件大小还是减小,都要先操作一个索引块,再操作文件大小;
(因为文件大小被用来定位需要操作的索引块)
"""
def __init__(self, index: int,
data: Container[Any],
object_accessor: ObjectAccessor,
free_block_manager: FreeBlockInterface):
self.index = index
self.data = data
self.object_accessor = object_accessor
self.free_block_manager = free_block_manager
self.block_count = ceil(self.data.d_size / C.BLOCK_BYTES)
@classmethod
def from_index(cls, index: int,
object_accessor: ObjectAccessor,
free_block_manager: FreeBlockInterface):
"""
通过Inode号码构造Inode对象
"""
# inode_data: Container[Any] = object_accessor.inodes[index]._obj
inode_data: Container[Any] = object_accessor.inodes[index]
return cls(index, inode_data, object_accessor, free_block_manager)
@classmethod
def new(cls, index: int,
file_type: FILE_TYPE,
object_accessor: ObjectAccessor,
free_block_manager: FreeBlockInterface):
mode = 0b1_00_0000_111_111_111
mode |= file_type.value << 13
mode = mode.to_bytes(4, 'little')
inode = mode + b'\x00' * (C.INODE_BYTES - 4)
inode = InodeStruct.parse(inode)
time = timestamp()
inode.d_atime = time
inode.d_mtime = time
return cls(index, inode, object_accessor, free_block_manager)
@property
def file_type(self) -> FILE_TYPE:
return FILE_TYPE(self.data.d_mode.IFMT)
@property
def size(self) -> int:
return self.data.d_size
@size.setter
def size(self, value: int) -> None:
self.data.d_size = value
def flush(self) -> None:
self.object_accessor.inodes[self.index] = self.data
def _get_index_block(self, block_index: int) -> FileIndexBlock:
return FileIndexBlock.from_index(block_index, self.object_accessor)
def _get_index_list(self, block_index: int) -> list[int]:
list = self._get_index_block(block_index).to_list()
while list and list[-1] == 0:
list.pop()
return list
def _get_block_index(self, index: int):
if index < C.FILE_INDEX_SMALL_THRESHOLD:
return index, -1, -1
if index < C.FILE_INDEX_LARGE_THRESHOLD:
index -= C.FILE_INDEX_SMALL_THRESHOLD
index_1 = index // C.FILE_INDEX_PER_BLOCK + C.INODE_SMALL_THRESHOLD
index_2 = index % C.FILE_INDEX_PER_BLOCK
return index_1, index_2, -1
if index < C.FILE_INDEX_HUGE_THRESHOLD:
index -= C.FILE_INDEX_LARGE_THRESHOLD
index_1 = index // (C.FILE_INDEX_PER_BLOCK ** 2) + C.INODE_LARGE_THRESHOLD
index_2 = (index % (C.FILE_INDEX_PER_BLOCK ** 2)) // C.FILE_INDEX_PER_BLOCK
index_3 = index % C.FILE_INDEX_PER_BLOCK
return index_1, index_2, index_3
return -1, -1, -1
def _block_index_planner(self, start: int):
index_1, index_2, index_3 = self._get_block_index(start)
while index_1 < C.INODE_HUGE_THRESHOLD:
yield index_1, index_2, index_3
index_1 += 1
index_2 = -1 if index_1 < C.INODE_SMALL_THRESHOLD else 0
index_3 = -1 if index_1 < C.INODE_LARGE_THRESHOLD else 0
raise StopIteration
def _block_list(self, start_block: int = 0) -> Generator[int, None, None]:
"""
返回完整的文件块序号列表
"""
compressed_list: list[int] = self.data.d_addr.copy()
while compressed_list and compressed_list[-1] == 0: # 去掉末尾的0
compressed_list.pop()
if not compressed_list:
return
for start_index_1, start_index_2, start_index_3 in self._block_index_planner(start_block):
if start_index_2 == -1:
yield compressed_list[start_index_1]
continue
for index_2 in self._get_index_list(compressed_list[start_index_1])[start_index_2:]:
if start_index_3 == -1:
yield index_2
continue
for index_3 in self._get_index_list(index_2)[start_index_3:]:
yield index_3
start_index_3 = 0
start_index_2 = 0
def block_list(self, start_block: int = 0, length: int = -1) -> Generator[int, None, None]:
"""
获取文件的块序号列表
"""
if length < 0:
length = self.block_count - start_block
else:
length = min(length, self.block_count - start_block)
if length <= 0:
return
for block_index in self._block_list(start_block):
yield block_index
length -= 1
if length <= 0:
break
def peek_block(self, index: int) -> int:
"""
获取文件的一个块
"""
iterator = self.block_list(index, 1)
return next(iterator)
def _new_data_block_index(self) -> int:
return self.free_block_manager.allocate_block(zero=True)
def _delete_data_block(self, index: int) -> None:
self.free_block_manager.release_block(index)
def push_block(self, index) -> None:
"""
向索引列表中添加一个新的索引
"""
insert_position: int = self.block_count
self.block_count += 1
index_1, index_2, index_3 = self._get_block_index(insert_position)
# 小型文件
if insert_position < C.FILE_INDEX_SMALL_THRESHOLD:
self.data.d_addr[insert_position] = index
return
# 大型文件
if insert_position < C.FILE_INDEX_LARGE_THRESHOLD:
# 是否应新增第一层索引块
if index_2 == 0:
self.data.d_addr[index_1] = self._new_data_block_index()
# 获取第一层索引块
block_1 = self._get_index_block(self.data.d_addr[index_1])
# 设置索引
block_1[index_2] = index
return
# 巨型文件
if insert_position < C.FILE_INDEX_HUGE_THRESHOLD:
# 是否应新增第一层索引块
if index_2 == index_3 == 0:
self.data.d_addr[index_1] = self._new_data_block_index()
# 获取第一层索引块
block_1 = self._get_index_block(self.data.d_addr[index_1])
# 是否应新增第二层索引块
if index_3 == 0:
block_1[index_2] = self._new_data_block_index()
# 获取第二层索引块
block_2 = block_1.subblock(index_2)
# 设置索引
block_2[index_3] = index
return
raise Exception("文件已达最大大小,无法增加索引块")
def pop_block(self) -> int:
pop_position: int = self.block_count - 1
self.block_count -= 1
index_1, index_2, index_3 = self._get_block_index(pop_position)
# 小型文件
if pop_position < C.FILE_INDEX_SMALL_THRESHOLD:
result = self.data.d_addr[pop_position]
self.data.d_addr[pop_position] = 0
return result
# 大型文件
if pop_position < C.FILE_INDEX_LARGE_THRESHOLD:
# 清除索引
block_1 = self._get_index_block(self.data.d_addr[index_1])
result = block_1[index_2]
block_1[index_2] = 0
# 是否应删除第一层索引块
if index_2 == 0:
self._delete_data_block(self.data.d_addr[index_1])
self.data.d_addr[index_1] = 0
return result
# 巨型文件
if pop_position < C.FILE_INDEX_HUGE_THRESHOLD:
# 清除索引
block_1 = self._get_index_block(self.data.d_addr[index_1])
block_2 = block_1.subblock(index_2)
result = block_2[index_3]
block_2[index_3] = 0
# 是否应删除第二层索引块
if index_3 == 0:
self._delete_data_block(block_1[index_2])
block_1[index_2] = 0
# 是否应删除第一层索引块
if index_2 == index_3 == 0:
self._delete_data_block(self.data.d_addr[index_1])
self.data.d_addr[index_1] = 0
return result
raise Exception("文件为空,无法删除索引块")
def update_atime(self) -> None:
self.data.d_atime = timestamp()
def update_mtime(self) -> None:
self.data.d_mtime = timestamp()