1
+ using System ;
2
+ using System . Collections . Generic ;
3
+ using System . ComponentModel ;
4
+ using System . ComponentModel . Design . Serialization ;
5
+ using System . IO ;
6
+ using System . Linq ;
7
+
8
+ namespace LibOrbisPkg . PFS
9
+ {
10
+ /// <summary>
11
+ /// Contains the functionality to construct a PFS disk image.
12
+ /// </summary>
13
+ public class PfsBuilder
14
+ {
15
+ static int CeilDiv ( int a , int b ) => a / b + ( a % b == 0 ? 0 : 1 ) ;
16
+ static long CeilDiv ( long a , long b ) => a / b + ( a % b == 0 ? 0 : 1 ) ;
17
+
18
+ private PfsHeader hdr ;
19
+ private List < PfsDinode32 > inodes ;
20
+ private List < List < PfsDirent > > dirents ;
21
+ private List < PfsDirent > super_root_dirents ;
22
+
23
+ private PfsDinode32 super_root_ino , fpt_ino ;
24
+
25
+ private FSDir root ;
26
+
27
+ private List < FSDir > allDirs ;
28
+ private List < FSFile > allFiles ;
29
+ private List < FSNode > allNodes ;
30
+
31
+ private FlatPathTable fpt ;
32
+
33
+ /// <summary>
34
+ /// Builds and saves a PFS image.
35
+ /// </summary>
36
+ /// <param name="p"></param>
37
+ public void BuildPfs ( PfsProperties p )
38
+ {
39
+ // TODO: Combine the superroot-specific stuff with the rest of the data block writing.
40
+ // I think this is as simple as adding superroot and flat_path_table to allNodes
41
+
42
+ hdr = new PfsHeader { BlockSize = p . BlockSize } ;
43
+ inodes = new List < PfsDinode32 > ( ) ;
44
+ dirents = new List < List < PfsDirent > > ( ) ;
45
+
46
+ Console . WriteLine ( "Setting up root structure..." ) ;
47
+ SetupRootStructure ( ) ;
48
+ BuildFSTree ( root , p . RootDirectory ) ;
49
+ allDirs = root . GetAllChildrenDirs ( ) ;
50
+ allFiles = root . GetAllChildrenFiles ( ) ;
51
+ allNodes = new List < FSNode > ( allDirs ) ;
52
+ allNodes . AddRange ( allFiles ) ;
53
+
54
+ Console . WriteLine ( "Creating directory inodes ({0})..." , allDirs . Count ) ;
55
+ addDirInodes ( ) ;
56
+
57
+ Console . WriteLine ( "Creating file inodes ({0})..." , allFiles . Count ) ;
58
+ addFileInodes ( ) ;
59
+
60
+ Console . WriteLine ( "Creating flat_path_table..." ) ;
61
+ fpt = new FlatPathTable ( allNodes ) ;
62
+
63
+
64
+ Console . WriteLine ( "Calculating data block layout..." ) ;
65
+ allNodes . Insert ( 0 , root ) ;
66
+ CalculateDataBlockLayout ( ) ;
67
+
68
+ Console . WriteLine ( "Writing image file..." ) ;
69
+ hdr . Ndblock = allFiles . Sum ( ( f ) => CeilDiv ( f . Size , hdr . BlockSize ) ) ;
70
+ using ( var stream = File . Create ( p . ImageFilename ) )
71
+ {
72
+ Console . WriteLine ( "Writing header..." ) ;
73
+ hdr . WriteToStream ( stream ) ;
74
+ Console . WriteLine ( "Writing inodes..." ) ;
75
+ WriteInodes ( stream ) ;
76
+ Console . WriteLine ( "Writing superroot dirents" ) ;
77
+ WriteSuperrootDirents ( stream ) ;
78
+
79
+ Console . WriteLine ( "Writing flat_path_table" ) ;
80
+ stream . Position = fpt_ino . db [ 0 ] * hdr . BlockSize ;
81
+ fpt . WriteToStream ( stream ) ;
82
+
83
+ Console . WriteLine ( "Writing data blocks..." ) ;
84
+ for ( var x = 0 ; x < allNodes . Count ; x ++ )
85
+ {
86
+ var f = allNodes [ x ] ;
87
+ stream . Position = f . ino . db [ 0 ] * hdr . BlockSize ;
88
+ WriteFSNode ( stream , f ) ;
89
+ }
90
+ }
91
+ }
92
+
93
+ /// <summary>
94
+ /// Adds inodes for each dir.
95
+ /// </summary>
96
+ void addDirInodes ( )
97
+ {
98
+ inodes . Add ( root . ino ) ;
99
+ foreach ( var dir in allDirs )
100
+ {
101
+ var ino = new PfsDinode32
102
+ {
103
+ Mode = InodeMode . dir | InodeMode . rwx ,
104
+ Number = ( uint ) inodes . Count ,
105
+ Blocks = 1 ,
106
+ Size = 65536
107
+ } ;
108
+ dir . ino = ino ;
109
+ dir . Dirents . Add ( new PfsDirent { Name = "." , InodeNumber = ino . Number , Type = 4 } ) ;
110
+ dir . Dirents . Add ( new PfsDirent { Name = ".." , InodeNumber = dir . Parent . ino . Number , Type = 5 } ) ;
111
+ dirents . Add ( dir . Dirents ) ;
112
+ var dirent = new PfsDirent { Name = dir . name , InodeNumber = ( uint ) inodes . Count , Type = 3 } ;
113
+ dir . Parent . Dirents . Add ( dirent ) ;
114
+ dir . Parent . ino . Nlink ++ ;
115
+ inodes . Add ( ino ) ;
116
+ }
117
+ }
118
+
119
+ /// <summary>
120
+ /// Adds inodes for each file.
121
+ /// </summary>
122
+ void addFileInodes ( )
123
+ {
124
+ foreach ( var file in allFiles )
125
+ {
126
+ var ino = new PfsDinode32
127
+ {
128
+ Mode = InodeMode . file | InodeMode . rwx ,
129
+ Size = file . Size ,
130
+ SizeCompressed = file . Size ,
131
+ Number = ( uint ) inodes . Count ,
132
+ Blocks = ( uint ) CeilDiv ( file . Size , hdr . BlockSize )
133
+ } ;
134
+ file . ino = ino ;
135
+ var dirent = new PfsDirent { Name = file . name , Type = 2 , InodeNumber = ( uint ) inodes . Count } ;
136
+ file . Parent . Dirents . Add ( dirent ) ;
137
+ inodes . Add ( ino ) ;
138
+ }
139
+ }
140
+
141
+ long dirSizeToSize ( long size ) => CeilDiv ( size , hdr . BlockSize ) * hdr . BlockSize ;
142
+
143
+ /// <summary>
144
+ /// Sets the data blocks. Also updates header for total number of data blocks.
145
+ /// </summary>
146
+ void CalculateDataBlockLayout ( )
147
+ {
148
+ var inodesPerBlock = hdr . BlockSize / PfsDinode32 . SizeOf ;
149
+ hdr . DinodeCount = inodes . Count ;
150
+ hdr . DinodeBlockCount = CeilDiv ( inodes . Count , inodesPerBlock ) ;
151
+ super_root_ino . db [ 0 ] = ( int ) ( hdr . DinodeBlockCount + 1 ) ;
152
+
153
+ // flat path table
154
+ fpt_ino . db [ 0 ] = super_root_ino . db [ 0 ] + 1 ;
155
+ fpt_ino . Size = fpt . Size ;
156
+ fpt_ino . SizeCompressed = fpt . Size ;
157
+ fpt_ino . Blocks = ( uint ) CeilDiv ( fpt . Size , hdr . BlockSize ) ;
158
+ for ( int i = 1 ; i < fpt_ino . Blocks && i < fpt_ino . db . Length ; i ++ )
159
+ fpt_ino . db [ i ] = - 1 ;
160
+
161
+ // All fs entries.
162
+ var currentBlock = fpt_ino . db [ 0 ] + fpt_ino . Blocks ;
163
+ hdr . Ndblock = 0 ;
164
+ // Calculate length of all dirent blocks
165
+ foreach ( var n in allNodes )
166
+ {
167
+ var blocks = CeilDiv ( n . Size , hdr . BlockSize ) ;
168
+ n . ino . db [ 0 ] = ( int ) currentBlock ;
169
+ n . ino . Blocks = ( uint ) blocks ;
170
+ n . ino . Size = n is FSDir ? dirSizeToSize ( n . Size ) : n . Size ;
171
+ n . ino . SizeCompressed = n . ino . Size ;
172
+ for ( int i = 1 ; i < blocks && i < n . ino . db . Length ; i ++ )
173
+ {
174
+ n . ino . db [ i ] = - 1 ;
175
+ }
176
+ currentBlock += blocks ;
177
+ hdr . Ndblock += blocks ;
178
+ }
179
+ }
180
+
181
+ /// <summary>
182
+ /// Creates inodes and dirents for superroot, flat_path_table, and uroot.
183
+ /// Also, creates the root node for the FS tree.
184
+ /// </summary>
185
+ void SetupRootStructure ( )
186
+ {
187
+ inodes . Add ( super_root_ino = new PfsDinode32
188
+ {
189
+ Mode = InodeMode . dir | InodeMode . rx_only ,
190
+ Blocks = 1 ,
191
+ Size = 65536 ,
192
+ SizeCompressed = 65536 ,
193
+ Nlink = 1 ,
194
+ Number = 0 ,
195
+ Flags = InodeFlags . @internal
196
+ } ) ;
197
+ inodes . Add ( fpt_ino = new PfsDinode32
198
+ {
199
+ Mode = InodeMode . file | InodeMode . rwx ,
200
+ Blocks = 1 ,
201
+ Number = 1 ,
202
+ Flags = InodeFlags . @internal
203
+ } ) ;
204
+ var uroot_ino = new PfsDinode32 { Mode = InodeMode . dir | InodeMode . rwx , Number = 2 , Size = 65536 , SizeCompressed = 65536 , Blocks = 1 } ;
205
+
206
+ super_root_dirents = new List < PfsDirent >
207
+ {
208
+ new PfsDirent { InodeNumber = 1 , Name = "flat_path_table" , Type = 2 } ,
209
+ new PfsDirent { InodeNumber = 2 , Name = "uroot" , Type = 3 }
210
+ } ;
211
+
212
+ root = new FSDir
213
+ {
214
+ name = "uroot" ,
215
+ ino = uroot_ino ,
216
+ Dirents = new List < PfsDirent >
217
+ {
218
+ new PfsDirent { Name = "." , Type = 4 , InodeNumber = 2 } ,
219
+ new PfsDirent { Name = ".." , Type = 5 , InodeNumber = 2 }
220
+ }
221
+ } ;
222
+ }
223
+
224
+ /// <summary>
225
+ /// Takes a directory and a root node, and recursively makes a filesystem tree.
226
+ /// </summary>
227
+ /// <param name="root"></param>
228
+ /// <param name="rootDir"></param>
229
+ void BuildFSTree ( FSDir root , string rootDir )
230
+ {
231
+ foreach ( var d in Directory . EnumerateDirectories ( rootDir ) )
232
+ {
233
+ FSDir dir ;
234
+ root . Dirs . Add ( dir = new FSDir { name = Path . GetFileName ( d ) , Parent = root } ) ;
235
+ BuildFSTree ( dir , d ) ;
236
+ }
237
+
238
+ foreach ( var f in Directory . EnumerateFiles ( rootDir ) )
239
+ {
240
+ root . Files . Add ( new FSFile
241
+ {
242
+ Parent = root ,
243
+ name = Path . GetFileName ( f ) ,
244
+ OrigFileName = f ,
245
+ Size = new FileInfo ( f ) . Length
246
+ } ) ;
247
+ }
248
+ }
249
+
250
+ /// <summary>
251
+ /// Writes all the inodes to the image file.
252
+ /// </summary>
253
+ /// <param name="s"></param>
254
+ void WriteInodes ( Stream s )
255
+ {
256
+ s . Position = hdr . BlockSize ;
257
+ foreach ( var di in inodes )
258
+ {
259
+ di . WriteToStream ( s ) ;
260
+ if ( s . Position % hdr . BlockSize > hdr . BlockSize - PfsDinode32 . SizeOf )
261
+ {
262
+ s . Position += hdr . BlockSize - ( s . Position % hdr . BlockSize ) ;
263
+ }
264
+ }
265
+ }
266
+
267
+ /// <summary>
268
+ /// Writes the dirents for the superroot, which precede the flat_path_table.
269
+ /// </summary>
270
+ /// <param name="stream"></param>
271
+ void WriteSuperrootDirents ( Stream stream )
272
+ {
273
+ stream . Position = hdr . BlockSize * ( hdr . DinodeBlockCount + 1 ) ;
274
+ foreach ( var d in super_root_dirents )
275
+ {
276
+ d . WriteToStream ( stream ) ;
277
+ }
278
+ }
279
+
280
+ /// <summary>
281
+ /// Writes all the data blocks.
282
+ /// </summary>
283
+ /// <param name="s"></param>
284
+ void WriteFSNode ( Stream s , FSNode f )
285
+ {
286
+ if ( f is FSDir )
287
+ {
288
+ var dir = ( FSDir ) f ;
289
+ var startBlock = f . ino . db [ 0 ] ;
290
+ foreach ( var d in dir . Dirents )
291
+ {
292
+ d . WriteToStream ( s ) ;
293
+ if ( s . Position % hdr . BlockSize > hdr . BlockSize - PfsDirent . MaxSize )
294
+ {
295
+ s . Position = ( ++ startBlock * hdr . BlockSize ) ;
296
+ }
297
+ }
298
+ }
299
+ else if ( f is FSFile )
300
+ {
301
+ var file = ( FSFile ) f ;
302
+ using ( var fileStream = File . OpenRead ( file . OrigFileName ) )
303
+ fileStream . CopyTo ( s ) ;
304
+ }
305
+ }
306
+ }
307
+ }
0 commit comments