Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/drivers/windows/file_access_windows.cpp
20816 views
1
/**************************************************************************/
2
/* file_access_windows.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#ifdef WINDOWS_ENABLED
32
33
#include "file_access_windows.h"
34
35
#include "core/config/project_settings.h"
36
#include "core/os/os.h"
37
#include "core/string/print_string.h"
38
39
#include <share.h> // _SH_DENYNO
40
#include <shlwapi.h>
41
#define WIN32_LEAN_AND_MEAN
42
#include <windows.h>
43
44
#include <io.h>
45
#include <sys/stat.h>
46
#include <sys/types.h>
47
#include <tchar.h>
48
#include <cerrno>
49
#include <cwchar>
50
51
#ifdef _MSC_VER
52
#define S_ISREG(m) ((m) & _S_IFREG)
53
#endif
54
55
void FileAccessWindows::check_errors(bool p_write) const {
56
ERR_FAIL_NULL(f);
57
58
last_error = OK;
59
if (ferror(f)) {
60
if (p_write) {
61
last_error = ERR_FILE_CANT_WRITE;
62
} else {
63
last_error = ERR_FILE_CANT_READ;
64
}
65
}
66
if (!p_write && feof(f)) {
67
last_error = ERR_FILE_EOF;
68
}
69
}
70
71
bool FileAccessWindows::is_path_invalid(const String &p_path) {
72
// Check for invalid operating system file.
73
String fname = p_path.get_file().to_lower();
74
75
int dot = fname.find_char('.');
76
if (dot != -1) {
77
fname = fname.substr(0, dot);
78
}
79
return invalid_files.has(fname);
80
}
81
82
String FileAccessWindows::fix_path(const String &p_path) const {
83
String r_path = FileAccess::fix_path(p_path);
84
85
if (r_path.is_relative_path()) {
86
Char16String current_dir_name;
87
size_t str_len = GetCurrentDirectoryW(0, nullptr);
88
current_dir_name.resize_uninitialized(str_len + 1);
89
GetCurrentDirectoryW(current_dir_name.size(), (LPWSTR)current_dir_name.ptrw());
90
r_path = String::utf16((const char16_t *)current_dir_name.get_data()).trim_prefix(R"(\\?\)").replace_char('\\', '/').path_join(r_path);
91
}
92
r_path = r_path.simplify_path();
93
r_path = r_path.replace_char('/', '\\');
94
if (!r_path.is_network_share_path() && !r_path.begins_with(R"(\\?\)")) {
95
r_path = R"(\\?\)" + r_path;
96
}
97
return r_path;
98
}
99
100
Error FileAccessWindows::open_internal(const String &p_path, int p_mode_flags) {
101
if (is_path_invalid(p_path)) {
102
#ifdef DEBUG_ENABLED
103
if (p_mode_flags != READ) {
104
WARN_PRINT("The path :" + p_path + " is a reserved Windows system pipe, so it can't be used for creating files.");
105
}
106
#endif
107
return ERR_INVALID_PARAMETER;
108
}
109
110
_close();
111
112
path_src = p_path;
113
path = fix_path(p_path);
114
115
const WCHAR *mode_string;
116
117
if (p_mode_flags == READ) {
118
mode_string = L"rb";
119
} else if (p_mode_flags == WRITE) {
120
mode_string = L"wb";
121
} else if (p_mode_flags == READ_WRITE) {
122
mode_string = L"rb+";
123
} else if (p_mode_flags == WRITE_READ) {
124
mode_string = L"wb+";
125
} else {
126
return ERR_INVALID_PARAMETER;
127
}
128
129
if (path.ends_with(":\\") || path.ends_with(":")) {
130
return ERR_FILE_CANT_OPEN;
131
}
132
DWORD file_attr = GetFileAttributesW((LPCWSTR)(path.utf16().get_data()));
133
if (file_attr != INVALID_FILE_ATTRIBUTES && (file_attr & FILE_ATTRIBUTE_DIRECTORY)) {
134
return ERR_FILE_CANT_OPEN;
135
}
136
137
#ifdef TOOLS_ENABLED
138
// Windows is case insensitive in the default configuration, but other platforms can be sensitive to it
139
// To ease cross-platform development, we issue a warning if users try to access
140
// a file using the wrong case (which *works* on Windows, but won't on other
141
// platforms), we only check for relative paths, or paths in res:// or user://,
142
// other paths aren't likely to be portable anyway.
143
if (p_mode_flags == READ && (p_path.is_relative_path() || get_access_type() != ACCESS_FILESYSTEM)) {
144
String base_path = FileAccess::fix_path(p_path);
145
String working_path;
146
String proper_path;
147
148
if (get_access_type() == ACCESS_RESOURCES) {
149
if (ProjectSettings::get_singleton()) {
150
working_path = ProjectSettings::get_singleton()->get_resource_path();
151
if (!working_path.is_empty()) {
152
base_path = working_path.path_to_file(base_path);
153
}
154
}
155
proper_path = "res://";
156
} else if (get_access_type() == ACCESS_USERDATA) {
157
working_path = OS::get_singleton()->get_user_data_dir();
158
if (!working_path.is_empty()) {
159
base_path = working_path.path_to_file(base_path);
160
}
161
proper_path = "user://";
162
}
163
working_path = fix_path(working_path);
164
165
WIN32_FIND_DATAW d;
166
Vector<String> parts = base_path.simplify_path().split("/");
167
168
bool mismatch = false;
169
170
for (const String &part : parts) {
171
working_path = working_path + "\\" + part;
172
173
HANDLE fnd = FindFirstFileW((LPCWSTR)(working_path.utf16().get_data()), &d);
174
if (fnd == INVALID_HANDLE_VALUE) {
175
mismatch = false;
176
break;
177
}
178
179
const String fname = String::utf16((const char16_t *)(d.cFileName));
180
181
FindClose(fnd);
182
183
if (!mismatch) {
184
mismatch = (part != fname && part.findn(fname) == 0);
185
}
186
187
proper_path = proper_path.path_join(fname);
188
}
189
190
if (mismatch) {
191
WARN_PRINT("Case mismatch opening requested file '" + p_path + "', stored as '" + proper_path + "' in the filesystem. This file will not open when exported to other case-sensitive platforms.");
192
}
193
}
194
#endif
195
196
if (is_backup_save_enabled() && p_mode_flags == WRITE) {
197
save_path = path;
198
// Create a temporary file in the same directory as the target file.
199
// Note: do not use GetTempFileNameW, it's not long path aware!
200
String tmpfile;
201
uint64_t id = OS::get_singleton()->get_ticks_usec();
202
while (true) {
203
tmpfile = path + itos(id++) + ".tmp";
204
HANDLE handle = CreateFileW((LPCWSTR)tmpfile.utf16().get_data(), GENERIC_WRITE, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
205
if (handle != INVALID_HANDLE_VALUE) {
206
CloseHandle(handle);
207
break;
208
}
209
if (GetLastError() != ERROR_FILE_EXISTS && GetLastError() != ERROR_SHARING_VIOLATION) {
210
last_error = ERR_FILE_CANT_WRITE;
211
return FAILED;
212
}
213
}
214
path = tmpfile;
215
}
216
217
f = _wfsopen((LPCWSTR)(path.utf16().get_data()), mode_string, is_backup_save_enabled() ? ((p_mode_flags == READ) ? _SH_DENYWR : _SH_DENYRW) : _SH_DENYNO);
218
219
if (f == nullptr) {
220
switch (errno) {
221
case ENOENT: {
222
last_error = ERR_FILE_NOT_FOUND;
223
} break;
224
default: {
225
last_error = ERR_FILE_CANT_OPEN;
226
} break;
227
}
228
return last_error;
229
} else {
230
last_error = OK;
231
flags = p_mode_flags;
232
return OK;
233
}
234
}
235
236
void FileAccessWindows::_close() {
237
if (!f) {
238
return;
239
}
240
241
fclose(f);
242
f = nullptr;
243
244
if (!save_path.is_empty()) {
245
// This workaround of trying multiple times is added to deal with paranoid Windows
246
// antiviruses that love reading just written files even if they are not executable, thus
247
// locking the file and preventing renaming from happening.
248
249
bool rename_error = true;
250
const Char16String &path_utf16 = path.utf16();
251
const Char16String &save_path_utf16 = save_path.utf16();
252
for (int i = 0; i < 1000; i++) {
253
if (ReplaceFileW((LPCWSTR)(save_path_utf16.get_data()), (LPCWSTR)(path_utf16.get_data()), nullptr, REPLACEFILE_IGNORE_MERGE_ERRORS | REPLACEFILE_IGNORE_ACL_ERRORS, nullptr, nullptr)) {
254
rename_error = false;
255
} else {
256
// Either the target exists and is locked (temporarily, hopefully)
257
// or it doesn't exist; let's assume the latter before re-trying.
258
rename_error = MoveFileW((LPCWSTR)(path_utf16.get_data()), (LPCWSTR)(save_path_utf16.get_data())) == 0;
259
}
260
261
if (!rename_error) {
262
break;
263
}
264
265
OS::get_singleton()->delay_usec(1000);
266
}
267
268
if (rename_error) {
269
if (close_fail_notify) {
270
close_fail_notify(save_path);
271
}
272
}
273
274
save_path = "";
275
276
ERR_FAIL_COND_MSG(rename_error, "Safe save failed. This may be a permissions problem, but also may happen because you are running a paranoid antivirus. If this is the case, please switch to Windows Defender or disable the 'safe save' option in editor settings. This makes it work, but increases the risk of file corruption in a crash.");
277
}
278
}
279
280
String FileAccessWindows::get_path() const {
281
return path_src;
282
}
283
284
String FileAccessWindows::get_path_absolute() const {
285
return path.trim_prefix(R"(\\?\)").replace_char('\\', '/');
286
}
287
288
bool FileAccessWindows::is_open() const {
289
return (f != nullptr);
290
}
291
292
void FileAccessWindows::seek(uint64_t p_position) {
293
ERR_FAIL_NULL(f);
294
295
if (_fseeki64(f, p_position, SEEK_SET)) {
296
check_errors();
297
}
298
prev_op = 0;
299
}
300
301
void FileAccessWindows::seek_end(int64_t p_position) {
302
ERR_FAIL_NULL(f);
303
304
if (_fseeki64(f, p_position, SEEK_END)) {
305
check_errors();
306
}
307
prev_op = 0;
308
}
309
310
uint64_t FileAccessWindows::get_position() const {
311
ERR_FAIL_NULL_V_MSG(f, 0, "File must be opened before use.");
312
313
int64_t aux_position = _ftelli64(f);
314
if (aux_position < 0) {
315
check_errors();
316
}
317
return aux_position;
318
}
319
320
uint64_t FileAccessWindows::get_length() const {
321
ERR_FAIL_NULL_V(f, 0);
322
323
uint64_t pos = get_position();
324
_fseeki64(f, 0, SEEK_END);
325
uint64_t size = get_position();
326
_fseeki64(f, pos, SEEK_SET);
327
328
return size;
329
}
330
331
bool FileAccessWindows::eof_reached() const {
332
return feof(f);
333
}
334
335
uint64_t FileAccessWindows::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
336
ERR_FAIL_NULL_V(f, -1);
337
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
338
339
if (flags == READ_WRITE || flags == WRITE_READ) {
340
if (prev_op == WRITE) {
341
fflush(f);
342
}
343
prev_op = READ;
344
}
345
346
uint64_t read = fread(p_dst, 1, p_length, f);
347
check_errors();
348
349
return read;
350
}
351
352
Error FileAccessWindows::get_error() const {
353
return last_error;
354
}
355
356
Error FileAccessWindows::resize(int64_t p_length) {
357
ERR_FAIL_NULL_V_MSG(f, FAILED, "File must be opened before use.");
358
errno_t res = _chsize_s(_fileno(f), p_length);
359
switch (res) {
360
case 0:
361
return OK;
362
case EACCES:
363
case EBADF:
364
return ERR_FILE_CANT_OPEN;
365
case ENOSPC:
366
return ERR_OUT_OF_MEMORY;
367
case EINVAL:
368
return ERR_INVALID_PARAMETER;
369
default:
370
return FAILED;
371
}
372
}
373
374
void FileAccessWindows::flush() {
375
ERR_FAIL_NULL(f);
376
377
fflush(f);
378
if (prev_op == WRITE) {
379
prev_op = 0;
380
}
381
}
382
383
bool FileAccessWindows::store_buffer(const uint8_t *p_src, uint64_t p_length) {
384
ERR_FAIL_NULL_V(f, false);
385
ERR_FAIL_COND_V(!p_src && p_length > 0, false);
386
387
if (flags == READ_WRITE || flags == WRITE_READ) {
388
if (prev_op == READ) {
389
if (last_error != ERR_FILE_EOF) {
390
fseek(f, 0, SEEK_CUR);
391
}
392
}
393
prev_op = WRITE;
394
}
395
396
bool res = fwrite(p_src, 1, p_length, f) == (size_t)p_length;
397
check_errors(true);
398
return res;
399
}
400
401
bool FileAccessWindows::file_exists(const String &p_name) {
402
if (is_path_invalid(p_name)) {
403
return false;
404
}
405
406
String filename = fix_path(p_name);
407
DWORD file_attr = GetFileAttributesW((LPCWSTR)(filename.utf16().get_data()));
408
return (file_attr != INVALID_FILE_ATTRIBUTES) && !(file_attr & FILE_ATTRIBUTE_DIRECTORY);
409
}
410
411
uint64_t FileAccessWindows::_get_modified_time(const String &p_file) {
412
if (is_path_invalid(p_file)) {
413
return 0;
414
}
415
416
String file = fix_path(p_file);
417
if (file.ends_with("\\") && file != "\\") {
418
file = file.substr(0, file.length() - 1);
419
}
420
421
HANDLE handle = CreateFileW((LPCWSTR)(file.utf16().get_data()), FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
422
423
if (handle != INVALID_HANDLE_VALUE) {
424
FILETIME ft_create, ft_write;
425
426
bool status = GetFileTime(handle, &ft_create, nullptr, &ft_write);
427
428
CloseHandle(handle);
429
430
if (status) {
431
uint64_t ret = 0;
432
433
// If write time is invalid, fallback to creation time.
434
if (ft_write.dwHighDateTime == 0 && ft_write.dwLowDateTime == 0) {
435
ret = ft_create.dwHighDateTime;
436
ret <<= 32;
437
ret |= ft_create.dwLowDateTime;
438
} else {
439
ret = ft_write.dwHighDateTime;
440
ret <<= 32;
441
ret |= ft_write.dwLowDateTime;
442
}
443
444
const uint64_t WINDOWS_TICKS_PER_SECOND = 10000000;
445
const uint64_t TICKS_TO_UNIX_EPOCH = 116444736000000000LL;
446
447
if (ret >= TICKS_TO_UNIX_EPOCH) {
448
return (ret - TICKS_TO_UNIX_EPOCH) / WINDOWS_TICKS_PER_SECOND;
449
}
450
}
451
}
452
453
return 0;
454
}
455
456
uint64_t FileAccessWindows::_get_access_time(const String &p_file) {
457
if (is_path_invalid(p_file)) {
458
return 0;
459
}
460
461
String file = fix_path(p_file);
462
if (file.ends_with("\\") && file != "\\") {
463
file = file.substr(0, file.length() - 1);
464
}
465
466
HANDLE handle = CreateFileW((LPCWSTR)(file.utf16().get_data()), FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
467
468
if (handle != INVALID_HANDLE_VALUE) {
469
FILETIME ft_create, ft_access;
470
471
bool status = GetFileTime(handle, &ft_create, &ft_access, nullptr);
472
473
CloseHandle(handle);
474
475
if (status) {
476
uint64_t ret = 0;
477
478
// If access time is invalid, fallback to creation time.
479
if (ft_access.dwHighDateTime == 0 && ft_access.dwLowDateTime == 0) {
480
ret = ft_create.dwHighDateTime;
481
ret <<= 32;
482
ret |= ft_create.dwLowDateTime;
483
} else {
484
ret = ft_access.dwHighDateTime;
485
ret <<= 32;
486
ret |= ft_access.dwLowDateTime;
487
}
488
489
const uint64_t WINDOWS_TICKS_PER_SECOND = 10000000;
490
const uint64_t TICKS_TO_UNIX_EPOCH = 116444736000000000LL;
491
492
if (ret >= TICKS_TO_UNIX_EPOCH) {
493
return (ret - TICKS_TO_UNIX_EPOCH) / WINDOWS_TICKS_PER_SECOND;
494
}
495
}
496
}
497
498
ERR_FAIL_V_MSG(0, "Failed to get access time for: " + p_file + "");
499
}
500
501
int64_t FileAccessWindows::_get_size(const String &p_file) {
502
if (is_path_invalid(p_file)) {
503
return 0;
504
}
505
506
String file = fix_path(p_file);
507
if (file.ends_with("\\") && file != "\\") {
508
file = file.substr(0, file.length() - 1);
509
}
510
511
DWORD file_attr = GetFileAttributesW((LPCWSTR)(file.utf16().get_data()));
512
HANDLE handle = CreateFileW((LPCWSTR)(file.utf16().get_data()), FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
513
514
if (handle != INVALID_HANDLE_VALUE && !(file_attr & FILE_ATTRIBUTE_DIRECTORY)) {
515
LARGE_INTEGER fsize;
516
517
bool status = GetFileSizeEx(handle, &fsize);
518
519
CloseHandle(handle);
520
521
if (status) {
522
return (int64_t)fsize.QuadPart;
523
}
524
}
525
ERR_FAIL_V_MSG(-1, "Failed to get size for: " + p_file + "");
526
}
527
528
BitField<FileAccess::UnixPermissionFlags> FileAccessWindows::_get_unix_permissions(const String &p_file) {
529
return 0;
530
}
531
532
Error FileAccessWindows::_set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) {
533
return ERR_UNAVAILABLE;
534
}
535
536
bool FileAccessWindows::_get_hidden_attribute(const String &p_file) {
537
String file = fix_path(p_file);
538
539
DWORD attrib = GetFileAttributesW((LPCWSTR)file.utf16().get_data());
540
ERR_FAIL_COND_V_MSG(attrib == INVALID_FILE_ATTRIBUTES, false, "Failed to get attributes for: " + p_file);
541
return (attrib & FILE_ATTRIBUTE_HIDDEN);
542
}
543
544
Error FileAccessWindows::_set_hidden_attribute(const String &p_file, bool p_hidden) {
545
String file = fix_path(p_file);
546
const Char16String &file_utf16 = file.utf16();
547
548
DWORD attrib = GetFileAttributesW((LPCWSTR)file_utf16.get_data());
549
ERR_FAIL_COND_V_MSG(attrib == INVALID_FILE_ATTRIBUTES, FAILED, "Failed to get attributes for: " + p_file);
550
BOOL ok;
551
if (p_hidden) {
552
ok = SetFileAttributesW((LPCWSTR)file_utf16.get_data(), attrib | FILE_ATTRIBUTE_HIDDEN);
553
} else {
554
ok = SetFileAttributesW((LPCWSTR)file_utf16.get_data(), attrib & ~FILE_ATTRIBUTE_HIDDEN);
555
}
556
ERR_FAIL_COND_V_MSG(!ok, FAILED, "Failed to set attributes for: " + p_file);
557
558
return OK;
559
}
560
561
bool FileAccessWindows::_get_read_only_attribute(const String &p_file) {
562
String file = fix_path(p_file);
563
564
DWORD attrib = GetFileAttributesW((LPCWSTR)file.utf16().get_data());
565
ERR_FAIL_COND_V_MSG(attrib == INVALID_FILE_ATTRIBUTES, false, "Failed to get attributes for: " + p_file);
566
return (attrib & FILE_ATTRIBUTE_READONLY);
567
}
568
569
Error FileAccessWindows::_set_read_only_attribute(const String &p_file, bool p_ro) {
570
String file = fix_path(p_file);
571
const Char16String &file_utf16 = file.utf16();
572
573
DWORD attrib = GetFileAttributesW((LPCWSTR)file_utf16.get_data());
574
ERR_FAIL_COND_V_MSG(attrib == INVALID_FILE_ATTRIBUTES, FAILED, "Failed to get attributes for: " + p_file);
575
BOOL ok;
576
if (p_ro) {
577
ok = SetFileAttributesW((LPCWSTR)file_utf16.get_data(), attrib | FILE_ATTRIBUTE_READONLY);
578
} else {
579
ok = SetFileAttributesW((LPCWSTR)file_utf16.get_data(), attrib & ~FILE_ATTRIBUTE_READONLY);
580
}
581
ERR_FAIL_COND_V_MSG(!ok, FAILED, "Failed to set attributes for: " + p_file);
582
583
return OK;
584
}
585
586
PackedByteArray FileAccessWindows::_get_extended_attribute(const String &p_file, const String &p_attribute_name) {
587
ERR_FAIL_COND_V(p_attribute_name.is_empty(), PackedByteArray());
588
589
String file = fix_path(p_file);
590
file += ":" + p_attribute_name;
591
const Char16String &file_utf16 = file.utf16();
592
593
PackedByteArray data;
594
HANDLE h = CreateFileW((LPCWSTR)file_utf16.get_data(), GENERIC_READ, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
595
if (h != INVALID_HANDLE_VALUE) {
596
size_t bytes_in_buffer = 0;
597
const int CHUNK_SIZE = 4096;
598
599
DWORD read = 0;
600
for (;;) {
601
data.resize(bytes_in_buffer + CHUNK_SIZE);
602
bool success = ReadFile(h, data.ptrw() + bytes_in_buffer, CHUNK_SIZE, &read, nullptr);
603
if (!success || read == 0) {
604
break;
605
}
606
bytes_in_buffer += read;
607
}
608
data.resize(bytes_in_buffer);
609
CloseHandle(h);
610
}
611
return data;
612
}
613
614
Error FileAccessWindows::_set_extended_attribute(const String &p_file, const String &p_attribute_name, const PackedByteArray &p_data) {
615
ERR_FAIL_COND_V(p_attribute_name.is_empty(), FAILED);
616
617
String file = fix_path(p_file);
618
file += ":" + p_attribute_name;
619
const Char16String &file_utf16 = file.utf16();
620
621
PackedByteArray data;
622
HANDLE h = CreateFileW((LPCWSTR)file_utf16.get_data(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
623
if (h == INVALID_HANDLE_VALUE) {
624
return FAILED;
625
}
626
627
DWORD written = 0;
628
bool ok = true;
629
if (p_data.size() > 0) {
630
ok = WriteFile(h, p_data.ptr(), p_data.size(), &written, nullptr);
631
}
632
CloseHandle(h);
633
634
ERR_FAIL_COND_V_MSG(!ok || written != p_data.size(), FAILED, "Failed to set extended attributes for: " + p_file);
635
636
return OK;
637
}
638
639
Error FileAccessWindows::_remove_extended_attribute(const String &p_file, const String &p_attribute_name) {
640
ERR_FAIL_COND_V(p_attribute_name.is_empty(), FAILED);
641
642
String file = fix_path(p_file);
643
file += ":" + p_attribute_name;
644
const Char16String &file_utf16 = file.utf16();
645
646
return DeleteFileW((LPCWSTR)(file_utf16.get_data())) != 0 ? OK : FAILED;
647
}
648
649
PackedStringArray FileAccessWindows::_get_extended_attributes_list(const String &p_file) {
650
PackedStringArray ret;
651
String file = fix_path(p_file);
652
653
char info_block[65536] = {};
654
PFILE_STREAM_INFO stream_info = (PFILE_STREAM_INFO)info_block;
655
656
HANDLE h = CreateFileW((LPCWSTR)file.utf16().get_data(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
657
if (h == INVALID_HANDLE_VALUE) {
658
return ret;
659
}
660
BOOL out = GetFileInformationByHandleEx(h, FileStreamInfo, info_block, sizeof(info_block));
661
CloseHandle(h);
662
if (!out) {
663
return ret;
664
}
665
while (true) {
666
if (stream_info->StreamNameLength != 0) {
667
String name = String::utf16((const char16_t *)stream_info->StreamName, stream_info->StreamNameLength / sizeof(WCHAR));
668
if (name.ends_with(":$DATA")) {
669
name = name.trim_prefix(":").trim_suffix(":$DATA");
670
if (!name.is_empty()) {
671
ret.push_back(name);
672
}
673
}
674
}
675
if (stream_info->NextEntryOffset == 0) {
676
break;
677
}
678
stream_info = (PFILE_STREAM_INFO)((LPBYTE)stream_info + stream_info->NextEntryOffset);
679
}
680
681
return ret;
682
}
683
684
void FileAccessWindows::close() {
685
_close();
686
}
687
688
FileAccessWindows::~FileAccessWindows() {
689
_close();
690
}
691
692
HashSet<String> FileAccessWindows::invalid_files;
693
694
void FileAccessWindows::initialize() {
695
static const char *reserved_files[]{
696
"con", "prn", "aux", "nul", "com0", "com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8", "com9", "lpt0", "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", nullptr
697
};
698
int reserved_file_index = 0;
699
while (reserved_files[reserved_file_index] != nullptr) {
700
invalid_files.insert(reserved_files[reserved_file_index]);
701
reserved_file_index++;
702
}
703
704
_setmaxstdio(8192);
705
print_verbose(vformat("Maximum number of file handles: %d", _getmaxstdio()));
706
}
707
708
void FileAccessWindows::finalize() {
709
invalid_files.clear();
710
}
711
712
#endif // WINDOWS_ENABLED
713
714