1111#include < string>
1212#include < vector>
1313
14- std::vector<u32 > read_sizes {8 , 16 , 32 , 64 , 128 , 256 , 512 , 1024 , 2048 , 4096 , 8192 , 16384 , 32768 , 65536 , 131072 };
15- std::vector<u16 > read_offsets {0 };
16-
1714const char * clone_source_app0 = " /app0/assets/misc" ;
1815const char * clone_destination_read = " /data/enderman/clone_read" ;
1916const char * clone_destination_getdirentries = " /data/enderman/clone_getdents" ;
@@ -33,7 +30,6 @@ bool PrepareTests() {
3330
3431 RegenerateDir (" /data/enderman" );
3532 sceKernelMkdir (clone_dir.c_str (), 0777 );
36- sceKernelWrite (1 , " !@#!@#!WD" , 8 );
3733
3834 for (auto & dent: fs::directory_iterator (" /app0/assets/misc" )) {
3935 target = clone_dir / dent.path ().filename ();
@@ -49,29 +45,16 @@ bool PrepareTests() {
4945 return false ;
5046 }
5147 return true ;
48+ }
5249
53- // Log("---------------------");
54- // Log("Dump normal directory");
55- // Log("---------------------");
56-
57- // int fd = sceKernelOpen("/data/enderman", O_DIRECTORY | O_RDONLY, 0777);
58- // for (auto read_size: read_sizes) {
59- // for (auto read_offset: read_offsets) {
60- // DumpDirectory(fd, read_size, read_offset);
61- // }
62- // }
63- // sceKernelClose(fd);
64-
65- // Log("------------------");
66- // Log("Dump PFS directory");
67- // Log("------------------");
68- // fd = sceKernelOpen("/app0/assets/misc", O_DIRECTORY | O_RDONLY, 0777);
69- // for (auto read_size: read_sizes) {
70- // for (auto read_offset: read_offsets) {
71- // DumpDirectory(fd, read_size, read_offset, true);
72- // }
73- // }
74- // sceKernelClose(fd);
50+ s64 undump_file (const char * path, char * data, u64 length) {
51+ int fd = sceKernelOpen (path, O_RDONLY, 0777 );
52+ if (fd < 0 ) return fd;
53+ memset (data, ' A' , length);
54+ int tbr = sceKernelRead (fd, data, length);
55+ if (tbr < 0 ) return tbr;
56+ if (auto res = sceKernelClose (fd); res < 0 ) return res;
57+ return tbr;
7558}
7659
7760TEST_GROUP (DirentTests) {
@@ -88,7 +71,10 @@ TEST_GROUP (DirentTests) {
8871 int fd;
8972 s64 tbr;
9073
91- void setup () {}
74+ void setup () {
75+ fd = -1 ;
76+ tbr = 0 ;
77+ }
9278 void teardown () {
9379 sceKernelClose (fd);
9480 fd = -1 ;
@@ -102,21 +88,18 @@ TEST(DirentTests, PFSGetdirentries) {
10288 char buffer[65536 ];
10389 int result_cast {};
10490
105- auto pattern = [](s64 size, s64 offset) -> fs::path { return " pfs_getdirentries_fail_o" + std::to_string (offset) + " _s" + std::to_string (size) + " .bin" ; };
106-
107- RegenerateDir (output_root.c_str ());
108-
10991 fd = sceKernelOpen (input_pfs, O_DIRECTORY, 0777 );
11092 s64 basep {};
11193 for (auto & spec: pfs_dirent_variants) {
11294 basep = 0 ;
11395 memset (buffer, 0xAA , 65536 );
11496 result_cast = int (spec.expected_result );
97+
11598 if (spec.read_offset >= 0 ) CHECK_EQUAL (spec.read_offset , sceKernelLseek (fd, spec.read_offset , 0 ));
11699 errno = 0 ;
117100 tbr = sceKernelGetdirentries (fd, buffer, spec.read_size , &basep);
118101 LogTest (spec.read_size , spec.read_offset , spec.expected_basep , result_cast, spec.expected_errno , " \t ->\t " , basep, tbr, errno, " \t " ,
119- to_hex_string (buffer,16 ," " ));
102+ to_hex_string (buffer, 16 , " " ));
120103 if (tbr < 0 ) {
121104 CHECK_EQUAL (result_cast, tbr);
122105 } else {
@@ -132,25 +115,21 @@ TEST(DirentTests, PFSGetdirentries) {
132115TEST (DirentTests, NormalGetdirentries) {
133116 LogTest (" <<<< Normal getdirentries tests >>>>" );
134117
135- fs::path output_root = " /data/enderman/normal_getdirentries" ;
136- char buffer[65536 ];
137- int result_cast {};
138-
139- auto pattern = [](s64 size, s64 offset) -> fs::path { return " normal_getdirentries_fail_o" + std::to_string (offset) + " _s" + std::to_string (size) + " .bin" ; };
140-
141- RegenerateDir (output_root.c_str ());
118+ char buffer[65536 ];
119+ int result_cast {};
142120
143121 fd = sceKernelOpen (input_normal, O_DIRECTORY, 0777 );
144122 s64 basep {};
145123 for (auto & spec: normal_dirent_variants) {
146124 basep = 0 ;
147125 memset (buffer, 0xAA , 65536 );
148126 result_cast = int (spec.expected_result );
127+
149128 if (spec.read_offset >= 0 ) CHECK_EQUAL (spec.read_offset , sceKernelLseek (fd, spec.read_offset , 0 ));
150129 errno = 0 ;
151130 tbr = sceKernelGetdirentries (fd, buffer, spec.read_size , &basep);
152131 LogTest (spec.read_size , spec.read_offset , spec.expected_basep , result_cast, spec.expected_errno , " \t ->\t " , basep, tbr, errno,
153- to_hex_string (buffer,16 ," " ));
132+ to_hex_string (buffer, 16 , " " ));
154133 if (tbr < 0 ) {
155134 CHECK_EQUAL (result_cast, tbr);
156135 } else {
@@ -163,56 +142,112 @@ TEST(DirentTests, NormalGetdirentries) {
163142 sceKernelClose (fd);
164143}
165144
166- s64 qqq (const void * master, const void * test, s64 buffer_size, s64 tbr, struct oi ::DirentCombinationRead* spec) {
145+ s64 compare_data_dump (const void * master, const void * test, s64 buffer_size, s64 tbr, struct oi ::DirentCombinationRead* spec) {
167146 const char * master_data = reinterpret_cast <const char *>(master) + spec->read_offset ;
168147 const char * test_data = reinterpret_cast <const char *>(test);
169148
170149 if (auto qw = imemcmp (master_data, test_data, tbr); qw != -1 ) {
171- LogError (" Incorrect read at" , spec->read_offset + qw);
172- LogError (" Global dump:" , to_hex_string (master_data + qw, std::min ((s64)32 , tbr)));
173- LogError (" Recent dump:" , to_hex_string (test_data + qw, std::min ((s64)32 , tbr)));
150+ LogError (" Inconsistent read at" , spec->read_offset + qw);
151+ LogError (" Global dump:" , to_hex_string (master_data + qw, std::min ((s64)48 , tbr)));
152+ LogError (" Recent dump:" , to_hex_string (test_data + qw, std::min ((s64)48 , tbr)));
174153
175154 return -1 ;
176155 }
177156 return 0 ;
178157}
179158
159+ TEST (DirentTests, PFSRead) {
160+ LogTest (" <<<< PFS read tests >>>>" );
161+
162+ char buffer[65536 ];
163+ char master_buffer[65536 ];
164+ const s64 master_length = undump_file (output_pfs_read, master_buffer, 65536 );
165+ s64 expected_length_adj {};
166+
167+ LogTest (" Master PFS read length is" , master_length);
168+
169+ fd = sceKernelOpen (input_pfs, O_DIRECTORY, 0777 );
170+ for (auto & spec: normal_read_variants) {
171+ memset (buffer, ' A' , 65536 );
172+
173+ CHECK_EQUAL (spec.read_offset , sceKernelLseek (fd, spec.read_offset , 0 ));
174+ errno = 0 ;
175+ tbr = sceKernelRead (fd, buffer, spec.read_size );
176+
177+ expected_length_adj = (master_length - spec.read_offset ) > 0 ? master_length - spec.read_offset : 0 ;
178+ expected_length_adj = std::min (expected_length_adj, spec.read_size );
179+
180+ LogTest (spec.read_size , spec.read_offset , int (spec.expected_result ), spec.expected_errno , " \t ->\t " , tbr, errno, expected_length_adj, " \t " ,
181+ to_hex_string (buffer, 16 , " " ));
182+ CHECK_EQUAL (expected_length_adj, tbr);
183+ CHECK_EQUAL (spec.expected_errno , errno);
184+ compare_data_dump (master_buffer, buffer, 65536 , tbr, &spec);
185+ // dump good ones to file
186+ }
187+ sceKernelClose (fd);
188+ }
189+
180190TEST (DirentTests, NormalRead) {
181191 LogTest (" <<<< Normal read tests >>>>" );
182192
183- fs::path output_root = " /data/enderman/normal_read" ;
184- char buffer[65536 ];
185- int result_cast {};
186-
187- auto pattern = [](s64 size, s64 offset) -> fs::path { return " normal_read_fail_o" + std::to_string (offset) + " _s" + std::to_string (size) + " .bin" ; };
193+ char buffer[65536 ];
194+ char master_buffer[65536 ];
195+ const s64 master_length = undump_file (output_normal_read, master_buffer, 65536 );
196+ s64 expected_length_adj {};
188197
189- RegenerateDir (output_root. c_str () );
198+ LogTest ( " Master PFS read length is " , master_length );
190199
191200 fd = sceKernelOpen (input_normal, O_DIRECTORY, 0777 );
192201 for (auto & spec: normal_read_variants) {
193- result_cast = int (spec.expected_result );
194202 memset (buffer, ' A' , 65536 );
195203
196204 CHECK_EQUAL (spec.read_offset , sceKernelLseek (fd, spec.read_offset , 0 ));
197205 errno = 0 ;
198206 tbr = sceKernelRead (fd, buffer, spec.read_size );
199207
200- LogTest (spec.read_size , spec.read_offset , result_cast, spec.expected_errno , " \t ->\t " , tbr, errno, " \t " , to_hex_string (buffer,16 ," " ));
208+ expected_length_adj = (master_length - spec.read_offset ) > 0 ? master_length - spec.read_offset : 0 ;
209+ expected_length_adj = std::min (expected_length_adj, spec.read_size );
201210
202- {
203- int f = sceKernelOpen (output_normal_read, O_RDONLY, 0777 );
204- char b[65536 ] {' A' };
205- sceKernelRead (f, b, 65536 );
206- sceKernelClose (f);
207- qqq (b, buffer, 65536 , tbr, &spec);
208- }
209- CHECK_EQUAL (spec.expected_result , tbr);
211+ LogTest (spec.read_size , spec.read_offset , int (spec.expected_result ), spec.expected_errno , " \t ->\t " , tbr, errno, expected_length_adj, " \t " ,
212+ to_hex_string (buffer, 16 , " " ));
213+ CHECK_EQUAL (expected_length_adj, tbr);
210214 CHECK_EQUAL (spec.expected_errno , errno);
215+ compare_data_dump (master_buffer, buffer, 65536 , tbr, &spec);
211216 // dump good ones to file
212217 }
213218 sceKernelClose (fd);
214219}
215220
221+ TEST (DirentTests, NormalGetdirentriesErrors) {
222+ LogTest (" <<<< Normal getdirentries test illegal reads >>>>" );
223+ LogTest (" Test reads that do not break 512b alignment" );
224+
225+ char buffer[1024 ];
226+ int result_cast {};
227+ s64 spec_read_size {};
228+ s64 spec_offset_base {};
229+ s64 spec_offset {};
230+ s64 basep {};
231+ memset (buffer, 0xAA , 1024 );
232+
233+ fd = sceKernelOpen (input_normal, O_DIRECTORY, 0777 );
234+
235+ for (spec_offset_base = 0 ; spec_offset_base < 65536 ; spec_offset_base += 512 ) {
236+ for (spec_read_size = 0 ; spec_read_size < 512 ; ++spec_read_size) {
237+ basep = 0 ;
238+ spec_offset = spec_offset_base + 511 - spec_read_size;
239+ CHECK_EQUAL (spec_offset, sceKernelLseek (fd, spec_offset, 0 ));
240+ errno = 0 ;
241+ tbr = sceKernelGetdirentries (fd, buffer, spec_read_size, &basep);
242+ CHECK_EQUAL (0xAAAAAAAAAAAAAAAA , *reinterpret_cast <u64 *>(buffer));
243+ CHECK_EQUAL (ORBIS_KERNEL_ERROR_EINVAL, int (tbr));
244+ CHECK_EQUAL (EINVAL, errno);
245+ CHECK_EQUAL (0 , basep);
246+ }
247+ }
248+ sceKernelClose (fd);
249+ }
250+
216251TEST (DirentTests, PFSGetdirentriesErrors) {
217252 LogTest (" <<<< PFS getdirentries test illegal reads >>>>" );
218253 LogTest (" Test reads that do not break 512b alignment" );
@@ -243,6 +278,75 @@ TEST(DirentTests, PFSGetdirentriesErrors) {
243278 sceKernelClose (fd);
244279}
245280
281+ s64 NormalComparator (const char * read, const char * getdirentries, u64 length) {
282+ s64 offset {0 };
283+ while (offset < length) {
284+ const oi::FolderDirent* dirent_read = reinterpret_cast <const oi::FolderDirent*>(read + offset);
285+ const oi::FolderDirent* dirent_getdirentries = reinterpret_cast <const oi::FolderDirent*>(getdirentries + offset);
286+
287+ if (dirent_read->d_namlen != dirent_getdirentries->d_namlen ) break ;
288+ if (dirent_read->d_reclen != dirent_getdirentries->d_reclen ) break ;
289+ if (dirent_read->d_type != dirent_getdirentries->d_type ) break ;
290+ if (memcmp (dirent_read->d_name , dirent_getdirentries->d_name , dirent_read->d_namlen )) break ; // namlen is the same
291+ offset += dirent_read->d_reclen ;
292+ if (dirent_read->d_reclen == 0 ) break ;
293+ }
294+ return offset;
295+ }
296+
297+ TEST (DirentTests, Normal_Consistency) {
298+ LogTest (" <<<< Normal read and getdirentries consistency >>>>" );
299+
300+ char master_read[65536 ] {' A' };
301+ char master_getdirentries[65536 ] {' A' };
302+
303+ auto master_read_size = undump_file (output_normal_read, master_read, 65536 );
304+ auto master_getdirentries_size = undump_file (output_normal_getdirentries, master_getdirentries, 65536 );
305+
306+ if (auto res = NormalComparator (master_read, master_getdirentries, master_read_size); master_getdirentries_size != res) {
307+ LogError (" Normal read and getdirentries have a different dirent at" , res, " :" );
308+ LogError (" Normal Read:\t " , to_hex_string (master_read + res, 24 ));
309+ LogError (" Normal Getdirentries: " , to_hex_string (master_read + res, 24 ));
310+ FAIL (" Normal read and getdirentries returned different amount of data" );
311+ }
312+ }
313+
314+ s64 PFSComparator (const char * read, const char * getdirentries, u64 length) {
315+
316+ s64 offset {0 };
317+ while (offset < length) {
318+ const oi::PfsDirent* dirent_read = reinterpret_cast <const oi::PfsDirent*>(read + offset);
319+ const oi::FolderDirent* dirent_getdirentries = reinterpret_cast <const oi::FolderDirent*>(getdirentries + offset);
320+
321+ if (dirent_read->d_namlen != dirent_getdirentries->d_namlen ) break ;
322+ if (dirent_read->d_reclen != dirent_getdirentries->d_reclen ) break ;
323+ // if (dirent_read->d_type != dirent_getdirentries->d_type) break;
324+ if (memcmp (dirent_read->d_name , dirent_getdirentries->d_name , dirent_read->d_namlen )) break ; // namlen is the same
325+ if (dirent_read->d_reclen == 0 ) break ; // reclen is the same
326+ offset += dirent_read->d_reclen ;
327+ if (dirent_read->d_reclen == 0 ) break ;
328+ }
329+ return offset;
330+ }
331+
332+ TEST (DirentTests, PFS_Consistency) {
333+ LogTest (" <<<< PFS read and getdirentries consistency >>>>" );
334+ LogWarning (" This function does not translate d_type between PFS and user filesystems" );
335+
336+ char master_read[65536 ] {' A' };
337+ char master_getdirentries[65536 ] {' A' };
338+
339+ auto master_read_size = undump_file (output_pfs_read, master_read, 65536 );
340+ auto master_getdirentries_size = undump_file (output_pfs_getdirentries, master_getdirentries, 65536 );
341+
342+ if (auto res = PFSComparator (master_read, master_getdirentries, 65536 ); master_getdirentries_size != res) {
343+ LogError (" PFS read and getdirentries have a different dirent at" , res, " :" );
344+ LogError (" PFS Read:\t " , to_hex_string (master_read + res, 24 ));
345+ LogError (" PFS Getdirentries: " , to_hex_string (master_read + res, 24 ));
346+ FAIL (" PFS read and getdirentries returned different amount of data" );
347+ }
348+ }
349+
246350TEST (DirentTests, DumpEverythingRaw) {
247351 LogTest (" <<<< Dump everything >>>>" );
248352
@@ -265,8 +369,10 @@ TEST(DirentTests, DumpEverythingRaw) {
265369 memset (buffer, 0xAA , 65536 );
266370 tbr = sceKernelRead (fd_read, buffer, 65536 );
267371 CHECK_COMPARE_TEXT (tbr, >=, 0 , " PFS read failed" );
372+ LogTest (" PFS read got" , tbr, " bytes" );
268373 if (tbr == 0 ) break ;
269- CHECK_EQUAL (65536 , sceKernelWrite (fd_dump, buffer, 65536 ));
374+ CHECK_EQUAL_TEXT (65536 , tbr, " Incorrect read size" ); // not ready for multiples of buffer size
375+ CHECK_EQUAL (tbr, sceKernelWrite (fd_dump, buffer, tbr));
270376 } while (tbr);
271377 CHECK_EQUAL_ZERO (sceKernelClose (fd_dump));
272378
@@ -279,8 +385,10 @@ TEST(DirentTests, DumpEverythingRaw) {
279385 memset (buffer, 0xAA , 65536 );
280386 tbr = sceKernelGetdirentries (fd_read, buffer, 65536 , nullptr );
281387 CHECK_COMPARE_TEXT (tbr, >=, 0 , " PFS sceKernelGetdirentries failed" );
388+ LogTest (" PFS sceKernelGetdirentries got" , tbr, " bytes" );
282389 if (tbr == 0 ) break ;
283- CHECK_EQUAL (65536 , sceKernelWrite (fd_dump, buffer, 65536 ));
390+ CHECK_EQUAL_TEXT (10616 , tbr, " Incorrect read size" ); // not ready for multiples of buffer size
391+ CHECK_EQUAL (tbr, sceKernelWrite (fd_dump, buffer, tbr));
284392 } while (tbr);
285393 CHECK_EQUAL_ZERO (sceKernelClose (fd_dump));
286394
@@ -297,9 +405,11 @@ TEST(DirentTests, DumpEverythingRaw) {
297405 do {
298406 memset (buffer, 0xAA , 65536 );
299407 tbr = sceKernelRead (fd_read, buffer, 65536 );
300- CHECK_COMPARE_TEXT (tbr, >=, 0 , " Normal sceKernelRead failed" );
408+ CHECK_COMPARE_TEXT (tbr, >=, 0 , " Normal read failed" );
409+ LogTest (" Normal read got" , tbr, " bytes" );
301410 if (tbr == 0 ) break ;
302- CHECK_EQUAL (65536 , sceKernelWrite (fd_dump, buffer, 65536 ));
411+ CHECK_EQUAL_TEXT (8704 , tbr, " Incorrect read size" ); // not ready for multiples of buffer size
412+ CHECK_EQUAL (tbr, sceKernelWrite (fd_dump, buffer, tbr));
303413 } while (tbr);
304414 CHECK_EQUAL_ZERO (sceKernelClose (fd_dump));
305415
@@ -311,8 +421,10 @@ TEST(DirentTests, DumpEverythingRaw) {
311421 memset (buffer, 0xAA , 65536 );
312422 tbr = sceKernelGetdirentries (fd_read, buffer, 65536 , nullptr );
313423 CHECK_COMPARE_TEXT (tbr, >=, 0 , " Normal sceKernelGetdirentries failed" );
424+ LogTest (" Normal sceKernelGetdirentries got" , tbr, " bytes" );
314425 if (tbr == 0 ) break ;
315- CHECK_EQUAL (65536 , sceKernelWrite (fd_dump, buffer, 65536 ));
426+ CHECK_EQUAL_TEXT (8704 , tbr, " Incorrect read size" ); // not ready for multiples of buffer size
427+ CHECK_EQUAL (tbr, sceKernelWrite (fd_dump, buffer, tbr));
316428 } while (tbr);
317429 CHECK_EQUAL_ZERO (sceKernelClose (fd_dump));
318430
0 commit comments