Repository: godotengine/godot (language: cpp)

Issue:
[Web] Cannot remove files with `DirAccess.remove_absolute`
### Tested versions

Reproducible: 4.4.dev5 and 4.4dev6

### System information

Godot v4.4.dev6 - Windows 10.0.22631 - Multi-window, 2 monitors - OpenGL 3 (Compatibility) - NVIDIA GeForce RTX 3060 Laptop GPU (NVIDIA; 32.0.15.6603) - 12th Gen Intel(R) Core(TM) i5-12500H (16 threads)

### Issue description


On the exported project, I am creating a file in `user://` folder, then letting users remove it. `DirAccess.remove_absolute(filepath)` returns 0 (successful), but after restarting the scene Ctrl + Refresh, the file is seen there again. 


### Steps to reproduce

List folders in `user://`, if empty create a file there.

Delete using `DirAccess.remove_absolute("user://filename")`.
Restart the game via refresh button on the browser.

```
Listing folder: user://
test.js:459 ["text.txt"]
test.js:459 File stored at: user://text.txt
test.js:459 Listing folder: user://
test.js:459 ["text.txt"]
test.js:459 Deleting: user://text.txt
test.js:459 Deleted user://text.txt
test.js:459 Listing folder: user://
test.js:459 []
```
After refreshing,

```
Listing folder: user://
test.js:459 ["text.txt"]`
```

The `text.txt` is still there.

### Minimal reproduction project (MRP)

[testbug.zip](https://github.com/user-attachments/files/18067479/testbug.zip)

Export as web and launch it. Check the Dev Console.


Discussion / hints:

Fix patch:
diff --git a/drivers/unix/dir_access_unix.cpp b/drivers/unix/dir_access_unix.cpp
index 816ffd7a322f..6d3e4c7dbf19 100644
--- a/drivers/unix/dir_access_unix.cpp
+++ b/drivers/unix/dir_access_unix.cpp
@@ -438,11 +438,19 @@ Error DirAccessUnix::remove(String p_path) {
 		return FAILED;
 	}
 
+	int err;
 	if (S_ISDIR(flags.st_mode) && !is_link(p_path)) {
-		return ::rmdir(p_path.utf8().get_data()) == 0 ? OK : FAILED;
+		err = ::rmdir(p_path.utf8().get_data());
 	} else {
-		return ::unlink(p_path.utf8().get_data()) == 0 ? OK : FAILED;
+		err = ::unlink(p_path.utf8().get_data());
 	}
+	if (err != 0) {
+		return FAILED;
+	}
+	if (remove_notification_func != nullptr) {
+		remove_notification_func(p_path);
+	}
+	return OK;
 }
 
 bool DirAccessUnix::is_link(String p_file) {
@@ -552,6 +560,8 @@ DirAccessUnix::DirAccessUnix() {
 	change_dir(current_dir);
 }
 
+DirAccessUnix::RemoveNotificationFunc DirAccessUnix::remove_notification_func = nullptr;
+
 DirAccessUnix::~DirAccessUnix() {
 	list_dir_end();
 }
diff --git a/drivers/unix/dir_access_unix.h b/drivers/unix/dir_access_unix.h
index 8d13ff1fa88d..adf1fa80e7be 100644
--- a/drivers/unix/dir_access_unix.h
+++ b/drivers/unix/dir_access_unix.h
@@ -52,6 +52,9 @@ class DirAccessUnix : public DirAccess {
 	virtual bool is_hidden(const String &p_name);
 
 public:
+	typedef void (*RemoveNotificationFunc)(const String &p_file);
+	static RemoveNotificationFunc remove_notification_func;
+
 	virtual Error list_dir_begin() override; ///< This starts dir listing
 	virtual String get_next() override;
 	virtual bool current_is_dir() const override;
diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp
index f2e4b3966be8..048d42529029 100644
--- a/drivers/unix/file_access_unix.cpp
+++ b/drivers/unix/file_access_unix.cpp
@@ -443,7 +443,7 @@ void FileAccessUnix::close() {
 	_close();
 }
 
-CloseNotificationFunc FileAccessUnix::close_notification_func = nullptr;
+FileAccessUnix::CloseNotificationFunc FileAccessUnix::close_notification_func = nullptr;
 
 FileAccessUnix::~FileAccessUnix() {
 	_close();
diff --git a/drivers/unix/file_access_unix.h b/drivers/unix/file_access_unix.h
index 66e4bccc2525..fb9d684714c3 100644
--- a/drivers/unix/file_access_unix.h
+++ b/drivers/unix/file_access_unix.h
@@ -38,8 +38,6 @@
 
 #if defined(UNIX_ENABLED)
 
-typedef void (*CloseNotificationFunc)(const String &p_file, int p_flags);
-
 class FileAccessUnix : public FileAccess {
 	FILE *f = nullptr;
 	int flags = 0;
@@ -56,6 +54,7 @@ class FileAccessUnix : public FileAccess {
 #endif
 
 public:
+	typedef void (*CloseNotificationFunc)(const String &p_file, int p_flags);
 	static CloseNotificationFunc close_notification_func;
 
 	virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file
diff --git a/platform/web/os_web.cpp b/platform/web/os_web.cpp
index 51facbaa845d..8f59c8ad668c 100644
--- a/platform/web/os_web.cpp
+++ b/platform/web/os_web.cpp
@@ -224,6 +224,18 @@ void OS_Web::file_access_close_callback(const String &p_file, int p_flags) {
 	}
 }
 
+void OS_Web::dir_access_remove_callback(const String &p_file) {
+	OS_Web *os = OS_Web::get_singleton();
+	bool is_file_persistent = p_file.begins_with("/userfs");
+#ifdef TOOLS_ENABLED
+	// Hack for editor persistence (can we track).
+	is_file_persistent = is_file_persistent || p_file.begins_with("/home/web_user/");
+#endif
+	if (is_file_persistent) {
+		os->idb_needs_sync = true;
+	}
+}
+
 void OS_Web::update_pwa_state_callback() {
 	if (OS_Web::get_singleton()) {
 		OS_Web::get_singleton()->pwa_is_waiting = true;
@@ -287,4 +299,5 @@ OS_Web::OS_Web() {
 	_set_logger(memnew(CompositeLogger(loggers)));
 
 	FileAccessUnix::close_notification_func = file_access_close_callback;
+	DirAccessUnix::remove_notification_func = dir_access_remove_callback;
 }
diff --git a/platform/web/os_web.h b/platform/web/os_web.h
index 1ddb745965ac..8965cc4424b7 100644
--- a/platform/web/os_web.h
+++ b/platform/web/os_web.h
@@ -53,6 +53,7 @@ class OS_Web : public OS_Unix {
 	WASM_EXPORT static void main_loop_callback();
 
 	WASM_EXPORT static void file_access_close_callback(const String &p_file, int p_flags);
+	WASM_EXPORT static void dir_access_remove_callback(const String &p_file);
 	WASM_EXPORT static void fs_sync_callback();
 	WASM_EXPORT static void update_pwa_state_callback();
 


Test patch: