librats 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/binding.gyp +1 -0
- package/native-src/3rdparty/android/ifaddrs-android.c +600 -0
- package/native-src/3rdparty/android/ifaddrs-android.h +54 -0
- package/native-src/CMakeLists.txt +360 -0
- package/native-src/LICENSE +21 -0
- package/native-src/src/bencode.cpp +485 -0
- package/native-src/src/bencode.h +145 -0
- package/native-src/src/bittorrent.cpp +3682 -0
- package/native-src/src/bittorrent.h +731 -0
- package/native-src/src/dht.cpp +2342 -0
- package/native-src/src/dht.h +501 -0
- package/native-src/src/encrypted_socket.cpp +817 -0
- package/native-src/src/encrypted_socket.h +239 -0
- package/native-src/src/file_transfer.cpp +1808 -0
- package/native-src/src/file_transfer.h +567 -0
- package/native-src/src/fs.cpp +639 -0
- package/native-src/src/fs.h +108 -0
- package/native-src/src/gossipsub.cpp +1137 -0
- package/native-src/src/gossipsub.h +403 -0
- package/native-src/src/ice.cpp +1386 -0
- package/native-src/src/ice.h +328 -0
- package/native-src/src/json.hpp +25526 -0
- package/native-src/src/krpc.cpp +558 -0
- package/native-src/src/krpc.h +145 -0
- package/native-src/src/librats.cpp +2715 -0
- package/native-src/src/librats.h +1729 -0
- package/native-src/src/librats_bittorrent.cpp +167 -0
- package/native-src/src/librats_c.cpp +1317 -0
- package/native-src/src/librats_c.h +237 -0
- package/native-src/src/librats_encryption.cpp +123 -0
- package/native-src/src/librats_file_transfer.cpp +226 -0
- package/native-src/src/librats_gossipsub.cpp +293 -0
- package/native-src/src/librats_ice.cpp +515 -0
- package/native-src/src/librats_logging.cpp +158 -0
- package/native-src/src/librats_mdns.cpp +171 -0
- package/native-src/src/librats_nat.cpp +571 -0
- package/native-src/src/librats_persistence.cpp +815 -0
- package/native-src/src/logger.h +412 -0
- package/native-src/src/mdns.cpp +1178 -0
- package/native-src/src/mdns.h +253 -0
- package/native-src/src/network_utils.cpp +598 -0
- package/native-src/src/network_utils.h +162 -0
- package/native-src/src/noise.cpp +981 -0
- package/native-src/src/noise.h +227 -0
- package/native-src/src/os.cpp +371 -0
- package/native-src/src/os.h +40 -0
- package/native-src/src/rats_export.h +17 -0
- package/native-src/src/sha1.cpp +163 -0
- package/native-src/src/sha1.h +42 -0
- package/native-src/src/socket.cpp +1376 -0
- package/native-src/src/socket.h +309 -0
- package/native-src/src/stun.cpp +484 -0
- package/native-src/src/stun.h +349 -0
- package/native-src/src/threadmanager.cpp +105 -0
- package/native-src/src/threadmanager.h +53 -0
- package/native-src/src/tracker.cpp +1110 -0
- package/native-src/src/tracker.h +268 -0
- package/native-src/src/version.cpp +24 -0
- package/native-src/src/version.h.in +45 -0
- package/native-src/version.rc.in +31 -0
- package/package.json +2 -8
- package/scripts/build-librats.js +59 -12
- package/scripts/prepare-package.js +133 -37
|
@@ -0,0 +1,639 @@
|
|
|
1
|
+
#include "fs.h"
|
|
2
|
+
#include "logger.h"
|
|
3
|
+
#include <cstring>
|
|
4
|
+
#include <cstdlib>
|
|
5
|
+
#include <sys/stat.h>
|
|
6
|
+
|
|
7
|
+
#ifdef _WIN32
|
|
8
|
+
#include <windows.h>
|
|
9
|
+
#include <direct.h>
|
|
10
|
+
#include <io.h>
|
|
11
|
+
#define stat _stat
|
|
12
|
+
#define mkdir(path, mode) _mkdir(path)
|
|
13
|
+
#define access _access
|
|
14
|
+
#define F_OK 0
|
|
15
|
+
#define getcwd _getcwd
|
|
16
|
+
#define chdir _chdir
|
|
17
|
+
#else
|
|
18
|
+
#include <unistd.h>
|
|
19
|
+
#include <dirent.h>
|
|
20
|
+
#include <errno.h>
|
|
21
|
+
#include <libgen.h>
|
|
22
|
+
#endif
|
|
23
|
+
|
|
24
|
+
namespace librats {
|
|
25
|
+
|
|
26
|
+
bool file_or_directory_exists(const char* path) {
|
|
27
|
+
if (!path) return false;
|
|
28
|
+
return access(path, F_OK) == 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
bool file_exists(const char* path) {
|
|
32
|
+
if (!path) return false;
|
|
33
|
+
|
|
34
|
+
struct stat st;
|
|
35
|
+
if (stat(path, &st) == 0) {
|
|
36
|
+
return (st.st_mode & S_IFREG) != 0;
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
bool directory_exists(const char* path) {
|
|
42
|
+
if (!path) return false;
|
|
43
|
+
|
|
44
|
+
struct stat st;
|
|
45
|
+
if (stat(path, &st) == 0) {
|
|
46
|
+
return (st.st_mode & S_IFDIR) != 0;
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
bool create_file(const char* path, const char* content) {
|
|
52
|
+
if (!path) return false;
|
|
53
|
+
|
|
54
|
+
FILE* file = fopen(path, "wb");
|
|
55
|
+
if (!file) {
|
|
56
|
+
LOG_ERROR("FS", "Failed to create file: " << path);
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (content) {
|
|
61
|
+
size_t len = strlen(content);
|
|
62
|
+
size_t written = fwrite(content, 1, len, file);
|
|
63
|
+
fclose(file);
|
|
64
|
+
|
|
65
|
+
if (written != len) {
|
|
66
|
+
LOG_ERROR("FS", "Failed to write complete content to file: " << path);
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
fclose(file);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
bool create_file_binary(const char* path, const void* data, size_t size) {
|
|
77
|
+
if (!path) return false;
|
|
78
|
+
|
|
79
|
+
FILE* file = fopen(path, "wb");
|
|
80
|
+
if (!file) {
|
|
81
|
+
LOG_ERROR("FS", "Failed to create binary file: " << path);
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (data && size > 0) {
|
|
86
|
+
size_t written = fwrite(data, 1, size, file);
|
|
87
|
+
fclose(file);
|
|
88
|
+
|
|
89
|
+
if (written != size) {
|
|
90
|
+
LOG_ERROR("FS", "Failed to write complete binary data to file: " << path);
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
fclose(file);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
bool append_to_file(const char* path, const char* content) {
|
|
101
|
+
if (!path || !content) return false;
|
|
102
|
+
|
|
103
|
+
FILE* file = fopen(path, "ab");
|
|
104
|
+
if (!file) {
|
|
105
|
+
LOG_ERROR("FS", "Failed to open file for appending: " << path);
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
size_t len = strlen(content);
|
|
110
|
+
size_t written = fwrite(content, 1, len, file);
|
|
111
|
+
fclose(file);
|
|
112
|
+
|
|
113
|
+
if (written != len) {
|
|
114
|
+
LOG_ERROR("FS", "Failed to append complete content to file: " << path);
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
char* read_file_text(const char* path, size_t* size_out) {
|
|
122
|
+
if (!path) return nullptr;
|
|
123
|
+
|
|
124
|
+
struct stat st;
|
|
125
|
+
if (stat(path, &st) != 0) {
|
|
126
|
+
LOG_ERROR("FS", "Failed to stat file: " << path);
|
|
127
|
+
return nullptr;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
size_t file_size = st.st_size;
|
|
131
|
+
|
|
132
|
+
FILE* file = fopen(path, "rb");
|
|
133
|
+
if (!file) {
|
|
134
|
+
LOG_ERROR("FS", "Failed to open file for reading: " << path);
|
|
135
|
+
return nullptr;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Allocate buffer (+1 for null terminator)
|
|
139
|
+
char* buffer = static_cast<char*>(malloc(file_size + 1));
|
|
140
|
+
if (!buffer) {
|
|
141
|
+
LOG_ERROR("FS", "Failed to allocate memory for file: " << path);
|
|
142
|
+
fclose(file);
|
|
143
|
+
return nullptr;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Read file
|
|
147
|
+
size_t bytes_read = fread(buffer, 1, file_size, file);
|
|
148
|
+
fclose(file);
|
|
149
|
+
|
|
150
|
+
// Null terminate
|
|
151
|
+
buffer[bytes_read] = '\0';
|
|
152
|
+
|
|
153
|
+
if (size_out) {
|
|
154
|
+
*size_out = bytes_read;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return buffer;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
void* read_file_binary(const char* path, size_t* size_out) {
|
|
161
|
+
if (!path || !size_out) return nullptr;
|
|
162
|
+
|
|
163
|
+
FILE* file = fopen(path, "rb");
|
|
164
|
+
if (!file) {
|
|
165
|
+
LOG_ERROR("FS", "Failed to open binary file for reading: " << path);
|
|
166
|
+
return nullptr;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Get file size
|
|
170
|
+
fseek(file, 0, SEEK_END);
|
|
171
|
+
long file_size = ftell(file);
|
|
172
|
+
fseek(file, 0, SEEK_SET);
|
|
173
|
+
|
|
174
|
+
if (file_size < 0) {
|
|
175
|
+
LOG_ERROR("FS", "Failed to get binary file size: " << path);
|
|
176
|
+
fclose(file);
|
|
177
|
+
return nullptr;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Allocate buffer
|
|
181
|
+
void* buffer = malloc(file_size);
|
|
182
|
+
if (!buffer) {
|
|
183
|
+
LOG_ERROR("FS", "Failed to allocate memory for binary file: " << path);
|
|
184
|
+
fclose(file);
|
|
185
|
+
return nullptr;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Read file
|
|
189
|
+
size_t bytes_read = fread(buffer, 1, file_size, file);
|
|
190
|
+
fclose(file);
|
|
191
|
+
|
|
192
|
+
*size_out = bytes_read;
|
|
193
|
+
return buffer;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
bool create_directory(const char* path) {
|
|
197
|
+
if (!path) return false;
|
|
198
|
+
|
|
199
|
+
if (directory_exists(path)) {
|
|
200
|
+
return true; // Already exists
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
#ifdef _WIN32
|
|
204
|
+
return _mkdir(path) == 0;
|
|
205
|
+
#else
|
|
206
|
+
return mkdir(path, 0755) == 0;
|
|
207
|
+
#endif
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
bool create_directories(const char* path) {
|
|
211
|
+
if (!path) return false;
|
|
212
|
+
|
|
213
|
+
if (directory_exists(path)) {
|
|
214
|
+
return true; // Already exists
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Create a copy of the path to modify
|
|
218
|
+
size_t len = strlen(path);
|
|
219
|
+
char* path_copy = static_cast<char*>(malloc(len + 1));
|
|
220
|
+
if (!path_copy) {
|
|
221
|
+
LOG_ERROR("FS", "Failed to allocate memory for path copy in create_directories");
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
memcpy(path_copy, path, len + 1);
|
|
226
|
+
|
|
227
|
+
// Create parent directories recursively
|
|
228
|
+
for (size_t i = 1; i < len; i++) {
|
|
229
|
+
if (path_copy[i] == '/' || path_copy[i] == '\\') {
|
|
230
|
+
path_copy[i] = '\0';
|
|
231
|
+
|
|
232
|
+
if (!directory_exists(path_copy)) {
|
|
233
|
+
if (!create_directory(path_copy)) {
|
|
234
|
+
free(path_copy);
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
path_copy[i] = '/'; // Normalize to forward slash
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Create the final directory
|
|
244
|
+
bool result = create_directory(path_copy);
|
|
245
|
+
free(path_copy);
|
|
246
|
+
return result;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
int64_t get_file_size(const char* path) {
|
|
250
|
+
if (!path) return -1;
|
|
251
|
+
|
|
252
|
+
struct stat st;
|
|
253
|
+
if (stat(path, &st) == 0) {
|
|
254
|
+
return st.st_size;
|
|
255
|
+
}
|
|
256
|
+
return -1;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
bool is_file(const char* path) {
|
|
260
|
+
return file_exists(path);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
bool is_directory(const char* path) {
|
|
264
|
+
return directory_exists(path);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
bool delete_file(const char* path) {
|
|
268
|
+
if (!path) return false;
|
|
269
|
+
|
|
270
|
+
return remove(path) == 0;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
bool delete_directory(const char* path) {
|
|
274
|
+
if (!path) return false;
|
|
275
|
+
|
|
276
|
+
#ifdef _WIN32
|
|
277
|
+
return RemoveDirectoryA(path) != 0;
|
|
278
|
+
#else
|
|
279
|
+
return rmdir(path) == 0;
|
|
280
|
+
#endif
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
bool copy_file(const char* src_path, const char* dest_path) {
|
|
284
|
+
if (!src_path || !dest_path) return false;
|
|
285
|
+
|
|
286
|
+
FILE* src_file = fopen(src_path, "rb");
|
|
287
|
+
if (!src_file) {
|
|
288
|
+
LOG_ERROR("FS", "Failed to open source file for copying: " << src_path);
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
FILE* dest_file = fopen(dest_path, "wb");
|
|
293
|
+
if (!dest_file) {
|
|
294
|
+
LOG_ERROR("FS", "Failed to open destination file for copying: " << dest_path);
|
|
295
|
+
fclose(src_file);
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
char buffer[4096];
|
|
300
|
+
size_t bytes_read;
|
|
301
|
+
bool success = true;
|
|
302
|
+
|
|
303
|
+
while ((bytes_read = fread(buffer, 1, sizeof(buffer), src_file)) > 0) {
|
|
304
|
+
if (fwrite(buffer, 1, bytes_read, dest_file) != bytes_read) {
|
|
305
|
+
LOG_ERROR("FS", "Failed to write to destination file: " << dest_path);
|
|
306
|
+
success = false;
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (ferror(src_file)) {
|
|
312
|
+
LOG_ERROR("FS", "Error reading from source file: " << src_path);
|
|
313
|
+
success = false;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
fclose(src_file);
|
|
317
|
+
fclose(dest_file);
|
|
318
|
+
|
|
319
|
+
if (!success) {
|
|
320
|
+
delete_file(dest_path);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return success;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
bool move_file(const char* src_path, const char* dest_path) {
|
|
327
|
+
if (!src_path || !dest_path) return false;
|
|
328
|
+
|
|
329
|
+
if (rename(src_path, dest_path) == 0) {
|
|
330
|
+
return true;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// If rename fails, try copy and delete
|
|
334
|
+
if (copy_file(src_path, dest_path)) {
|
|
335
|
+
return delete_file(src_path);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return false;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
void free_file_buffer(void* buffer) {
|
|
342
|
+
if (buffer) {
|
|
343
|
+
free(buffer);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
bool get_current_directory(char* buffer, size_t buffer_size) {
|
|
348
|
+
if (!buffer || buffer_size == 0) return false;
|
|
349
|
+
|
|
350
|
+
return getcwd(buffer, buffer_size) != nullptr;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
bool set_current_directory(const char* path) {
|
|
354
|
+
if (!path) return false;
|
|
355
|
+
|
|
356
|
+
return chdir(path) == 0;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// File metadata operations
|
|
360
|
+
uint64_t get_file_modified_time(const char* path) {
|
|
361
|
+
if (!path) return 0;
|
|
362
|
+
|
|
363
|
+
struct stat st;
|
|
364
|
+
if (stat(path, &st) == 0) {
|
|
365
|
+
return static_cast<uint64_t>(st.st_mtime);
|
|
366
|
+
}
|
|
367
|
+
return 0;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
std::string get_file_extension(const char* path) {
|
|
371
|
+
if (!path) return "";
|
|
372
|
+
|
|
373
|
+
const char* dot = strrchr(path, '.');
|
|
374
|
+
if (dot && dot != path) {
|
|
375
|
+
return std::string(dot);
|
|
376
|
+
}
|
|
377
|
+
return "";
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
std::string get_filename_from_path(const char* path) {
|
|
381
|
+
if (!path) return "";
|
|
382
|
+
|
|
383
|
+
const char* filename = strrchr(path, '/');
|
|
384
|
+
if (!filename) {
|
|
385
|
+
filename = strrchr(path, '\\');
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (filename) {
|
|
389
|
+
return std::string(filename + 1);
|
|
390
|
+
} else {
|
|
391
|
+
return std::string(path);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
std::string get_parent_directory(const char* path) {
|
|
396
|
+
if (!path) return "";
|
|
397
|
+
|
|
398
|
+
std::string str_path(path);
|
|
399
|
+
size_t pos = str_path.find_last_of("/\\");
|
|
400
|
+
if (pos != std::string::npos) {
|
|
401
|
+
return str_path.substr(0, pos);
|
|
402
|
+
}
|
|
403
|
+
return "";
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// File chunk operations
|
|
407
|
+
bool write_file_chunk(const char* path, uint64_t offset, const void* data, size_t size) {
|
|
408
|
+
if (!path || !data) return false;
|
|
409
|
+
|
|
410
|
+
FILE* file = fopen(path, "r+b");
|
|
411
|
+
if (!file) {
|
|
412
|
+
// Try to create the file if it doesn't exist
|
|
413
|
+
file = fopen(path, "w+b");
|
|
414
|
+
if (!file) {
|
|
415
|
+
LOG_ERROR("FS", "Failed to open file for chunk writing: " << path);
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Use platform-specific 64-bit seek
|
|
421
|
+
#ifdef _WIN32
|
|
422
|
+
if (_fseeki64(file, static_cast<__int64>(offset), SEEK_SET) != 0) {
|
|
423
|
+
#else
|
|
424
|
+
if (fseeko(file, static_cast<off_t>(offset), SEEK_SET) != 0) {
|
|
425
|
+
#endif
|
|
426
|
+
LOG_ERROR("FS", "Failed to seek to offset " << offset << " in file: " << path);
|
|
427
|
+
fclose(file);
|
|
428
|
+
return false;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
size_t written = fwrite(data, 1, size, file);
|
|
432
|
+
fclose(file);
|
|
433
|
+
|
|
434
|
+
if (written != size) {
|
|
435
|
+
LOG_ERROR("FS", "Failed to write complete chunk to file: " << path);
|
|
436
|
+
return false;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
return true;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
bool read_file_chunk(const char* path, uint64_t offset, void* buffer, size_t size) {
|
|
443
|
+
if (!path || !buffer) return false;
|
|
444
|
+
|
|
445
|
+
FILE* file = fopen(path, "rb");
|
|
446
|
+
if (!file) {
|
|
447
|
+
LOG_ERROR("FS", "Failed to open file for chunk reading: " << path);
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Use platform-specific 64-bit seek
|
|
452
|
+
#ifdef _WIN32
|
|
453
|
+
if (_fseeki64(file, static_cast<__int64>(offset), SEEK_SET) != 0) {
|
|
454
|
+
#else
|
|
455
|
+
if (fseeko(file, static_cast<off_t>(offset), SEEK_SET) != 0) {
|
|
456
|
+
#endif
|
|
457
|
+
LOG_ERROR("FS", "Failed to seek to offset " << offset << " in file: " << path);
|
|
458
|
+
fclose(file);
|
|
459
|
+
return false;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
size_t bytes_read = fread(buffer, 1, size, file);
|
|
463
|
+
fclose(file);
|
|
464
|
+
|
|
465
|
+
if (bytes_read != size) {
|
|
466
|
+
LOG_ERROR("FS", "Failed to read complete chunk from file: " << path);
|
|
467
|
+
return false;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return true;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Advanced file operations
|
|
474
|
+
bool create_file_with_size(const char* path, uint64_t size) {
|
|
475
|
+
if (!path) return false;
|
|
476
|
+
|
|
477
|
+
FILE* file = fopen(path, "wb");
|
|
478
|
+
if (!file) {
|
|
479
|
+
LOG_ERROR("FS", "Failed to create file with size: " << path);
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (size > 0) {
|
|
484
|
+
// Pre-allocate file space by seeking to size-1 and writing a byte
|
|
485
|
+
// Use platform-specific 64-bit seek
|
|
486
|
+
#ifdef _WIN32
|
|
487
|
+
if (_fseeki64(file, static_cast<__int64>(size - 1), SEEK_SET) == 0) {
|
|
488
|
+
#else
|
|
489
|
+
if (fseeko(file, static_cast<off_t>(size - 1), SEEK_SET) == 0) {
|
|
490
|
+
#endif
|
|
491
|
+
fputc(0, file);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
fclose(file);
|
|
496
|
+
return true;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
bool rename_file(const char* old_path, const char* new_path) {
|
|
500
|
+
if (!old_path || !new_path) return false;
|
|
501
|
+
|
|
502
|
+
return rename(old_path, new_path) == 0;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Path utilities
|
|
506
|
+
std::string combine_paths(const std::string& base, const std::string& relative) {
|
|
507
|
+
if (base.empty()) return relative;
|
|
508
|
+
if (relative.empty()) return base;
|
|
509
|
+
|
|
510
|
+
std::string result = base;
|
|
511
|
+
|
|
512
|
+
// Normalize all backslashes to forward slashes in result
|
|
513
|
+
for (char& c : result) {
|
|
514
|
+
if (c == '\\') c = '/';
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// Ensure base path ends with separator
|
|
518
|
+
if (result.back() != '/') {
|
|
519
|
+
result += '/';
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// Remove leading separator from relative path and normalize
|
|
523
|
+
std::string rel = relative;
|
|
524
|
+
if (!rel.empty() && (rel.front() == '/' || rel.front() == '\\')) {
|
|
525
|
+
rel = rel.substr(1);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Normalize backslashes to forward slashes in relative path
|
|
529
|
+
for (char& c : rel) {
|
|
530
|
+
if (c == '\\') c = '/';
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
return result + rel;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
bool validate_path(const char* path, bool check_write_access) {
|
|
537
|
+
if (!path) return false;
|
|
538
|
+
|
|
539
|
+
if (check_write_access) {
|
|
540
|
+
// Check if we can write to the parent directory
|
|
541
|
+
std::string parent = get_parent_directory(path);
|
|
542
|
+
if (!parent.empty() && !directory_exists(parent.c_str())) {
|
|
543
|
+
return false;
|
|
544
|
+
}
|
|
545
|
+
// Additional write permission checks could be added here
|
|
546
|
+
} else {
|
|
547
|
+
// Check if path exists and is a regular file (not a directory)
|
|
548
|
+
if (!is_file(path)) {
|
|
549
|
+
return false;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
return true;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// Directory listing
|
|
557
|
+
bool list_directory(const char* path, std::vector<DirectoryEntry>& entries) {
|
|
558
|
+
if (!path || !directory_exists(path)) return false;
|
|
559
|
+
|
|
560
|
+
entries.clear();
|
|
561
|
+
|
|
562
|
+
#ifdef _WIN32
|
|
563
|
+
WIN32_FIND_DATAA find_data;
|
|
564
|
+
std::string search_path = std::string(path) + "\\*";
|
|
565
|
+
HANDLE find_handle = FindFirstFileA(search_path.c_str(), &find_data);
|
|
566
|
+
|
|
567
|
+
if (find_handle == INVALID_HANDLE_VALUE) {
|
|
568
|
+
return false;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
do {
|
|
572
|
+
// Skip "." and ".." entries
|
|
573
|
+
if (strcmp(find_data.cFileName, ".") == 0 || strcmp(find_data.cFileName, "..") == 0) {
|
|
574
|
+
continue;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
DirectoryEntry entry;
|
|
578
|
+
entry.name = find_data.cFileName;
|
|
579
|
+
entry.path = combine_paths(path, entry.name);
|
|
580
|
+
entry.is_directory = (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
|
581
|
+
|
|
582
|
+
if (!entry.is_directory) {
|
|
583
|
+
entry.size = (static_cast<uint64_t>(find_data.nFileSizeHigh) << 32) | find_data.nFileSizeLow;
|
|
584
|
+
} else {
|
|
585
|
+
entry.size = 0;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// Convert Windows FILETIME to Unix timestamp
|
|
589
|
+
FILETIME ft = find_data.ftLastWriteTime;
|
|
590
|
+
LARGE_INTEGER li;
|
|
591
|
+
li.LowPart = ft.dwLowDateTime;
|
|
592
|
+
li.HighPart = ft.dwHighDateTime;
|
|
593
|
+
// Convert from 100ns intervals since 1601 to seconds since 1970
|
|
594
|
+
entry.modified_time = (li.QuadPart - 116444736000000000LL) / 10000000LL;
|
|
595
|
+
|
|
596
|
+
entries.push_back(entry);
|
|
597
|
+
|
|
598
|
+
} while (FindNextFileA(find_handle, &find_data));
|
|
599
|
+
|
|
600
|
+
FindClose(find_handle);
|
|
601
|
+
|
|
602
|
+
#else
|
|
603
|
+
DIR* dir = opendir(path);
|
|
604
|
+
if (!dir) {
|
|
605
|
+
return false;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
struct dirent* entry_ptr;
|
|
609
|
+
while ((entry_ptr = readdir(dir)) != nullptr) {
|
|
610
|
+
// Skip "." and ".." entries
|
|
611
|
+
if (strcmp(entry_ptr->d_name, ".") == 0 || strcmp(entry_ptr->d_name, "..") == 0) {
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
DirectoryEntry entry;
|
|
616
|
+
entry.name = entry_ptr->d_name;
|
|
617
|
+
entry.path = combine_paths(path, entry.name);
|
|
618
|
+
|
|
619
|
+
struct stat st;
|
|
620
|
+
if (stat(entry.path.c_str(), &st) == 0) {
|
|
621
|
+
entry.is_directory = S_ISDIR(st.st_mode);
|
|
622
|
+
entry.size = entry.is_directory ? 0 : static_cast<uint64_t>(st.st_size);
|
|
623
|
+
entry.modified_time = static_cast<uint64_t>(st.st_mtime);
|
|
624
|
+
} else {
|
|
625
|
+
entry.is_directory = false;
|
|
626
|
+
entry.size = 0;
|
|
627
|
+
entry.modified_time = 0;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
entries.push_back(entry);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
closedir(dir);
|
|
634
|
+
#endif
|
|
635
|
+
|
|
636
|
+
return true;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
} // namespace librats
|