summaryrefslogtreecommitdiff
path: root/uisimulator/common/filesystem-sim.c
diff options
context:
space:
mode:
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}