diff options
Diffstat (limited to 'uisimulator/common/filesystem-sim.c')
-rw-r--r-- | uisimulator/common/filesystem-sim.c | 833 |
1 files changed, 833 insertions, 0 deletions
diff --git a/uisimulator/common/filesystem-sim.c b/uisimulator/common/filesystem-sim.c new file mode 100644 index 0000000000..766beb3fda --- /dev/null +++ b/uisimulator/common/filesystem-sim.c | |||
@@ -0,0 +1,833 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 Daniel Stenberg | ||
11 | * Copyright (C) 2014 Michael Sevakis | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * as published by the Free Software Foundation; either version 2 | ||
16 | * of the License, or (at your option) any later version. | ||
17 | * | ||
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
19 | * KIND, either express or implied. | ||
20 | * | ||
21 | ****************************************************************************/ | ||
22 | #define RB_FILESYSTEM_OS | ||
23 | #include <stdio.h> | ||
24 | #include <stdlib.h> | ||
25 | #include <stdarg.h> | ||
26 | #include <time.h> | ||
27 | #include <errno.h> | ||
28 | #include <limits.h> | ||
29 | #include "config.h" | ||
30 | #include "system.h" | ||
31 | #include "file.h" | ||
32 | #include "dir.h" | ||
33 | #include "file_internal.h" | ||
34 | #include "pathfuncs.h" | ||
35 | #include "string-extra.h" | ||
36 | #include "debug.h" | ||
37 | |||
38 | #ifndef os_fstatat | ||
39 | #define USE_OSDIRNAME /* we need to remember the open directory path */ | ||
40 | #endif | ||
41 | |||
42 | extern const char *sim_root_dir; | ||
43 | |||
44 | /* Windows (and potentially other OSes) distinguish binary and text files. | ||
45 | * Define a dummy for the others. */ | ||
46 | #ifndef O_BINARY | ||
47 | #define O_BINARY 0 | ||
48 | #endif | ||
49 | |||
50 | struct filestr_desc | ||
51 | { | ||
52 | int osfd; /* The host OS file descriptor */ | ||
53 | bool mounted; /* Is host volume still mounted? */ | ||
54 | #ifdef HAVE_MULTIVOLUME | ||
55 | int volume; /* The virtual volume number */ | ||
56 | #endif | ||
57 | } openfiles[MAX_OPEN_FILES] = | ||
58 | { | ||
59 | [0 ... MAX_OPEN_FILES-1] = { .osfd = -1 } | ||
60 | }; | ||
61 | |||
62 | static struct filestr_desc * alloc_filestr(int *fildesp) | ||
63 | { | ||
64 | for (unsigned int i = 0; i < MAX_OPEN_FILES; i++) | ||
65 | { | ||
66 | struct filestr_desc *filestr = &openfiles[i]; | ||
67 | if (filestr->osfd < 0) | ||
68 | { | ||
69 | *fildesp = i; | ||
70 | return filestr; | ||
71 | } | ||
72 | } | ||
73 | |||
74 | return NULL; | ||
75 | } | ||
76 | |||
77 | static struct filestr_desc * get_filestr(int fildes) | ||
78 | { | ||
79 | struct filestr_desc *filestr = &openfiles[fildes]; | ||
80 | |||
81 | if ((unsigned int)fildes >= MAX_OPEN_FILES || filestr->osfd < 0) | ||
82 | filestr = NULL; | ||
83 | else if (filestr->mounted) | ||
84 | return filestr; | ||
85 | |||
86 | errno = filestr ? ENXIO : EBADF; | ||
87 | DEBUGF("fildes %d: %s\n", fildes, strerror(errno)); | ||
88 | return NULL; | ||
89 | } | ||
90 | |||
91 | struct dirstr_desc | ||
92 | { | ||
93 | int osfd; /* Host OS directory file descriptor */ | ||
94 | bool osfd_opened; /* Host fd is another open file */ | ||
95 | OS_DIR_T *osdirp; /* Host OS directory stream */ | ||
96 | #ifdef USE_OSDIRNAME | ||
97 | char *osdirname; /* Host OS directory path */ | ||
98 | #endif | ||
99 | struct sim_dirent entry; /* Rockbox directory entry */ | ||
100 | #ifdef HAVE_MULTIVOLUME | ||
101 | int volume; /* Virtual volume number */ | ||
102 | int volumecounter; /* Counter for root volume entries */ | ||
103 | #endif | ||
104 | bool mounted; /* Is the virtual volume still mounted? */ | ||
105 | } opendirs[MAX_OPEN_DIRS]; | ||
106 | |||
107 | static struct dirstr_desc * alloc_dirstr(void) | ||
108 | { | ||
109 | for (unsigned int i = 0; i < MAX_OPEN_DIRS; i++) | ||
110 | { | ||
111 | struct dirstr_desc *dirstr = &opendirs[i]; | ||
112 | if (dirstr->osdirp == NULL) | ||
113 | return dirstr; | ||
114 | } | ||
115 | |||
116 | return NULL; | ||
117 | } | ||
118 | |||
119 | static struct dirstr_desc * get_dirstr(DIR *dirp) | ||
120 | { | ||
121 | struct dirstr_desc *dirstr = (struct dirstr_desc *)dirp; | ||
122 | |||
123 | if (!PTR_IN_ARRAY(opendirs, dirstr, MAX_OPEN_DIRS) || !dirstr->osdirp) | ||
124 | dirstr = NULL; | ||
125 | else if (dirstr->mounted) | ||
126 | return dirstr; | ||
127 | |||
128 | errno = dirstr ? ENXIO : EBADF; | ||
129 | DEBUGF("dir #%d: %s\n", (int)(dirstr - opendirs), strerror(errno)); | ||
130 | return NULL; | ||
131 | } | ||
132 | |||
133 | static int close_dirstr(struct dirstr_desc *dirstr) | ||
134 | { | ||
135 | OS_DIR_T *osdirp = dirstr->osdirp; | ||
136 | |||
137 | dirstr->mounted = false; | ||
138 | |||
139 | #ifdef USE_OSDIRNAME | ||
140 | free(dirstr->osdirname); | ||
141 | #endif | ||
142 | if (dirstr->osfd_opened) | ||
143 | { | ||
144 | os_close(dirstr->osfd); | ||
145 | dirstr->osfd_opened = false; | ||
146 | } | ||
147 | |||
148 | int rc = os_closedir(osdirp); | ||
149 | dirstr->osdirp = NULL; | ||
150 | |||
151 | return rc; | ||
152 | } | ||
153 | |||
154 | #ifdef HAVE_MULTIVOLUME | ||
155 | static int readdir_volume_inner(struct dirstr_desc *dirstr, | ||
156 | struct sim_dirent *entry) | ||
157 | { | ||
158 | /* Volumes (secondary file systems) get inserted into the system root | ||
159 | * directory. If the path specified volume 0, enumeration will not | ||
160 | * include other volumes, but just its own files and directories. | ||
161 | * | ||
162 | * Fake special directories, which don't really exist, that will get | ||
163 | * redirected upon opendir() | ||
164 | */ | ||
165 | while (++dirstr->volumecounter < NUM_VOLUMES) | ||
166 | { | ||
167 | /* on the system root */ | ||
168 | if (!volume_present(dirstr->volumecounter)) | ||
169 | continue; | ||
170 | |||
171 | get_volume_name(dirstr->volumecounter, entry->d_name); | ||
172 | return 1; | ||
173 | } | ||
174 | |||
175 | /* do normal directory entry fetching */ | ||
176 | return 0; | ||
177 | } | ||
178 | #endif /* HAVE_MULTIVOLUME */ | ||
179 | |||
180 | static inline int readdir_volume(struct dirstr_desc *dirstr, | ||
181 | struct sim_dirent *entry) | ||
182 | { | ||
183 | #ifdef HAVE_MULTIVOLUME | ||
184 | if (dirstr->volumecounter < NUM_VOLUMES) | ||
185 | return readdir_volume_inner(dirstr, entry); | ||
186 | #endif /* HAVE_MULTIVOLUME */ | ||
187 | |||
188 | /* do normal directory entry fetching */ | ||
189 | return 0; | ||
190 | (void)dirstr; (void)entry; | ||
191 | } | ||
192 | |||
193 | |||
194 | /** Internal functions **/ | ||
195 | |||
196 | #ifdef HAVE_MULTIDRIVE | ||
197 | /** | ||
198 | * Handle drive extraction by pretending the files' volumes no longer exist | ||
199 | * and invalidating their I/O for the remainder of their lifetimes as would | ||
200 | * happen on a native target | ||
201 | */ | ||
202 | void sim_ext_extracted(int drive) | ||
203 | { | ||
204 | for (unsigned int i = 0; i < MAX_OPEN_FILES; i++) | ||
205 | { | ||
206 | struct filestr_desc *filestr = &openfiles[i]; | ||
207 | if (filestr->osfd >= 0 && volume_drive(filestr->volume) == drive) | ||
208 | filestr->mounted = false; | ||
209 | } | ||
210 | |||
211 | for (unsigned int i = 0; i < MAX_OPEN_DIRS; i++) | ||
212 | { | ||
213 | struct dirstr_desc *dirstr = &opendirs[i]; | ||
214 | if (dirstr->osdirp && volume_drive(dirstr->volume) == drive) | ||
215 | dirstr->mounted = false; | ||
216 | } | ||
217 | |||
218 | (void)drive; | ||
219 | } | ||
220 | #endif /* HAVE_MULTIDRIVE */ | ||
221 | |||
222 | /** | ||
223 | * Provides target-like path parsing behavior with single and multiple volumes | ||
224 | * while performing minimal transforming of the input. | ||
225 | * | ||
226 | * Paths are sandboxed to the simulated namespace: | ||
227 | * e.g. "{simdir}/foo/../../../../bar" becomes "{simdir}/foo/../bar" | ||
228 | */ | ||
229 | int sim_get_os_path(char *buffer, const char *path, size_t bufsize) | ||
230 | { | ||
231 | #define ADVBUF(amt) \ | ||
232 | ({ buffer += (amt); bufsize -= (amt); }) | ||
233 | |||
234 | #define PPP_SHOWPATHS 0 | ||
235 | |||
236 | if (!path_is_absolute(path)) | ||
237 | { | ||
238 | DEBUGF("ERROR: path is not absolute: \"%s\"\n", path); | ||
239 | errno = ENOENT; | ||
240 | return -1; | ||
241 | } | ||
242 | |||
243 | #if PPP_SHOWPATHS | ||
244 | const char *const buffer0 = buffer; | ||
245 | DEBUGF("PPP (pre): \"%s\"\n", path); | ||
246 | #endif | ||
247 | |||
248 | /* Prepend sim root */ | ||
249 | size_t size = strlcpy(buffer, sim_root_dir, bufsize); | ||
250 | if (size >= bufsize) | ||
251 | { | ||
252 | errno = ENAMETOOLONG; | ||
253 | return -1; | ||
254 | } | ||
255 | ADVBUF(size); | ||
256 | |||
257 | #ifdef HAVE_MULTIVOLUME | ||
258 | /* Track the last valid volume spec encountered */ | ||
259 | int volume = -1; | ||
260 | bool sysroot = true; | ||
261 | |||
262 | /* Basename of sim dir to switch back to simdisk from ext */ | ||
263 | #define DIRBASE_FMT ".." PATH_SEPSTR "%s" | ||
264 | ssize_t dirbase_len = 0; | ||
265 | char dirbase[size + 3 + 1]; | ||
266 | |||
267 | /* Basename of ext directory to switch to alternate volume */ | ||
268 | #define SIMEXT_FMT ".." PATH_SEPSTR "simext%d" | ||
269 | char extbuf[sizeof (SIMEXT_FMT) + 20 + 1]; | ||
270 | #endif /* HAVE_MULTIVOLUME */ | ||
271 | |||
272 | int level = 0; | ||
273 | bool done = false; | ||
274 | while (!done) | ||
275 | { | ||
276 | const char *p; | ||
277 | ssize_t len = parse_path_component(&path, &p); | ||
278 | |||
279 | |||
280 | switch (len) | ||
281 | { | ||
282 | case 0: | ||
283 | done = true; | ||
284 | if (path[-1] != PATH_SEPCH) | ||
285 | continue; | ||
286 | |||
287 | /* Path ends with a separator; preserve that */ | ||
288 | p = &path[-1]; | ||
289 | len = 1; | ||
290 | break; | ||
291 | |||
292 | case 1: | ||
293 | case 2: | ||
294 | if (p[0] == '.') | ||
295 | { | ||
296 | if (len == 1) | ||
297 | break; | ||
298 | |||
299 | if (p[1] == '.') | ||
300 | goto is_dot_dot; | ||
301 | } | ||
302 | |||
303 | default: | ||
304 | level++; | ||
305 | |||
306 | #ifdef HAVE_MULTIVOLUME | ||
307 | if (level != 1) | ||
308 | break; /* Volume spec only valid @ root level */ | ||
309 | |||
310 | const char *next; | ||
311 | volume = path_strip_volume(p, &next, true); | ||
312 | |||
313 | if (next > p) | ||
314 | { | ||
315 | #ifdef HAVE_MULTIDRIVE | ||
316 | /* Feign failure if the volume isn't "mounted" */ | ||
317 | if (!volume_present(volume)) | ||
318 | { | ||
319 | errno = ENXIO; | ||
320 | return -1; | ||
321 | } | ||
322 | #endif /* HAVE_MULTIDRIVE */ | ||
323 | |||
324 | sysroot = false; | ||
325 | |||
326 | if (volume == 0) | ||
327 | continue; | ||
328 | |||
329 | p = extbuf; | ||
330 | len = snprintf(extbuf, sizeof (extbuf), SIMEXT_FMT, volume); | ||
331 | } | ||
332 | #endif /* HAVE_MULTIVOLUME */ | ||
333 | break; | ||
334 | |||
335 | is_dot_dot: | ||
336 | if (level <= 0) | ||
337 | continue; /* Can't go above root; erase */ | ||
338 | |||
339 | level--; | ||
340 | |||
341 | #ifdef HAVE_MULTIVOLUME | ||
342 | if (level == 0) | ||
343 | { | ||
344 | int v = volume; | ||
345 | bool sr = sysroot; | ||
346 | volume = -1; | ||
347 | sysroot = true; | ||
348 | |||
349 | if (v <= 0) | ||
350 | { | ||
351 | if (sr) | ||
352 | break; | ||
353 | |||
354 | continue; | ||
355 | } | ||
356 | |||
357 | /* Going up from a volume root and back down to the sys root */ | ||
358 | if (dirbase_len == 0) | ||
359 | { | ||
360 | /* Get the main simdisk directory so it can be reentered */ | ||
361 | char tmpbuf[sizeof (dirbase)]; | ||
362 | #ifdef WIN32 | ||
363 | path_correct_separators(tmpbuf, sim_root_dir); | ||
364 | path_strip_drive(tmpbuf, &p, false); | ||
365 | #else | ||
366 | p = tmpbuf; | ||
367 | strcpy(tmpbuf, sim_root_dir); | ||
368 | #endif | ||
369 | size = path_basename(p, &p); | ||
370 | ((char *)p)[size] = '\0'; | ||
371 | |||
372 | if (size == 0 || is_dotdir_name(p)) | ||
373 | { | ||
374 | /* This is nonsense and won't work */ | ||
375 | DEBUGF("ERROR: sim root dir basname is dotdir or" | ||
376 | " empty: \"%s\"\n", sim_root_dir); | ||
377 | errno = ENOENT; | ||
378 | return -1; | ||
379 | } | ||
380 | |||
381 | dirbase_len = snprintf(dirbase, sizeof (dirbase), | ||
382 | DIRBASE_FMT, p); | ||
383 | } | ||
384 | |||
385 | p = dirbase; | ||
386 | len = dirbase_len; | ||
387 | break; | ||
388 | } | ||
389 | #endif /* HAVE_MULTIVOLUME */ | ||
390 | break; | ||
391 | } /* end switch */ | ||
392 | |||
393 | char compname[len + 1]; | ||
394 | strmemcpy(compname, p, len); | ||
395 | |||
396 | size = path_append(buffer, PA_SEP_HARD, compname, bufsize); | ||
397 | if (size >= bufsize) | ||
398 | { | ||
399 | errno = ENAMETOOLONG; | ||
400 | return -1; | ||
401 | } | ||
402 | ADVBUF(size); | ||
403 | } | ||
404 | |||
405 | #if PPP_SHOWPATHS | ||
406 | DEBUGF("PPP (post): \"%s\"" IF_MV(" vol:%d") "\n", | ||
407 | buffer0 IF_MV(, volume)); | ||
408 | #endif | ||
409 | |||
410 | return IF_MV(volume) +1; | ||
411 | } | ||
412 | |||
413 | |||
414 | /** File functions **/ | ||
415 | |||
416 | int sim_open(const char *path, int oflag, ...) | ||
417 | { | ||
418 | int fildes; | ||
419 | struct filestr_desc *filestr = alloc_filestr(&fildes); | ||
420 | if (!filestr) | ||
421 | { | ||
422 | errno = EMFILE; | ||
423 | return -1; | ||
424 | } | ||
425 | |||
426 | char ospath[SIM_TMPBUF_MAX_PATH]; | ||
427 | int pprc = sim_get_os_path(ospath, path, sizeof (ospath)); | ||
428 | if (pprc < 0) | ||
429 | return -2; | ||
430 | |||
431 | filestr->osfd = os_open(ospath, oflag | O_BINARY __OPEN_MODE_ARG); | ||
432 | if (filestr->osfd < 0) | ||
433 | return -3; | ||
434 | |||
435 | #ifdef HAVE_MULTIVOLUME | ||
436 | filestr->volume = MAX(pprc - 1, 0); | ||
437 | #endif | ||
438 | filestr->mounted = true; | ||
439 | return fildes; | ||
440 | } | ||
441 | |||
442 | int sim_creat(const char *path, mode_t mode) | ||
443 | { | ||
444 | return sim_open(path, O_WRONLY|O_CREAT|O_TRUNC, mode); | ||
445 | } | ||
446 | |||
447 | int sim_close(int fildes) | ||
448 | { | ||
449 | struct filestr_desc *filestr = &openfiles[fildes]; | ||
450 | if ((unsigned int)fildes >= MAX_OPEN_FILES || filestr->osfd < 0) | ||
451 | { | ||
452 | errno = EBADF; | ||
453 | return -1; | ||
454 | } | ||
455 | |||
456 | int osfd = filestr->osfd; | ||
457 | filestr->osfd = -1; | ||
458 | return os_close(osfd); | ||
459 | } | ||
460 | |||
461 | int sim_ftruncate(int fildes, off_t length) | ||
462 | { | ||
463 | struct filestr_desc *filestr = get_filestr(fildes); | ||
464 | if (!filestr) | ||
465 | return -1; | ||
466 | |||
467 | off_t size = os_filesize(filestr->osfd); | ||
468 | if (size < 0) | ||
469 | return -1; | ||
470 | |||
471 | if (length >= size) | ||
472 | return 0; | ||
473 | |||
474 | int rc = os_ftruncate(filestr->osfd, length); | ||
475 | |||
476 | #ifdef HAVE_DIRCACHE | ||
477 | if (rc >= 0) | ||
478 | dircache_ftruncate(xxxxxx); | ||
479 | #endif | ||
480 | |||
481 | return rc; | ||
482 | } | ||
483 | |||
484 | int sim_fsync(int fildes) | ||
485 | { | ||
486 | struct filestr_desc *filestr = get_filestr(fildes); | ||
487 | if (!filestr) | ||
488 | return -1; | ||
489 | |||
490 | int rc = os_fsync(filestr->osfd); | ||
491 | |||
492 | #ifdef HAVE_DIRCACHE | ||
493 | if (rc >= 0) | ||
494 | dircache_fsync(xxxxxx); | ||
495 | #endif | ||
496 | |||
497 | return rc; | ||
498 | } | ||
499 | |||
500 | off_t sim_lseek(int fildes, off_t offset, int whence) | ||
501 | { | ||
502 | struct filestr_desc *filestr = get_filestr(fildes); | ||
503 | if (!filestr) | ||
504 | return -1; | ||
505 | |||
506 | return os_lseek(filestr->osfd, offset, whence); | ||
507 | } | ||
508 | |||
509 | ssize_t sim_read(int fildes, void *buf, size_t nbyte) | ||
510 | { | ||
511 | struct filestr_desc *filestr = get_filestr(fildes); | ||
512 | if (!filestr) | ||
513 | return -1; | ||
514 | |||
515 | return os_read(filestr->osfd, buf, nbyte); | ||
516 | } | ||
517 | |||
518 | ssize_t sim_write(int fildes, const void *buf, size_t nbyte) | ||
519 | { | ||
520 | struct filestr_desc *filestr = get_filestr(fildes); | ||
521 | if (!filestr) | ||
522 | return -1; | ||
523 | |||
524 | return os_write(filestr->osfd, buf, nbyte); | ||
525 | } | ||
526 | |||
527 | int sim_remove(const char *path) | ||
528 | { | ||
529 | char ospath[SIM_TMPBUF_MAX_PATH]; | ||
530 | if (sim_get_os_path(ospath, path, sizeof (ospath)) < 0) | ||
531 | return -1; | ||
532 | |||
533 | int rc = os_remove(ospath); | ||
534 | |||
535 | #ifdef HAVE_DIRCACHE | ||
536 | if (rc >= 0) | ||
537 | dircache_remove(xxxxxx); | ||
538 | #endif | ||
539 | |||
540 | return rc; | ||
541 | } | ||
542 | |||
543 | int sim_rename(const char *old, const char *new) | ||
544 | { | ||
545 | char osold[SIM_TMPBUF_MAX_PATH]; | ||
546 | int pprc1 = sim_get_os_path(osold, old, sizeof (osold)); | ||
547 | if (pprc1 < 0) | ||
548 | return -1; | ||
549 | |||
550 | char osnew[SIM_TMPBUF_MAX_PATH]; | ||
551 | int pprc2 = sim_get_os_path(osnew, new, sizeof (osnew)); | ||
552 | if (pprc2 < 0) | ||
553 | return -1; | ||
554 | |||
555 | if (MAX(pprc1 - 1, 0) != MAX(pprc2 - 1, 0)) | ||
556 | { | ||
557 | /* Pretend they're different devices */ | ||
558 | errno = EXDEV; | ||
559 | return -1; | ||
560 | } | ||
561 | |||
562 | int rc = os_rename(osold, osnew); | ||
563 | |||
564 | #ifdef HAVE_DIRCACHE | ||
565 | if (rc >= 0) | ||
566 | dircache_rename(xxxxxx); | ||
567 | #endif | ||
568 | |||
569 | return rc; | ||
570 | } | ||
571 | |||
572 | off_t sim_filesize(int fildes) | ||
573 | { | ||
574 | struct filestr_desc *filestr = get_filestr(fildes); | ||
575 | if (!filestr) | ||
576 | return -1; | ||
577 | |||
578 | return os_filesize(filestr->osfd); | ||
579 | } | ||
580 | |||
581 | int sim_fsamefile(int fildes1, int fildes2) | ||
582 | { | ||
583 | struct filestr_desc *filestr1 = get_filestr(fildes1); | ||
584 | if (!filestr1) | ||
585 | return -1; | ||
586 | |||
587 | struct filestr_desc *filestr2 = get_filestr(fildes2); | ||
588 | if (!filestr2) | ||
589 | return -1; | ||
590 | |||
591 | if (filestr1 == filestr2) | ||
592 | return 1; | ||
593 | |||
594 | return os_fsamefile(filestr1->osfd, filestr2->osfd); | ||
595 | } | ||
596 | |||
597 | int sim_relate(const char *path1, const char *path2) | ||
598 | { | ||
599 | char ospath1[SIM_TMPBUF_MAX_PATH]; | ||
600 | if (sim_get_os_path(ospath1, path1, sizeof (ospath1)) < 0) | ||
601 | return -1; | ||
602 | |||
603 | char ospath2[SIM_TMPBUF_MAX_PATH]; | ||
604 | if (sim_get_os_path(ospath2, path2, sizeof (ospath2)) < 0) | ||
605 | return -1; | ||
606 | |||
607 | return os_relate(ospath1, ospath2); | ||
608 | } | ||
609 | |||
610 | bool sim_file_exists(const char *path) | ||
611 | { | ||
612 | char ospath[SIM_TMPBUF_MAX_PATH]; | ||
613 | if (sim_get_os_path(ospath, path, sizeof (ospath)) < 0) | ||
614 | return false; | ||
615 | |||
616 | return os_file_exists(ospath); | ||
617 | } | ||
618 | |||
619 | |||
620 | /** Directory functions **/ | ||
621 | DIR * sim_opendir(const char *dirname) | ||
622 | { | ||
623 | struct dirstr_desc *dirstr = alloc_dirstr(); | ||
624 | if (!dirstr) | ||
625 | { | ||
626 | errno = EMFILE; | ||
627 | return NULL; | ||
628 | } | ||
629 | |||
630 | char osdirname[SIM_TMPBUF_MAX_PATH]; | ||
631 | int pprc = sim_get_os_path(osdirname, dirname, sizeof (osdirname)); | ||
632 | if (pprc < 0) | ||
633 | return NULL; | ||
634 | |||
635 | int rc = os_opendir_and_fd(osdirname, &dirstr->osdirp, &dirstr->osfd); | ||
636 | if (rc < 0) | ||
637 | return NULL; | ||
638 | |||
639 | dirstr->osfd_opened = rc > 0; | ||
640 | |||
641 | #ifdef USE_OSDIRNAME | ||
642 | dirstr->osdirname = strdup(osdirname); | ||
643 | if (!dirstr->osdirname) | ||
644 | { | ||
645 | close_dirstr(dirstr); | ||
646 | return NULL; | ||
647 | } | ||
648 | #endif | ||
649 | |||
650 | dirstr->entry.d_name[0] = 0; /* Mark as invalid */ | ||
651 | #ifdef HAVE_MULTIVOLUME | ||
652 | dirstr->volume = MAX(pprc - 1, 0); | ||
653 | dirstr->volumecounter = pprc == 0 ? 0 : INT_MAX; | ||
654 | #endif | ||
655 | dirstr->mounted = true; | ||
656 | return (DIR *)dirstr; /* A-Okay */ | ||
657 | } | ||
658 | |||
659 | int sim_closedir(DIR *dirp) | ||
660 | { | ||
661 | struct dirstr_desc *dirstr = (struct dirstr_desc *)dirp; | ||
662 | if (!PTR_IN_ARRAY(opendirs, dirstr, MAX_OPEN_DIRS) || !dirstr->osdirp) | ||
663 | { | ||
664 | errno = EBADF; | ||
665 | return -1; | ||
666 | } | ||
667 | |||
668 | return close_dirstr(dirstr); | ||
669 | } | ||
670 | |||
671 | struct sim_dirent * sim_readdir(DIR *dirp) | ||
672 | { | ||
673 | struct dirstr_desc *dirstr = get_dirstr(dirp); | ||
674 | if (!dirstr) | ||
675 | return NULL; | ||
676 | |||
677 | struct sim_dirent *entry = &dirstr->entry; | ||
678 | entry->info.osdirent = NULL; | ||
679 | |||
680 | if (readdir_volume(dirstr, entry)) | ||
681 | return entry; | ||
682 | |||
683 | OS_DIRENT_T *osdirent = os_readdir(dirstr->osdirp); | ||
684 | if (!osdirent) | ||
685 | return NULL; | ||
686 | |||
687 | size_t size = sizeof (entry->d_name); | ||
688 | if (strlcpy_from_os(entry->d_name, osdirent->d_name, size) >= size) | ||
689 | FILE_ERROR_RETURN(ENAMETOOLONG, NULL); | ||
690 | |||
691 | entry->info.osdirent = osdirent; | ||
692 | return entry; | ||
693 | } | ||
694 | |||
695 | int sim_mkdir(const char *path) | ||
696 | { | ||
697 | char ospath[SIM_TMPBUF_MAX_PATH]; | ||
698 | if (sim_get_os_path(ospath, path, sizeof (ospath)) < 0) | ||
699 | return -1; | ||
700 | |||
701 | int rc = os_mkdir(ospath __MKDIR_MODE_ARG); | ||
702 | |||
703 | #ifdef HAVE_DIRCACHE | ||
704 | if (rc >= 0) | ||
705 | dircache_mkdir(xxxxxx); | ||
706 | #endif | ||
707 | |||
708 | return rc; | ||
709 | } | ||
710 | |||
711 | int sim_rmdir(const char *path) | ||
712 | { | ||
713 | char ospath[SIM_TMPBUF_MAX_PATH]; | ||
714 | if (sim_get_os_path(ospath, path, sizeof (ospath)) < 0) | ||
715 | return -1; | ||
716 | |||
717 | int rc = os_rmdir(ospath); | ||
718 | |||
719 | #ifdef HAVE_DIRCACHE | ||
720 | if (rc >= 0) | ||
721 | dircache_rmdir(xxxxxx); | ||
722 | #endif | ||
723 | |||
724 | return rc; | ||
725 | } | ||
726 | |||
727 | int sim_samedir(DIR *dirp1, DIR *dirp2) | ||
728 | { | ||
729 | struct dirstr_desc *dirstr1 = get_dirstr(dirp1); | ||
730 | if (!dirstr1) | ||
731 | return -1; | ||
732 | |||
733 | struct dirstr_desc *dirstr2 = get_dirstr(dirp2); | ||
734 | if (!dirstr2) | ||
735 | return -1; | ||
736 | |||
737 | return os_fsamefile(dirstr1->osfd, dirstr2->osfd); | ||
738 | } | ||
739 | |||
740 | bool sim_dir_exists(const char *dirname) | ||
741 | { | ||
742 | char osdirname[SIM_TMPBUF_MAX_PATH]; | ||
743 | if (sim_get_os_path(osdirname, dirname, sizeof (osdirname)) < 0) | ||
744 | return false; | ||
745 | |||
746 | OS_DIR_T *dirp = os_opendir(osdirname); | ||
747 | if (!dirp) | ||
748 | return false; | ||
749 | |||
750 | os_closedir(dirp); | ||
751 | return true; | ||
752 | } | ||
753 | |||
754 | struct dirinfo dir_get_info(DIR *dirp, struct sim_dirent *entry) | ||
755 | { | ||
756 | int rc; | ||
757 | struct dirstr_desc *dirstr = get_dirstr(dirp); | ||
758 | if (!dirstr) | ||
759 | FILE_ERROR(ERRNO, RC); | ||
760 | |||
761 | if (entry->d_name[0] == 0) | ||
762 | FILE_ERROR(ENOENT, RC); | ||
763 | |||
764 | OS_DIRENT_T *osdirent = entry->info.osdirent; | ||
765 | if (osdirent == NULL) | ||
766 | return (struct dirinfo){ .attribute = ATTR_MOUNT_POINT }; | ||
767 | |||
768 | struct dirinfo info; | ||
769 | info.attribute = 0; | ||
770 | |||
771 | OS_STAT_T s; | ||
772 | |||
773 | #ifdef os_fstatat | ||
774 | if (os_fstatat(dirstr->osfd, entry->d_name, &s, 0)) | ||
775 | #else /* no fstatat; build file name for stat() */ | ||
776 | char statpath[SIM_TMPBUF_MAX_PATH]; | ||
777 | if (path_append(statpath, dirstr->osdirname, entry->d_name, | ||
778 | sizeof (statpath)) >= sizeof (statpath)) | ||
779 | { | ||
780 | FILE_ERROR(ENAMETOOLONG, RC); | ||
781 | } | ||
782 | |||
783 | if (os_stat(statpath, &s)) /* get info */ | ||
784 | #endif /* os_fstatat */ | ||
785 | { | ||
786 | /* File size larger than 2 GB? */ | ||
787 | #ifdef EOVERFLOW | ||
788 | if (errno == EOVERFLOW) | ||
789 | DEBUGF("stat overflow for \"%s\"\n", entry->d_name); | ||
790 | #endif | ||
791 | FILE_ERROR(ERRNO, RC); | ||
792 | } | ||
793 | |||
794 | if (S_ISDIR(s.st_mode)) | ||
795 | info.attribute |= ATTR_DIRECTORY; | ||
796 | |||
797 | info.size = s.st_size; | ||
798 | |||
799 | struct tm tm; | ||
800 | if (localtime_r(&s.st_mtime, &tm) == NULL) | ||
801 | FILE_ERROR(ERRNO, RC); | ||
802 | |||
803 | info.mtime = mktime(&tm); | ||
804 | |||
805 | return info; | ||
806 | |||
807 | file_error: | ||
808 | return (struct dirinfo){ .size = 0 }; | ||
809 | } | ||
810 | |||
811 | int os_volume_path(IF_MV(int volume, ) char *buffer, size_t bufsize) | ||
812 | { | ||
813 | if (!buffer || !bufsize IF_MV( || !volume_present(volume) )) | ||
814 | return -1; | ||
815 | |||
816 | char tmpbuf[SIM_TMPBUF_MAX_PATH]; | ||
817 | tmpbuf[0] = '\0'; | ||
818 | |||
819 | #ifdef HAVE_MULTIVOLUME | ||
820 | char volname[VOL_MAX_LEN + 1]; | ||
821 | get_volume_name(volume, volname); | ||
822 | |||
823 | if (path_append(tmpbuf, PA_SEP_HARD, volname, sizeof (volname)) | ||
824 | >= sizeof (volname)) | ||
825 | return -1; | ||
826 | #endif /* HAVE_MULTIVOLUME */ | ||
827 | |||
828 | if (path_append(tmpbuf, PA_SEP_HARD, ".", sizeof (tmpbuf)) | ||
829 | >= sizeof (tmpbuf)) | ||
830 | return -1; | ||
831 | |||
832 | return sim_get_os_path(buffer, tmpbuf, bufsize); | ||
833 | } | ||