Skip to content

Commit 049ff68

Browse files
committed
Add external path and type whitelist to ResourceLoader
1 parent 71d80b2 commit 049ff68

23 files changed

+494
-52
lines changed

core/core_bind.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ Error ResourceLoader::load_threaded_request(const String &p_path, const String &
5252
return ::ResourceLoader::load_threaded_request(p_path, p_type_hint, p_use_sub_threads, ResourceFormatLoader::CacheMode(p_cache_mode));
5353
}
5454

55+
Error ResourceLoader::load_threaded_request_whitelisted(const String &p_path, Dictionary p_external_path_whitelist, Dictionary p_type_whitelist, const String &p_type_hint, bool p_use_sub_threads, CacheMode p_cache_mode) {
56+
return ::ResourceLoader::load_threaded_request_whitelisted(p_path, p_external_path_whitelist, p_type_whitelist, p_type_hint, p_use_sub_threads, ResourceFormatLoader::CacheMode(p_cache_mode));
57+
}
58+
5559
ResourceLoader::ThreadLoadStatus ResourceLoader::load_threaded_get_status(const String &p_path, Array r_progress) {
5660
float progress = 0;
5761
::ResourceLoader::ThreadLoadStatus tls = ::ResourceLoader::load_threaded_get_status(p_path, &progress);
@@ -77,6 +81,14 @@ Ref<Resource> ResourceLoader::load(const String &p_path, const String &p_type_hi
7781
return ret;
7882
}
7983

84+
Ref<Resource> ResourceLoader::load_whitelisted(const String &p_path, Dictionary p_external_path_whitelist, Dictionary p_type_whitelist, const String &p_type_hint, CacheMode p_cache_mode) {
85+
Error err = OK;
86+
Ref<Resource> ret = ::ResourceLoader::load_whitelisted(p_path, p_external_path_whitelist, p_type_whitelist, p_type_hint, ResourceFormatLoader::CacheMode(p_cache_mode), &err);
87+
88+
ERR_FAIL_COND_V_MSG(err != OK, ret, "Error loading resource: '" + p_path + "'.");
89+
return ret;
90+
}
91+
8092
Vector<String> ResourceLoader::get_recognized_extensions_for_type(const String &p_type) {
8193
List<String> exts;
8294
::ResourceLoader::get_recognized_extensions_for_type(p_type, &exts);
@@ -136,10 +148,12 @@ Vector<String> ResourceLoader::list_directory(const String &p_directory) {
136148

137149
void ResourceLoader::_bind_methods() {
138150
ClassDB::bind_method(D_METHOD("load_threaded_request", "path", "type_hint", "use_sub_threads", "cache_mode"), &ResourceLoader::load_threaded_request, DEFVAL(""), DEFVAL(false), DEFVAL(CACHE_MODE_REUSE));
151+
ClassDB::bind_method(D_METHOD("load_threaded_request_whitelisted", "path", "external_path_whitelist", "type_whitelist", "type_hint", "use_sub_threads", "cache_mode"), &ResourceLoader::load_threaded_request_whitelisted, DEFVAL(""), DEFVAL(false), DEFVAL(CACHE_MODE_REUSE));
139152
ClassDB::bind_method(D_METHOD("load_threaded_get_status", "path", "progress"), &ResourceLoader::load_threaded_get_status, DEFVAL_ARRAY);
140153
ClassDB::bind_method(D_METHOD("load_threaded_get", "path"), &ResourceLoader::load_threaded_get);
141154

142155
ClassDB::bind_method(D_METHOD("load", "path", "type_hint", "cache_mode"), &ResourceLoader::load, DEFVAL(""), DEFVAL(CACHE_MODE_REUSE));
156+
ClassDB::bind_method(D_METHOD("load_whitelisted", "path", "external_path_whitelist", "type_whitelist", "type_hint", "cache_mode"), &ResourceLoader::load_whitelisted, DEFVAL(""), DEFVAL(CACHE_MODE_REUSE));
143157
ClassDB::bind_method(D_METHOD("get_recognized_extensions_for_type", "type"), &ResourceLoader::get_recognized_extensions_for_type);
144158
ClassDB::bind_method(D_METHOD("add_resource_format_loader", "format_loader", "at_front"), &ResourceLoader::add_resource_format_loader, DEFVAL(false));
145159
ClassDB::bind_method(D_METHOD("remove_resource_format_loader", "format_loader"), &ResourceLoader::remove_resource_format_loader);

core/core_bind.h

+2
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,12 @@ class ResourceLoader : public Object {
7070
static ResourceLoader *get_singleton() { return singleton; }
7171

7272
Error load_threaded_request(const String &p_path, const String &p_type_hint = "", bool p_use_sub_threads = false, CacheMode p_cache_mode = CACHE_MODE_REUSE);
73+
Error load_threaded_request_whitelisted(const String &p_path, Dictionary p_external_path_whitelist, Dictionary p_type_whitelist, const String &p_type_hint = "", bool p_use_sub_threads = false, CacheMode p_cache_mode = CACHE_MODE_REUSE);
7374
ThreadLoadStatus load_threaded_get_status(const String &p_path, Array r_progress = ClassDB::default_array_arg);
7475
Ref<Resource> load_threaded_get(const String &p_path);
7576

7677
Ref<Resource> load(const String &p_path, const String &p_type_hint = "", CacheMode p_cache_mode = CACHE_MODE_REUSE);
78+
Ref<Resource> load_whitelisted(const String &p_path, Dictionary p_external_path_whitelist, Dictionary p_type_whitelist, const String &p_type_hint = "", CacheMode p_cache_mode = CACHE_MODE_REUSE);
7779
Vector<String> get_recognized_extensions_for_type(const String &p_type);
7880
void add_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader, bool p_at_front);
7981
void remove_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader);

core/io/resource_format_binary.cpp

+58-6
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,7 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) {
408408
}
409409

410410
//always use internal cache for loading internal resources
411+
411412
if (!internal_index_cache.has(path)) {
412413
WARN_PRINT(vformat("Couldn't load resource (no cache): %s.", path));
413414
r_v = Variant();
@@ -426,14 +427,24 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) {
426427
path = ProjectSettings::get_singleton()->localize_path(res_path.get_base_dir().path_join(path));
427428
}
428429

429-
if (remaps.find(path)) {
430+
if (remaps.has(path)) {
430431
path = remaps[path];
431432
}
432433

434+
if (using_whitelist && !external_path_whitelist.has(path)) {
435+
WARN_PRINT(vformat("Blocked path not on whitelist: %s.", path));
436+
r_v = Variant();
437+
error = ERR_FILE_CANT_OPEN;
438+
return error;
439+
}
440+
433441
Ref<Resource> res = ResourceLoader::load(path, exttype, cache_mode_for_external);
434442

435443
if (res.is_null()) {
436-
WARN_PRINT(vformat("Couldn't load resource: %s.", path));
444+
ERR_PRINT(vformat("Couldn't load resource: %s.", path));
445+
r_v = Variant();
446+
error = ERR_FILE_CANT_OPEN;
447+
return error;
437448
}
438449
r_v = res;
439450

@@ -443,8 +454,10 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) {
443454
int erindex = f->get_32();
444455

445456
if (erindex < 0 || erindex >= external_resources.size()) {
446-
WARN_PRINT("Broken external resource! (index out of size)");
457+
ERR_PRINT("Broken external resource! (index out of size)");
447458
r_v = Variant();
459+
error = ERR_FILE_CORRUPT;
460+
return error;
448461
} else {
449462
Ref<ResourceLoader::LoadToken> &load_token = external_resources.write[erindex].load_token;
450463
if (load_token.is_valid()) { // If not valid, it's OK since then we know this load accepts broken dependencies.
@@ -696,8 +709,9 @@ Error ResourceLoaderBinary::load() {
696709
}
697710

698711
external_resources.write[i].path = path; //remap happens here, not on load because on load it can actually be used for filesystem dock resource remap
699-
external_resources.write[i].load_token = ResourceLoader::_load_start(path, external_resources[i].type, use_sub_threads ? ResourceLoader::LOAD_THREAD_DISTRIBUTE : ResourceLoader::LOAD_THREAD_FROM_CURRENT, cache_mode_for_external);
700-
if (external_resources[i].load_token.is_null()) {
712+
external_resources.write[i].load_token = ResourceLoader::_load_start(path, external_resources[i].type, use_sub_threads ? ResourceLoader::LOAD_THREAD_DISTRIBUTE : ResourceLoader::LOAD_THREAD_FROM_CURRENT, cache_mode_for_external, false, false, Dictionary(), Dictionary());
713+
714+
if (!external_resources[i].load_token.is_valid()) {
701715
if (!ResourceLoader::get_abort_on_missing_resources()) {
702716
ResourceLoader::notify_dependency_error(local_path, path, external_resources[i].type);
703717
} else {
@@ -768,7 +782,10 @@ Error ResourceLoaderBinary::load() {
768782
if (res.is_null()) {
769783
//did not replace
770784

771-
Object *obj = ClassDB::instantiate(t);
785+
Object *obj = nullptr;
786+
if (!using_whitelist || type_whitelist.has(t)) {
787+
obj = ClassDB::instantiate(t);
788+
}
772789
if (!obj) {
773790
if (ResourceLoader::is_creating_missing_resources_if_class_unavailable_enabled()) {
774791
//create a missing resource
@@ -1246,6 +1263,41 @@ Ref<Resource> ResourceFormatLoaderBinary::load(const String &p_path, const Strin
12461263
String path = !p_original_path.is_empty() ? p_original_path : p_path;
12471264
loader.local_path = ProjectSettings::get_singleton()->localize_path(path);
12481265
loader.res_path = loader.local_path;
1266+
loader.using_whitelist = false;
1267+
loader.open(f);
1268+
1269+
err = loader.load();
1270+
1271+
if (r_error) {
1272+
*r_error = err;
1273+
}
1274+
1275+
if (err) {
1276+
return Ref<Resource>();
1277+
}
1278+
return loader.resource;
1279+
}
1280+
1281+
Ref<Resource> ResourceFormatLoaderBinary::load_whitelisted(const String &p_path, Dictionary p_external_path_whitelist, Dictionary p_type_whitelist, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
1282+
if (r_error) {
1283+
*r_error = ERR_FILE_CANT_OPEN;
1284+
}
1285+
1286+
Error err;
1287+
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err);
1288+
1289+
ERR_FAIL_COND_V_MSG(err != OK, Ref<Resource>(), "Cannot open file '" + p_path + "'.");
1290+
1291+
ResourceLoaderBinary loader;
1292+
loader.cache_mode = p_cache_mode;
1293+
loader.use_sub_threads = p_use_sub_threads;
1294+
loader.progress = r_progress;
1295+
String path = !p_original_path.is_empty() ? p_original_path : p_path;
1296+
loader.local_path = ProjectSettings::get_singleton()->localize_path(path);
1297+
loader.res_path = loader.local_path;
1298+
loader.using_whitelist = true;
1299+
loader.external_path_whitelist = p_external_path_whitelist;
1300+
loader.type_whitelist = p_type_whitelist;
12491301
loader.open(f);
12501302

12511303
err = loader.load();

core/io/resource_format_binary.h

+5
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ class ResourceLoaderBinary {
8484
HashMap<String, String> remaps;
8585
Error error = OK;
8686

87+
bool using_whitelist = false;
88+
Dictionary external_path_whitelist;
89+
Dictionary type_whitelist;
90+
8791
ResourceFormatLoader::CacheMode cache_mode = ResourceFormatLoader::CACHE_MODE_REUSE;
8892
ResourceFormatLoader::CacheMode cache_mode_for_external = ResourceFormatLoader::CACHE_MODE_REUSE;
8993

@@ -111,6 +115,7 @@ class ResourceLoaderBinary {
111115
class ResourceFormatLoaderBinary : public ResourceFormatLoader {
112116
public:
113117
virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE) override;
118+
virtual Ref<Resource> load_whitelisted(const String &p_path, Dictionary p_external_path_whitelist, Dictionary p_type_whitelist, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE) override;
114119
virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const override;
115120
virtual void get_recognized_extensions(List<String> *p_extensions) const override;
116121
virtual bool handles_type(const String &p_type) const override;

core/io/resource_importer.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ Ref<Resource> ResourceFormatImporter::load_internal(const String &p_path, Error
171171
}
172172
}
173173

174-
Ref<Resource> res = ResourceLoader::_load(pat.path, p_path, pat.type, p_cache_mode, r_error, p_use_sub_threads, r_progress);
174+
Ref<Resource> res = ResourceLoader::_load(pat.path, p_path, pat.type, p_cache_mode, false, Dictionary(), Dictionary(), r_error, p_use_sub_threads, r_progress);
175175

176176
#ifdef TOOLS_ENABLED
177177
if (res.is_valid()) {

core/io/resource_loader.cpp

+63-7
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ ResourceLoader::LoadToken::~LoadToken() {
279279
clear();
280280
}
281281

282-
Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_original_path, const String &p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, Error *r_error, bool p_use_sub_threads, float *r_progress) {
282+
Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_original_path, const String &p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, bool p_using_whitelist, Dictionary p_external_path_whitelist, Dictionary p_type_whitelist, Error *r_error, bool p_use_sub_threads, float *r_progress) {
283283
const String &original_path = p_original_path.is_empty() ? p_path : p_original_path;
284284
load_nesting++;
285285
if (load_paths_stack.size()) {
@@ -304,7 +304,11 @@ Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_origin
304304
continue;
305305
}
306306
found = true;
307-
res = loader[i]->load(p_path, original_path, r_error, p_use_sub_threads, r_progress, p_cache_mode);
307+
if (p_using_whitelist) {
308+
res = loader[i]->load_whitelisted(p_path, p_external_path_whitelist, p_type_whitelist, p_original_path.is_valid_filename() ? p_path : p_original_path, r_error, p_use_sub_threads, r_progress, p_cache_mode);
309+
} else {
310+
res = loader[i]->load(p_path, p_original_path.is_valid_filename() ? p_path : p_original_path, r_error, p_use_sub_threads, r_progress, p_cache_mode);
311+
}
308312
if (res.is_valid()) {
309313
break;
310314
}
@@ -386,7 +390,17 @@ void ResourceLoader::_run_load_task(void *p_userdata) {
386390
const String &remapped_path = _path_remap(load_task.local_path, &xl_remapped);
387391

388392
Error load_err = OK;
389-
Ref<Resource> res = _load(remapped_path, remapped_path != load_task.local_path ? load_task.local_path : String(), load_task.type_hint, load_task.cache_mode, &load_err, load_task.use_sub_threads, &load_task.progress);
393+
Ref<Resource> res = _load(remapped_path,
394+
remapped_path != load_task.local_path ? load_task.local_path : String(),
395+
load_task.type_hint,
396+
load_task.cache_mode,
397+
load_task.using_whitelist,
398+
load_task.external_path_whitelist,
399+
load_task.type_whitelist,
400+
&load_err,
401+
load_task.use_sub_threads,
402+
&load_task.progress);
403+
390404
if (MessageQueue::get_singleton() != MessageQueue::get_main_singleton()) {
391405
MessageQueue::get_singleton()->flush();
392406
}
@@ -507,7 +521,12 @@ String ResourceLoader::_validate_local_path(const String &p_path) {
507521
}
508522

509523
Error ResourceLoader::load_threaded_request(const String &p_path, const String &p_type_hint, bool p_use_sub_threads, ResourceFormatLoader::CacheMode p_cache_mode) {
510-
Ref<ResourceLoader::LoadToken> token = _load_start(p_path, p_type_hint, p_use_sub_threads ? LOAD_THREAD_DISTRIBUTE : LOAD_THREAD_SPAWN_SINGLE, p_cache_mode, true);
524+
Ref<ResourceLoader::LoadToken> token = _load_start(p_path, p_type_hint, p_use_sub_threads ? LOAD_THREAD_DISTRIBUTE : LOAD_THREAD_SPAWN_SINGLE, p_cache_mode, true, false, Dictionary(), Dictionary());
525+
return token.is_valid() ? OK : FAILED;
526+
}
527+
528+
Error ResourceLoader::load_threaded_request_whitelisted(const String &p_path, Dictionary p_external_path_whitelist, Dictionary p_type_whitelist, const String &p_type_hint, bool p_use_sub_threads, ResourceFormatLoader::CacheMode p_cache_mode) {
529+
Ref<ResourceLoader::LoadToken> token = _load_start(p_path, p_type_hint, p_use_sub_threads ? LOAD_THREAD_DISTRIBUTE : LOAD_THREAD_SPAWN_SINGLE, p_cache_mode, true, true, p_external_path_whitelist, p_type_whitelist);
511530
return token.is_valid() ? OK : FAILED;
512531
}
513532

@@ -544,8 +563,33 @@ Ref<Resource> ResourceLoader::load(const String &p_path, const String &p_type_hi
544563
// cyclic load detection and awaiting.
545564
thread_mode = LOAD_THREAD_SPAWN_SINGLE;
546565
}
547-
Ref<LoadToken> load_token = _load_start(p_path, p_type_hint, thread_mode, p_cache_mode);
548-
if (load_token.is_null()) {
566+
Ref<LoadToken> load_token = _load_start(p_path, p_type_hint, thread_mode, p_cache_mode, false, false, Dictionary(), Dictionary());
567+
if (!load_token.is_valid()) {
568+
if (r_error) {
569+
*r_error = FAILED;
570+
}
571+
return Ref<Resource>();
572+
}
573+
574+
Ref<Resource> res = _load_complete(*load_token.ptr(), r_error);
575+
return res;
576+
}
577+
578+
Ref<Resource> ResourceLoader::load_whitelisted(const String &p_path, Dictionary p_external_path_whitelist, Dictionary p_type_whitelist, const String &p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, Error *r_error) {
579+
if (r_error) {
580+
*r_error = OK;
581+
}
582+
583+
LoadThreadMode thread_mode = LOAD_THREAD_FROM_CURRENT;
584+
if (WorkerThreadPool::get_singleton()->get_caller_task_id() != WorkerThreadPool::INVALID_TASK_ID) {
585+
// If user is initiating a single-threaded load from a WorkerThreadPool task,
586+
// we instead spawn a new task so there's a precondition that a load in a pool task
587+
// is always initiated by the engine. That makes certain aspects simpler, such as
588+
// cyclic load detection and awaiting.
589+
thread_mode = LOAD_THREAD_SPAWN_SINGLE;
590+
}
591+
Ref<LoadToken> load_token = _load_start(p_path, p_type_hint, thread_mode, p_cache_mode, false, true, p_external_path_whitelist, p_type_whitelist);
592+
if (!load_token.is_valid()) {
549593
if (r_error) {
550594
*r_error = FAILED;
551595
}
@@ -556,9 +600,14 @@ Ref<Resource> ResourceLoader::load(const String &p_path, const String &p_type_hi
556600
return res;
557601
}
558602

559-
Ref<ResourceLoader::LoadToken> ResourceLoader::_load_start(const String &p_path, const String &p_type_hint, LoadThreadMode p_thread_mode, ResourceFormatLoader::CacheMode p_cache_mode, bool p_for_user) {
603+
Ref<ResourceLoader::LoadToken> ResourceLoader::_load_start(const String &p_path, const String &p_type_hint, LoadThreadMode p_thread_mode, ResourceFormatLoader::CacheMode p_cache_mode, bool p_for_user, bool p_use_whitelist, Dictionary p_external_path_whitelist, Dictionary p_type_whitelist) {
560604
String local_path = _validate_local_path(p_path);
561605

606+
if (p_use_whitelist && !p_external_path_whitelist.has(local_path)) {
607+
WARN_PRINT(vformat("Blocked path not on whitelist: %s.", local_path));
608+
return Ref<ResourceLoader::LoadToken>();
609+
}
610+
562611
bool ignoring_cache = p_cache_mode == ResourceFormatLoader::CACHE_MODE_IGNORE || p_cache_mode == ResourceFormatLoader::CACHE_MODE_IGNORE_DEEP;
563612

564613
Ref<LoadToken> load_token;
@@ -605,6 +654,9 @@ Ref<ResourceLoader::LoadToken> ResourceLoader::_load_start(const String &p_path,
605654
load_task.type_hint = p_type_hint;
606655
load_task.cache_mode = p_cache_mode;
607656
load_task.use_sub_threads = p_thread_mode == LOAD_THREAD_DISTRIBUTE;
657+
load_task.using_whitelist = p_use_whitelist;
658+
load_task.external_path_whitelist = p_external_path_whitelist;
659+
load_task.type_whitelist = p_type_whitelist;
608660
if (p_cache_mode == ResourceFormatLoader::CACHE_MODE_REUSE) {
609661
Ref<Resource> existing = ResourceCache::get_ref(local_path);
610662
if (existing.is_valid()) {
@@ -1573,3 +1625,7 @@ HashMap<String, Vector<String>> ResourceLoader::translation_remaps;
15731625
HashMap<String, String> ResourceLoader::path_remaps;
15741626

15751627
ResourceLoaderImport ResourceLoader::import = nullptr;
1628+
1629+
Ref<Resource> ResourceFormatLoader::load_whitelisted(const String &p_path, Dictionary p_external_path_whitelist, Dictionary p_type_whitelist, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
1630+
return Ref<Resource>();
1631+
}

0 commit comments

Comments
 (0)