forked from tihmstar/orbisFSTool
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathOrbisFSImage.cpp
More file actions
353 lines (303 loc) · 12.8 KB
/
OrbisFSImage.cpp
File metadata and controls
353 lines (303 loc) · 12.8 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
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
//
// OrbisFSImage.cpp
// orbisFSTool
//
// Created by tihmstar on 16.12.25.
//
#include "OrbisFSImage.hpp"
#include "utils.hpp"
#include <libgeneral/macros.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#ifdef HAVE_SYS_DISK_H
# include <sys/disk.h>
#endif //HAVE_SYS_DISK_H
#ifdef HAVE_LINUX_FS_H
# include <linux/fs.h>
#endif //HAVE_LINUX_FS_H
#define BLOCK_SIZE 0x10000
#define ARRAYOF(a) (sizeof(a)/sizeof(*a))
using namespace orbisFSTool;
#pragma mark helper
#pragma mark OrbisFSImage
OrbisFSImage::OrbisFSImage(const char *path, bool writeable, uint64_t offset)
: _writeable(writeable)
, _fd(-1)
, _mem(NULL), _memsize(0)
, _superblock(NULL), _diskinfoblock(NULL)
, _blockAllocator(nullptr)
, _inodeDir(nullptr)
, _references(0)
{
#ifndef DEBUG
retassure(!_writeable, "Experimental write support is only available in DEBUG builds!");
#endif
retassure((_fd = open(path, writeable ? O_RDWR : O_RDONLY)) != -1, "Failed to open=%s",path);
{
struct stat st = {};
retassure(!fstat(_fd, &st), "Failed to stat file");
if (S_ISBLK(st.st_mode)){
uint64_t count = 0;
uint64_t bsize = 0;
{
/*
macOS
*/
#ifdef DKIOCGETBLOCKCOUNT
retassure(!ioctl(_fd, DKIOCGETBLOCKCOUNT, &count), "Failed to get blk count");
#endif //DKIOCGETBLOCKCOUNT
#ifdef DKIOCGETBLOCKSIZE
retassure(!ioctl(_fd, DKIOCGETBLOCKSIZE, &bsize), "Failed to get blk size");
#endif //DKIOCGETBLOCKSIZE
debug("Got blkcnt=0x%llx",count);
debug("Got blksize=0x%llx",bsize);
_memsize = count * bsize;
}
{
/*
Linux
*/
uint64_t devsize = 0;
#ifdef BLKGETSIZE64
retassure(!ioctl(_fd, BLKGETSIZE64, &devsize), "Failed to get devsize size");
#endif //BLKGETSIZE64
if (!_memsize) _memsize = devsize;
}
}else{
_memsize = st.st_size;
}
}
retassure(_memsize, "Failed to detect image size!");
retassure(_memsize > offset, "offset beyond image size");
_memsize -= offset;
if ((_mem = (uint8_t*)mmap(NULL, _memsize, PROT_READ | PROT_WRITE, MAP_FILE | (writeable ? MAP_SHARED : MAP_PRIVATE), _fd, offset)) == MAP_FAILED){
reterror("Failed to mmap '%s' errno=%d (%s)",path,errno,strerror(errno));
}
init();
}
OrbisFSImage::~OrbisFSImage(){
/*
Close this before other files, because it might contain an open file
*/
safeDelete(_inodeDir);
{
std::unique_lock<std::mutex> ul(_referencesLck);
while (_references) {
debug("Waiting for open files to close, %d remaining",_references);
uint64_t wevent = _unrefEvent.getNextEvent();
ul.unlock();
_unrefEvent.waitForEvent(wevent);
ul.lock();
}
}
safeDelete(_blockAllocator);
if (_mem){
munmap(_mem, _memsize); _mem = NULL;
}
safeClose(_fd);
}
#pragma mark OrbisFSImage private
void OrbisFSImage::init(){
/*
Init Superblock
*/
_superblock = (OrbisFSSuperblock_t*)getBlock(0);
printf("Superblock:\n");
printf("\tmagic : 0x%llx\n",_superblock->magic);
printf("\tunk0 : 0x%llx\n",_superblock->unk0);
printf("\treserve : '%.*s'\n",(int)sizeof(_superblock->reserve),_superblock->reserve);
printf("\tversion : 0x%llx\n",_superblock->version);
printf("\tunk2 : 0x%llx\n",_superblock->unk2);
printf("\tblockAllocatorLnk : type: 0x%02x blk: %d\n",_superblock->blockAllocatorLnk.type,_superblock->blockAllocatorLnk.blk);
printf("\tunk4 : 0x%08x\n",_superblock->unk4);
printf("\tunk5 : 0x%08x\n",_superblock->unk5);
printf("\tdiskinfoLnk : type: 0x%02x blk: %d\n",_superblock->diskinfoLnk.type,_superblock->diskinfoLnk.blk);
retassure(_superblock->magic == ORBIS_FS_SUPERBLOCK_MAGIC, "Bad superblock magic");
retassure(memvcmp(_superblock->_pad1, sizeof(_superblock->_pad1), 0x00), "_pad1 is not zero");
retassure(!strcmp(_superblock->reserve, ORBIS_FS_SUPERBLOCK_RESERVE_STR), "unexpected reserve value '%.*s'",sizeof(_superblock->reserve),_superblock->reserve);
retassure(memvcmp(_superblock->_pad2, sizeof(_superblock->_pad2), 0x00), "_pad2 is not zero");
retassure(_superblock->version == ORBIS_FS_SUPERBLOCK_VERSION, "Unexpected superblock version!");
retassure(memvcmp(_superblock+1, BLOCK_SIZE-sizeof(*_superblock), 0x00), "Bytes beyong superblock are not zero");
retassure(_superblock->blockAllocatorLnk.type == ORBIS_FS_CHAINLINK_TYPE_LINK, "Unexpected blockAllocatorLnk.type");
retassure(_superblock->diskinfoLnk.type == ORBIS_FS_CHAINLINK_TYPE_LINK, "Unexpected diskinfoLnk.type");
/*
Init BlockAllocator
*/
_blockAllocator = new OrbisFSBlockAllocator(this, _superblock->blockAllocatorLnk.blk);
{
uint64_t totalBlocks = _blockAllocator->getTotalBlockNum();
uint64_t totalSize = totalBlocks * BLOCK_SIZE;
printf("\ttotalFSBlocks : 0x%llx\n",totalBlocks);
{
const char *unit = "B";
float s = 0;
if (totalSize >= 1e12) {
s = totalSize / 1e12;
unit = "TB";
}else if (totalSize >= 1e9) {
s = totalSize / 1e9;
unit = "GB";
}else if (totalSize >= 1e6) {
s = totalSize / 1e6;
unit = "MB";
}else if (totalSize >= 1e3) {
s = totalSize / 1e3;
unit = "KB";
}
printf("\ttotalFSSize : 0x%llx (%.2f %s)\n",totalSize,s,unit);
}
retassure(totalSize <= _memsize, "FS claims to use more block than the image has");
uint64_t freeBlocks = _blockAllocator->getFreeBlocksNum();
printf("\tfreeBlocks : 0x%llx\n",freeBlocks);
}
/*
Init DiskinfoBlock
*/
_diskinfoblock = (OrbisFSDiskinfoblock_t*)getBlock(_superblock->diskinfoLnk.blk);
printf("Diskinfoblock:\n");
printf("\tmagic : 0x%llx\n",_diskinfoblock->magic);
printf("\tunk1 : 0x%llx\n",_diskinfoblock->unk1_is_2);
printf("\tunk2 : 0x%llx\n",_diskinfoblock->unk2_is_0x40);
printf("\tunk3 : 0x%llx\n",_diskinfoblock->unk3_is_0);
printf("\tdevpath : '%.*s'\n",(int)sizeof(_diskinfoblock->devpath),_diskinfoblock->devpath);
printf("\tinodesInRootFolder: 0x%x (%d)\n",_diskinfoblock->inodesInRootFolder,_diskinfoblock->inodesInRootFolder);
printf("\trdev : 0x%x\n",_diskinfoblock->rdev_is_0xffffffff);
printf("\thighestUsedInode : 0x%x (%d)\n",_diskinfoblock->highestUsedInode,_diskinfoblock->highestUsedInode);
printf("\tblocksUsed : 0x%llx\n",_diskinfoblock->blocksUsed);
printf("\tblocksAvailable : 0x%llx\n",_diskinfoblock->blocksAvailable);
#ifdef DEBUG
printf("\tunk7:\n");
DumpHex(_diskinfoblock->unk7,sizeof(_diskinfoblock->unk7));
#endif
printf("\tinodedirLnk : type: 0x%02x blk: %d\n",_diskinfoblock->inodedirLnk.type,_diskinfoblock->inodedirLnk.blk);
printf("\tdiskinfoLnk : type: 0x%02x blk: %d\n",_diskinfoblock->diskinfoLnk.type,_diskinfoblock->diskinfoLnk.blk);
retassure(_diskinfoblock->magic == ORBIS_FS_DISKINFOBLOCK_MAGIC, "Bad diskinfoblock magic");
retassure(_diskinfoblock->unk1_is_2 == 2, "unexpected value for unk1");
retassure(_diskinfoblock->unk2_is_0x40 == 0x40, "unexpected value for unk2");
retassure(_diskinfoblock->unk3_is_0 == 0, "unexpected value for unk3");
retassure(_diskinfoblock->rdev_is_0xffffffff == 0xFFFFFFFF, "unexpected value for rdev_is_0xffffffff");
retassure(memvcmp(_diskinfoblock->_pad2, sizeof(_diskinfoblock->_pad2), 0x00), "_pad2 is not zero");
retassure(!memcmp(&_diskinfoblock->diskinfoLnk, &_superblock->diskinfoLnk, sizeof(_superblock->diskinfoLnk)), "diskinfoLnk mismatch between superblock and diskinfoblock");
retassure(_diskinfoblock->inodedirLnk.type == ORBIS_FS_CHAINLINK_TYPE_LINK, "Unexpected inodedirLnk.type");
/*
Init inode root dir block
*/
_inodeDir = new OrbisFSInodeDirectory(this, _diskinfoblock->inodedirLnk.blk);
}
uint8_t *OrbisFSImage::getBlock(uint32_t blknum){
size_t offset = (size_t)blknum * BLOCK_SIZE;
retassure(offset+BLOCK_SIZE <= _memsize, "trying to access out of bounds block");
return &_mem[offset];
}
std::shared_ptr<OrbisFSFile> OrbisFSImage::openFileNode(OrbisFSInode_t *node, bool noFilemodeChecks){
return std::make_shared<OrbisFSFile>(this, node, noFilemodeChecks);
}
bool OrbisFSImage::checkBlockAllocations(){
OrbisFSBlockAllocator va(this, _superblock->blockAllocatorLnk.blk, true);
va.freeBlock(_superblock->blockAllocatorLnk.blk);
va.freeBlock(_superblock->diskinfoLnk.blk);
{
OrbisFSAllocatorInfoElem_t *aie = (OrbisFSAllocatorInfoElem_t*)getBlock(_superblock->blockAllocatorLnk.blk);
uint32_t elemsCnt = getBlocksize() / sizeof(*aie);
for (int i=0; i<elemsCnt; i++) {
if (aie[i].bitmapBlk.type != ORBIS_FS_CHAINLINK_TYPE_LINK) break;
va.freeBlock(aie[i].bitmapBlk.blk);
}
}
auto fInodes = openFileNode(_inodeDir->findInode(kOrbisFSInodeRootDirID), true);
{
OrbisFSInode_t node = {};
while (fInodes->read(&node, sizeof(node)) == sizeof(node)) {
if (node.magic != ORBIS_FS_INODE_MAGIC) continue;
auto fnode = openFileNode(&node, true);
auto usedBlks = fnode->getAllAllocatedBlocks();
for (auto b : usedBlks) {
va.freeBlock(b);
}
}
}
return va.getFreeBlocksNum()+1 == va.getTotalBlockNum();
}
void OrbisFSImage::freeBlock(uint32_t blk){
_blockAllocator->freeBlock(blk);
_diskinfoblock->blocksUsed--;
}
#pragma mark OrbisFSImage public
bool OrbisFSImage::isWriteable(){
return _writeable;
}
uint32_t OrbisFSImage::getBlocksize(){
return BLOCK_SIZE;
}
std::vector<std::pair<std::string, OrbisFSInode_t>> OrbisFSImage::listFilesInFolder(std::string path, bool includeSelfAndParent){
return _inodeDir->listFilesInDir(_inodeDir->findInodeIDForPath(path), includeSelfAndParent);
}
std::vector<std::pair<std::string, OrbisFSInode_t>> OrbisFSImage::listFilesInFolder(uint32_t inode, bool includeSelfAndParent){
return _inodeDir->listFilesInDir(inode, includeSelfAndParent);
}
OrbisFSInode_t OrbisFSImage::getInodeForID(uint32_t inode){
return *_inodeDir->findInode(inode);
}
OrbisFSInode_t OrbisFSImage::getInodeForPath(std::string path){
return *_inodeDir->findInodeForPath(path);
}
std::shared_ptr<OrbisFSFile> OrbisFSImage::openFileID(uint32_t inode){
return openFileNode(_inodeDir->findInode(inode));
}
std::shared_ptr<OrbisFSFile> OrbisFSImage::openFilAtPath(std::string path){
return openFileNode(_inodeDir->findInodeForPath(path));
}
void OrbisFSImage::iterateOverFilesInFolder(std::string path, bool recursive, std::function<void(std::string path, OrbisFSInode_t node)> callback){
std::vector<std::pair<std::string, OrbisFSInode_t>> files;
std::pair<std::string, OrbisFSInode_t> curpath = {path,{}};
bool scanFolder = true;
curpath.second.inodeNum = _inodeDir->findInodeIDForPath(path);
do{
loopStart:
std::vector<std::pair<std::string, OrbisFSInode_t>> curfiles;
{
auto r = _inodeDir->findInode(curpath.second.inodeNum);
curpath = {curpath.first,*r};
}
if (S_ISDIR(curpath.second.fileMode)) {
if (scanFolder) curfiles = listFilesInFolder(curpath.second.inodeNum);
callback(curpath.first,curpath.second);
}else{
scanFolder = false;
files.push_back(curpath);
}
if (scanFolder) {
for (auto cp = curfiles.rbegin(); cp != curfiles.rend(); ++cp) {
std::string p = curpath.first;
if (p.back() != '/') {
p+='/';
}
p += cp->first;
files.push_back({p,cp->second});
}
scanFolder = recursive;
}
while (files.size()) {
curpath = files.back();
files.pop_back();
if (S_ISDIR(curpath.second.fileMode)) {
goto loopStart; //skip check at the end in case files.size() == 0
}else{
callback(curpath.first,curpath.second);
}
}
}while(files.size());
}
bool OrbisFSImage::check(){
info("Checking block allocations...");
if (!checkBlockAllocations()) goto fail;
success:
return true;
fail:
error("check failed!");
return false;
}