summaryrefslogtreecommitdiff
path: root/uisimulator/common/filesystem-sim.c
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2013-08-05 22:02:45 -0400
committerMichael Sevakis <jethead71@rockbox.org>2014-08-30 03:48:23 +0200
commit7d1a47cf13726c95ac46027156cc12dd9da5b855 (patch)
treeeb20d07656806479a8e1fea25887a490ea30d1d8 /uisimulator/common/filesystem-sim.c
parent95a4c3afcd53a1f8b835dec33de51f9c304de4d9 (diff)
downloadrockbox-7d1a47cf13726c95ac46027156cc12dd9da5b855.tar.gz
rockbox-7d1a47cf13726c95ac46027156cc12dd9da5b855.zip
Rewrite filesystem code (WIP)
This patch redoes the filesystem code from the FAT driver up to the clipboard code in onplay.c. Not every aspect of this is finished therefore it is still "WIP". I don't wish to do too much at once (haha!). What is left to do is get dircache back in the sim and find an implementation for the dircache indicies in the tagcache and playlist code or do something else that has the same benefit. Leaving these out for now does not make anything unusable. All the basics are done. Phone app code should probably get vetted (and app path handling just plain rewritten as environment expansions); the SDL app and Android run well. Main things addressed: 1) Thread safety: There is none right now in the trunk code. Most of what currently works is luck when multiple threads are involved or multiple descriptors to the same file are open. 2) POSIX compliance: Many of the functions behave nothing like their counterparts on a host system. This leads to inconsistent code or very different behavior from native to hosted. One huge offender was rename(). Going point by point would fill a book. 3) Actual running RAM usage: Many targets will use less RAM and less stack space (some more RAM because I upped the number of cache buffers for large memory). There's very little memory lying fallow in rarely-used areas (see 'Key core changes' below). Also, all targets may open the same number of directory streams whereas before those with less than 8MB RAM were limited to 8, not 12 implying those targets will save slightly less. 4) Performance: The test_disk plugin shows markedly improved performance, particularly in the area of (uncached) directory scanning, due partly to more optimal directory reading and to a better sector cache algorithm. Uncached times tend to be better while there is a bit of a slowdown in dircache due to it being a bit heavier of an implementation. It's not noticeable by a human as far as I can say. Key core changes: 1) Files and directories share core code and data structures. 2) The filesystem code knows which descriptors refer to same file. This ensures that changes from one stream are appropriately reflected in every open descriptor for that file (fileobj_mgr.c). 3) File and directory cache buffers are borrowed from the main sector cache. This means that when they are not in use by a file, they are not wasted, but used for the cache. Most of the time, only a few of them are needed. It also means that adding more file and directory handles is less expensive. All one must do in ensure a large enough cache to borrow from. 4) Relative path components are supported and the namespace is unified. It does not support full relative paths to an implied current directory; what is does support is use of "." and "..". Adding the former would not be very difficult. The namespace is unified in the sense that volumes may be specified several times along with relative parts, e.g.: "/<0>/foo/../../<1>/bar" :<=> "/<1>/bar". 5) Stack usage is down due to sharing of data, static allocation and less duplication of strings on the stack. This requires more serialization than I would like but since the number of threads is limited to a low number, the tradoff in favor of the stack seems reasonable. 6) Separates and heirarchicalizes (sic) the SIM and APP filesystem code. SIM path and volume handling is just like the target. Some aspects of the APP file code get more straightforward (e.g. no path hashing is needed). Dircache: Deserves its own section. Dircache is new but pays homage to the old. The old one was not compatible and so it, since it got redone, does all the stuff it always should have done such as: 1) It may be update and used at any time during the build process. No longer has one to wait for it to finish building to do basic file management (create, remove, rename, etc.). 2) It does not need to be either fully scanned or completely disabled; it can be incomplete (i.e. overfilled, missing paths), still be of benefit and be correct. 3) Handles mounting and dismounting of individual volumes which means a full rebuild is not needed just because you pop a new SD card in the slot. Now, because it reuses its freed entry data, may rebuild only that volume. 4) Much more fundamental to the file code. When it is built, it is the keeper of the master file list whether enabled or not ("disabled" is just a state of the cache). Its must always to ready to be started and bind all streams opened prior to being enabled. 5) Maintains any short filenames in OEM format which means that it does not need to be rebuilt when changing the default codepage. Miscellaneous Compatibility: 1) Update any other code that would otherwise not work such as the hotswap mounting code in various card drivers. 2) File management: Clipboard needed updating because of the behavioral changes. Still needs a little more work on some finer points. 3) Remove now-obsolete functionality such as the mutex's "no preempt" flag (which was only for the prior FAT driver). 4) struct dirinfo uses time_t rather than raw FAT directory entry time fields. I plan to follow up on genericizing everything there (i.e. no FAT attributes). 5) unicode.c needed some redoing so that the file code does not try try to load codepages during a scan, which is actually a problem with the current code. The default codepage, if any is required, is now kept in RAM separarately (bufalloced) from codepages specified to iso_decode() (which must not be bufalloced because the conversion may be done by playback threads). Brings with it some additional reusable core code: 1) Revised file functions: Reusable code that does things such as safe path concatenation and parsing without buffer limitations or data duplication. Variants that copy or alter the input path may be based off these. To do: 1) Put dircache functionality back in the sim. Treating it internally as a different kind of file system seems the best approach at this time. 2) Restore use of dircache indexes in the playlist and database or something effectively the same. Since the cache doesn't have to be complete in order to be used, not getting a hit on the cache doesn't unambiguously say if the path exists or not. Change-Id: Ia30f3082a136253e3a0eae0784e3091d138915c8 Reviewed-on: http://gerrit.rockbox.org/566 Reviewed-by: Michael Sevakis <jethead71@rockbox.org> Tested: Michael Sevakis <jethead71@rockbox.org>
Diffstat (limited to 'uisimulator/common/filesystem-sim.c')
-rw-r--r--uisimulator/common/filesystem-sim.c833
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
42extern 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
50struct 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
62static 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
77static 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
91struct 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
107static 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
119static 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
133static 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
155static 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
180static 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 */
202void 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 */
229int 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
416int 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
442int sim_creat(const char *path, mode_t mode)
443{
444 return sim_open(path, O_WRONLY|O_CREAT|O_TRUNC, mode);
445}
446
447int 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
461int 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
484int 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
500off_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
509ssize_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
518ssize_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
527int 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
543int 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
572off_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
581int 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
597int 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
610bool 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 **/
621DIR * 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
659int 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
671struct 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
695int 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
711int 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
727int 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
740bool 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
754struct 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
807file_error:
808 return (struct dirinfo){ .size = 0 };
809}
810
811int 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}