834 lines
20 KiB
C
834 lines
20 KiB
C
|
/***************************************************************************
|
||
|
* __________ __ ___.
|
||
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||
|
* \/ \/ \/ \/ \/
|
||
|
* $Id$
|
||
|
*
|
||
|
* Copyright (C) 2002 Daniel Stenberg
|
||
|
* Copyright (C) 2014 Michael Sevakis
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU General Public License
|
||
|
* as published by the Free Software Foundation; either version 2
|
||
|
* of the License, or (at your option) any later version.
|
||
|
*
|
||
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||
|
* KIND, either express or implied.
|
||
|
*
|
||
|
****************************************************************************/
|
||
|
#define RB_FILESYSTEM_OS
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <stdarg.h>
|
||
|
#include <time.h>
|
||
|
#include <errno.h>
|
||
|
#include <limits.h>
|
||
|
#include "config.h"
|
||
|
#include "system.h"
|
||
|
#include "file.h"
|
||
|
#include "dir.h"
|
||
|
#include "file_internal.h"
|
||
|
#include "pathfuncs.h"
|
||
|
#include "string-extra.h"
|
||
|
#include "debug.h"
|
||
|
|
||
|
#ifndef os_fstatat
|
||
|
#define USE_OSDIRNAME /* we need to remember the open directory path */
|
||
|
#endif
|
||
|
|
||
|
extern const char *sim_root_dir;
|
||
|
|
||
|
/* Windows (and potentially other OSes) distinguish binary and text files.
|
||
|
* Define a dummy for the others. */
|
||
|
#ifndef O_BINARY
|
||
|
#define O_BINARY 0
|
||
|
#endif
|
||
|
|
||
|
struct filestr_desc
|
||
|
{
|
||
|
int osfd; /* The host OS file descriptor */
|
||
|
bool mounted; /* Is host volume still mounted? */
|
||
|
#ifdef HAVE_MULTIVOLUME
|
||
|
int volume; /* The virtual volume number */
|
||
|
#endif
|
||
|
} openfiles[MAX_OPEN_FILES] =
|
||
|
{
|
||
|
[0 ... MAX_OPEN_FILES-1] = { .osfd = -1 }
|
||
|
};
|
||
|
|
||
|
static struct filestr_desc * alloc_filestr(int *fildesp)
|
||
|
{
|
||
|
for (unsigned int i = 0; i < MAX_OPEN_FILES; i++)
|
||
|
{
|
||
|
struct filestr_desc *filestr = &openfiles[i];
|
||
|
if (filestr->osfd < 0)
|
||
|
{
|
||
|
*fildesp = i;
|
||
|
return filestr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static struct filestr_desc * get_filestr(int fildes)
|
||
|
{
|
||
|
struct filestr_desc *filestr = &openfiles[fildes];
|
||
|
|
||
|
if ((unsigned int)fildes >= MAX_OPEN_FILES || filestr->osfd < 0)
|
||
|
filestr = NULL;
|
||
|
else if (filestr->mounted)
|
||
|
return filestr;
|
||
|
|
||
|
errno = filestr ? ENXIO : EBADF;
|
||
|
DEBUGF("fildes %d: %s\n", fildes, strerror(errno));
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
struct dirstr_desc
|
||
|
{
|
||
|
int osfd; /* Host OS directory file descriptor */
|
||
|
bool osfd_opened; /* Host fd is another open file */
|
||
|
OS_DIR_T *osdirp; /* Host OS directory stream */
|
||
|
#ifdef USE_OSDIRNAME
|
||
|
char *osdirname; /* Host OS directory path */
|
||
|
#endif
|
||
|
struct sim_dirent entry; /* Rockbox directory entry */
|
||
|
#ifdef HAVE_MULTIVOLUME
|
||
|
int volume; /* Virtual volume number */
|
||
|
int volumecounter; /* Counter for root volume entries */
|
||
|
#endif
|
||
|
bool mounted; /* Is the virtual volume still mounted? */
|
||
|
} opendirs[MAX_OPEN_DIRS];
|
||
|
|
||
|
static struct dirstr_desc * alloc_dirstr(void)
|
||
|
{
|
||
|
for (unsigned int i = 0; i < MAX_OPEN_DIRS; i++)
|
||
|
{
|
||
|
struct dirstr_desc *dirstr = &opendirs[i];
|
||
|
if (dirstr->osdirp == NULL)
|
||
|
return dirstr;
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static struct dirstr_desc * get_dirstr(DIR *dirp)
|
||
|
{
|
||
|
struct dirstr_desc *dirstr = (struct dirstr_desc *)dirp;
|
||
|
|
||
|
if (!PTR_IN_ARRAY(opendirs, dirstr, MAX_OPEN_DIRS) || !dirstr->osdirp)
|
||
|
dirstr = NULL;
|
||
|
else if (dirstr->mounted)
|
||
|
return dirstr;
|
||
|
|
||
|
errno = dirstr ? ENXIO : EBADF;
|
||
|
DEBUGF("dir #%d: %s\n", (int)(dirstr - opendirs), strerror(errno));
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static int close_dirstr(struct dirstr_desc *dirstr)
|
||
|
{
|
||
|
OS_DIR_T *osdirp = dirstr->osdirp;
|
||
|
|
||
|
dirstr->mounted = false;
|
||
|
|
||
|
#ifdef USE_OSDIRNAME
|
||
|
free(dirstr->osdirname);
|
||
|
#endif
|
||
|
if (dirstr->osfd_opened)
|
||
|
{
|
||
|
os_close(dirstr->osfd);
|
||
|
dirstr->osfd_opened = false;
|
||
|
}
|
||
|
|
||
|
int rc = os_closedir(osdirp);
|
||
|
dirstr->osdirp = NULL;
|
||
|
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
#ifdef HAVE_MULTIVOLUME
|
||
|
static int readdir_volume_inner(struct dirstr_desc *dirstr,
|
||
|
struct sim_dirent *entry)
|
||
|
{
|
||
|
/* Volumes (secondary file systems) get inserted into the system root
|
||
|
* directory. If the path specified volume 0, enumeration will not
|
||
|
* include other volumes, but just its own files and directories.
|
||
|
*
|
||
|
* Fake special directories, which don't really exist, that will get
|
||
|
* redirected upon opendir()
|
||
|
*/
|
||
|
while (++dirstr->volumecounter < NUM_VOLUMES)
|
||
|
{
|
||
|
/* on the system root */
|
||
|
if (!volume_present(dirstr->volumecounter))
|
||
|
continue;
|
||
|
|
||
|
get_volume_name(dirstr->volumecounter, entry->d_name);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/* do normal directory entry fetching */
|
||
|
return 0;
|
||
|
}
|
||
|
#endif /* HAVE_MULTIVOLUME */
|
||
|
|
||
|
static inline int readdir_volume(struct dirstr_desc *dirstr,
|
||
|
struct sim_dirent *entry)
|
||
|
{
|
||
|
#ifdef HAVE_MULTIVOLUME
|
||
|
if (dirstr->volumecounter < NUM_VOLUMES)
|
||
|
return readdir_volume_inner(dirstr, entry);
|
||
|
#endif /* HAVE_MULTIVOLUME */
|
||
|
|
||
|
/* do normal directory entry fetching */
|
||
|
return 0;
|
||
|
(void)dirstr; (void)entry;
|
||
|
}
|
||
|
|
||
|
|
||
|
/** Internal functions **/
|
||
|
|
||
|
#ifdef HAVE_MULTIDRIVE
|
||
|
/**
|
||
|
* Handle drive extraction by pretending the files' volumes no longer exist
|
||
|
* and invalidating their I/O for the remainder of their lifetimes as would
|
||
|
* happen on a native target
|
||
|
*/
|
||
|
void sim_ext_extracted(int drive)
|
||
|
{
|
||
|
for (unsigned int i = 0; i < MAX_OPEN_FILES; i++)
|
||
|
{
|
||
|
struct filestr_desc *filestr = &openfiles[i];
|
||
|
if (filestr->osfd >= 0 && volume_drive(filestr->volume) == drive)
|
||
|
filestr->mounted = false;
|
||
|
}
|
||
|
|
||
|
for (unsigned int i = 0; i < MAX_OPEN_DIRS; i++)
|
||
|
{
|
||
|
struct dirstr_desc *dirstr = &opendirs[i];
|
||
|
if (dirstr->osdirp && volume_drive(dirstr->volume) == drive)
|
||
|
dirstr->mounted = false;
|
||
|
}
|
||
|
|
||
|
(void)drive;
|
||
|
}
|
||
|
#endif /* HAVE_MULTIDRIVE */
|
||
|
|
||
|
/**
|
||
|
* Provides target-like path parsing behavior with single and multiple volumes
|
||
|
* while performing minimal transforming of the input.
|
||
|
*
|
||
|
* Paths are sandboxed to the simulated namespace:
|
||
|
* e.g. "{simdir}/foo/../../../../bar" becomes "{simdir}/foo/../bar"
|
||
|
*/
|
||
|
int sim_get_os_path(char *buffer, const char *path, size_t bufsize)
|
||
|
{
|
||
|
#define ADVBUF(amt) \
|
||
|
({ buffer += (amt); bufsize -= (amt); })
|
||
|
|
||
|
#define PPP_SHOWPATHS 0
|
||
|
|
||
|
if (!path_is_absolute(path))
|
||
|
{
|
||
|
DEBUGF("ERROR: path is not absolute: \"%s\"\n", path);
|
||
|
errno = ENOENT;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
#if PPP_SHOWPATHS
|
||
|
const char *const buffer0 = buffer;
|
||
|
DEBUGF("PPP (pre): \"%s\"\n", path);
|
||
|
#endif
|
||
|
|
||
|
/* Prepend sim root */
|
||
|
size_t size = strlcpy(buffer, sim_root_dir, bufsize);
|
||
|
if (size >= bufsize)
|
||
|
{
|
||
|
errno = ENAMETOOLONG;
|
||
|
return -1;
|
||
|
}
|
||
|
ADVBUF(size);
|
||
|
|
||
|
#ifdef HAVE_MULTIVOLUME
|
||
|
/* Track the last valid volume spec encountered */
|
||
|
int volume = -1;
|
||
|
bool sysroot = true;
|
||
|
|
||
|
/* Basename of sim dir to switch back to simdisk from ext */
|
||
|
#define DIRBASE_FMT ".." PATH_SEPSTR "%s"
|
||
|
ssize_t dirbase_len = 0;
|
||
|
char dirbase[size + 3 + 1];
|
||
|
|
||
|
/* Basename of ext directory to switch to alternate volume */
|
||
|
#define SIMEXT_FMT ".." PATH_SEPSTR "simext%d"
|
||
|
char extbuf[sizeof (SIMEXT_FMT) + 20 + 1];
|
||
|
#endif /* HAVE_MULTIVOLUME */
|
||
|
|
||
|
int level = 0;
|
||
|
bool done = false;
|
||
|
while (!done)
|
||
|
{
|
||
|
const char *p;
|
||
|
ssize_t len = parse_path_component(&path, &p);
|
||
|
|
||
|
|
||
|
switch (len)
|
||
|
{
|
||
|
case 0:
|
||
|
done = true;
|
||
|
if (path[-1] != PATH_SEPCH)
|
||
|
continue;
|
||
|
|
||
|
/* Path ends with a separator; preserve that */
|
||
|
p = &path[-1];
|
||
|
len = 1;
|
||
|
break;
|
||
|
|
||
|
case 1:
|
||
|
case 2:
|
||
|
if (p[0] == '.')
|
||
|
{
|
||
|
if (len == 1)
|
||
|
break;
|
||
|
|
||
|
if (p[1] == '.')
|
||
|
goto is_dot_dot;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
level++;
|
||
|
|
||
|
#ifdef HAVE_MULTIVOLUME
|
||
|
if (level != 1)
|
||
|
break; /* Volume spec only valid @ root level */
|
||
|
|
||
|
const char *next;
|
||
|
volume = path_strip_volume(p, &next, true);
|
||
|
|
||
|
if (next > p)
|
||
|
{
|
||
|
#ifdef HAVE_MULTIDRIVE
|
||
|
/* Feign failure if the volume isn't "mounted" */
|
||
|
if (!volume_present(volume))
|
||
|
{
|
||
|
errno = ENXIO;
|
||
|
return -1;
|
||
|
}
|
||
|
#endif /* HAVE_MULTIDRIVE */
|
||
|
|
||
|
sysroot = false;
|
||
|
|
||
|
if (volume == 0)
|
||
|
continue;
|
||
|
|
||
|
p = extbuf;
|
||
|
len = snprintf(extbuf, sizeof (extbuf), SIMEXT_FMT, volume);
|
||
|
}
|
||
|
#endif /* HAVE_MULTIVOLUME */
|
||
|
break;
|
||
|
|
||
|
is_dot_dot:
|
||
|
if (level <= 0)
|
||
|
continue; /* Can't go above root; erase */
|
||
|
|
||
|
level--;
|
||
|
|
||
|
#ifdef HAVE_MULTIVOLUME
|
||
|
if (level == 0)
|
||
|
{
|
||
|
int v = volume;
|
||
|
bool sr = sysroot;
|
||
|
volume = -1;
|
||
|
sysroot = true;
|
||
|
|
||
|
if (v <= 0)
|
||
|
{
|
||
|
if (sr)
|
||
|
break;
|
||
|
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/* Going up from a volume root and back down to the sys root */
|
||
|
if (dirbase_len == 0)
|
||
|
{
|
||
|
/* Get the main simdisk directory so it can be reentered */
|
||
|
char tmpbuf[sizeof (dirbase)];
|
||
|
#ifdef WIN32
|
||
|
path_correct_separators(tmpbuf, sim_root_dir);
|
||
|
path_strip_drive(tmpbuf, &p, false);
|
||
|
#else
|
||
|
p = tmpbuf;
|
||
|
strcpy(tmpbuf, sim_root_dir);
|
||
|
#endif
|
||
|
size = path_basename(p, &p);
|
||
|
((char *)p)[size] = '\0';
|
||
|
|
||
|
if (size == 0 || is_dotdir_name(p))
|
||
|
{
|
||
|
/* This is nonsense and won't work */
|
||
|
DEBUGF("ERROR: sim root dir basname is dotdir or"
|
||
|
" empty: \"%s\"\n", sim_root_dir);
|
||
|
errno = ENOENT;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
dirbase_len = snprintf(dirbase, sizeof (dirbase),
|
||
|
DIRBASE_FMT, p);
|
||
|
}
|
||
|
|
||
|
p = dirbase;
|
||
|
len = dirbase_len;
|
||
|
break;
|
||
|
}
|
||
|
#endif /* HAVE_MULTIVOLUME */
|
||
|
break;
|
||
|
} /* end switch */
|
||
|
|
||
|
char compname[len + 1];
|
||
|
strmemcpy(compname, p, len);
|
||
|
|
||
|
size = path_append(buffer, PA_SEP_HARD, compname, bufsize);
|
||
|
if (size >= bufsize)
|
||
|
{
|
||
|
errno = ENAMETOOLONG;
|
||
|
return -1;
|
||
|
}
|
||
|
ADVBUF(size);
|
||
|
}
|
||
|
|
||
|
#if PPP_SHOWPATHS
|
||
|
DEBUGF("PPP (post): \"%s\"" IF_MV(" vol:%d") "\n",
|
||
|
buffer0 IF_MV(, volume));
|
||
|
#endif
|
||
|
|
||
|
return IF_MV(volume) +1;
|
||
|
}
|
||
|
|
||
|
|
||
|
/** File functions **/
|
||
|
|
||
|
int sim_open(const char *path, int oflag, ...)
|
||
|
{
|
||
|
int fildes;
|
||
|
struct filestr_desc *filestr = alloc_filestr(&fildes);
|
||
|
if (!filestr)
|
||
|
{
|
||
|
errno = EMFILE;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
char ospath[SIM_TMPBUF_MAX_PATH];
|
||
|
int pprc = sim_get_os_path(ospath, path, sizeof (ospath));
|
||
|
if (pprc < 0)
|
||
|
return -2;
|
||
|
|
||
|
filestr->osfd = os_open(ospath, oflag | O_BINARY __OPEN_MODE_ARG);
|
||
|
if (filestr->osfd < 0)
|
||
|
return -3;
|
||
|
|
||
|
#ifdef HAVE_MULTIVOLUME
|
||
|
filestr->volume = MAX(pprc - 1, 0);
|
||
|
#endif
|
||
|
filestr->mounted = true;
|
||
|
return fildes;
|
||
|
}
|
||
|
|
||
|
int sim_creat(const char *path, mode_t mode)
|
||
|
{
|
||
|
return sim_open(path, O_WRONLY|O_CREAT|O_TRUNC, mode);
|
||
|
}
|
||
|
|
||
|
int sim_close(int fildes)
|
||
|
{
|
||
|
struct filestr_desc *filestr = &openfiles[fildes];
|
||
|
if ((unsigned int)fildes >= MAX_OPEN_FILES || filestr->osfd < 0)
|
||
|
{
|
||
|
errno = EBADF;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
int osfd = filestr->osfd;
|
||
|
filestr->osfd = -1;
|
||
|
return os_close(osfd);
|
||
|
}
|
||
|
|
||
|
int sim_ftruncate(int fildes, off_t length)
|
||
|
{
|
||
|
struct filestr_desc *filestr = get_filestr(fildes);
|
||
|
if (!filestr)
|
||
|
return -1;
|
||
|
|
||
|
off_t size = os_filesize(filestr->osfd);
|
||
|
if (size < 0)
|
||
|
return -1;
|
||
|
|
||
|
if (length >= size)
|
||
|
return 0;
|
||
|
|
||
|
int rc = os_ftruncate(filestr->osfd, length);
|
||
|
|
||
|
#ifdef HAVE_DIRCACHE
|
||
|
if (rc >= 0)
|
||
|
dircache_ftruncate(xxxxxx);
|
||
|
#endif
|
||
|
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
int sim_fsync(int fildes)
|
||
|
{
|
||
|
struct filestr_desc *filestr = get_filestr(fildes);
|
||
|
if (!filestr)
|
||
|
return -1;
|
||
|
|
||
|
int rc = os_fsync(filestr->osfd);
|
||
|
|
||
|
#ifdef HAVE_DIRCACHE
|
||
|
if (rc >= 0)
|
||
|
dircache_fsync(xxxxxx);
|
||
|
#endif
|
||
|
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
off_t sim_lseek(int fildes, off_t offset, int whence)
|
||
|
{
|
||
|
struct filestr_desc *filestr = get_filestr(fildes);
|
||
|
if (!filestr)
|
||
|
return -1;
|
||
|
|
||
|
return os_lseek(filestr->osfd, offset, whence);
|
||
|
}
|
||
|
|
||
|
ssize_t sim_read(int fildes, void *buf, size_t nbyte)
|
||
|
{
|
||
|
struct filestr_desc *filestr = get_filestr(fildes);
|
||
|
if (!filestr)
|
||
|
return -1;
|
||
|
|
||
|
return os_read(filestr->osfd, buf, nbyte);
|
||
|
}
|
||
|
|
||
|
ssize_t sim_write(int fildes, const void *buf, size_t nbyte)
|
||
|
{
|
||
|
struct filestr_desc *filestr = get_filestr(fildes);
|
||
|
if (!filestr)
|
||
|
return -1;
|
||
|
|
||
|
return os_write(filestr->osfd, buf, nbyte);
|
||
|
}
|
||
|
|
||
|
int sim_remove(const char *path)
|
||
|
{
|
||
|
char ospath[SIM_TMPBUF_MAX_PATH];
|
||
|
if (sim_get_os_path(ospath, path, sizeof (ospath)) < 0)
|
||
|
return -1;
|
||
|
|
||
|
int rc = os_remove(ospath);
|
||
|
|
||
|
#ifdef HAVE_DIRCACHE
|
||
|
if (rc >= 0)
|
||
|
dircache_remove(xxxxxx);
|
||
|
#endif
|
||
|
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
int sim_rename(const char *old, const char *new)
|
||
|
{
|
||
|
char osold[SIM_TMPBUF_MAX_PATH];
|
||
|
int pprc1 = sim_get_os_path(osold, old, sizeof (osold));
|
||
|
if (pprc1 < 0)
|
||
|
return -1;
|
||
|
|
||
|
char osnew[SIM_TMPBUF_MAX_PATH];
|
||
|
int pprc2 = sim_get_os_path(osnew, new, sizeof (osnew));
|
||
|
if (pprc2 < 0)
|
||
|
return -1;
|
||
|
|
||
|
if (MAX(pprc1 - 1, 0) != MAX(pprc2 - 1, 0))
|
||
|
{
|
||
|
/* Pretend they're different devices */
|
||
|
errno = EXDEV;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
int rc = os_rename(osold, osnew);
|
||
|
|
||
|
#ifdef HAVE_DIRCACHE
|
||
|
if (rc >= 0)
|
||
|
dircache_rename(xxxxxx);
|
||
|
#endif
|
||
|
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
off_t sim_filesize(int fildes)
|
||
|
{
|
||
|
struct filestr_desc *filestr = get_filestr(fildes);
|
||
|
if (!filestr)
|
||
|
return -1;
|
||
|
|
||
|
return os_filesize(filestr->osfd);
|
||
|
}
|
||
|
|
||
|
int sim_fsamefile(int fildes1, int fildes2)
|
||
|
{
|
||
|
struct filestr_desc *filestr1 = get_filestr(fildes1);
|
||
|
if (!filestr1)
|
||
|
return -1;
|
||
|
|
||
|
struct filestr_desc *filestr2 = get_filestr(fildes2);
|
||
|
if (!filestr2)
|
||
|
return -1;
|
||
|
|
||
|
if (filestr1 == filestr2)
|
||
|
return 1;
|
||
|
|
||
|
return os_fsamefile(filestr1->osfd, filestr2->osfd);
|
||
|
}
|
||
|
|
||
|
int sim_relate(const char *path1, const char *path2)
|
||
|
{
|
||
|
char ospath1[SIM_TMPBUF_MAX_PATH];
|
||
|
if (sim_get_os_path(ospath1, path1, sizeof (ospath1)) < 0)
|
||
|
return -1;
|
||
|
|
||
|
char ospath2[SIM_TMPBUF_MAX_PATH];
|
||
|
if (sim_get_os_path(ospath2, path2, sizeof (ospath2)) < 0)
|
||
|
return -1;
|
||
|
|
||
|
return os_relate(ospath1, ospath2);
|
||
|
}
|
||
|
|
||
|
bool sim_file_exists(const char *path)
|
||
|
{
|
||
|
char ospath[SIM_TMPBUF_MAX_PATH];
|
||
|
if (sim_get_os_path(ospath, path, sizeof (ospath)) < 0)
|
||
|
return false;
|
||
|
|
||
|
return os_file_exists(ospath);
|
||
|
}
|
||
|
|
||
|
|
||
|
/** Directory functions **/
|
||
|
DIR * sim_opendir(const char *dirname)
|
||
|
{
|
||
|
struct dirstr_desc *dirstr = alloc_dirstr();
|
||
|
if (!dirstr)
|
||
|
{
|
||
|
errno = EMFILE;
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
char osdirname[SIM_TMPBUF_MAX_PATH];
|
||
|
int pprc = sim_get_os_path(osdirname, dirname, sizeof (osdirname));
|
||
|
if (pprc < 0)
|
||
|
return NULL;
|
||
|
|
||
|
int rc = os_opendir_and_fd(osdirname, &dirstr->osdirp, &dirstr->osfd);
|
||
|
if (rc < 0)
|
||
|
return NULL;
|
||
|
|
||
|
dirstr->osfd_opened = rc > 0;
|
||
|
|
||
|
#ifdef USE_OSDIRNAME
|
||
|
dirstr->osdirname = strdup(osdirname);
|
||
|
if (!dirstr->osdirname)
|
||
|
{
|
||
|
close_dirstr(dirstr);
|
||
|
return NULL;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
dirstr->entry.d_name[0] = 0; /* Mark as invalid */
|
||
|
#ifdef HAVE_MULTIVOLUME
|
||
|
dirstr->volume = MAX(pprc - 1, 0);
|
||
|
dirstr->volumecounter = pprc == 0 ? 0 : INT_MAX;
|
||
|
#endif
|
||
|
dirstr->mounted = true;
|
||
|
return (DIR *)dirstr; /* A-Okay */
|
||
|
}
|
||
|
|
||
|
int sim_closedir(DIR *dirp)
|
||
|
{
|
||
|
struct dirstr_desc *dirstr = (struct dirstr_desc *)dirp;
|
||
|
if (!PTR_IN_ARRAY(opendirs, dirstr, MAX_OPEN_DIRS) || !dirstr->osdirp)
|
||
|
{
|
||
|
errno = EBADF;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return close_dirstr(dirstr);
|
||
|
}
|
||
|
|
||
|
struct sim_dirent * sim_readdir(DIR *dirp)
|
||
|
{
|
||
|
struct dirstr_desc *dirstr = get_dirstr(dirp);
|
||
|
if (!dirstr)
|
||
|
return NULL;
|
||
|
|
||
|
struct sim_dirent *entry = &dirstr->entry;
|
||
|
entry->info.osdirent = NULL;
|
||
|
|
||
|
if (readdir_volume(dirstr, entry))
|
||
|
return entry;
|
||
|
|
||
|
OS_DIRENT_T *osdirent = os_readdir(dirstr->osdirp);
|
||
|
if (!osdirent)
|
||
|
return NULL;
|
||
|
|
||
|
size_t size = sizeof (entry->d_name);
|
||
|
if (strlcpy_from_os(entry->d_name, osdirent->d_name, size) >= size)
|
||
|
FILE_ERROR_RETURN(ENAMETOOLONG, NULL);
|
||
|
|
||
|
entry->info.osdirent = osdirent;
|
||
|
return entry;
|
||
|
}
|
||
|
|
||
|
int sim_mkdir(const char *path)
|
||
|
{
|
||
|
char ospath[SIM_TMPBUF_MAX_PATH];
|
||
|
if (sim_get_os_path(ospath, path, sizeof (ospath)) < 0)
|
||
|
return -1;
|
||
|
|
||
|
int rc = os_mkdir(ospath __MKDIR_MODE_ARG);
|
||
|
|
||
|
#ifdef HAVE_DIRCACHE
|
||
|
if (rc >= 0)
|
||
|
dircache_mkdir(xxxxxx);
|
||
|
#endif
|
||
|
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
int sim_rmdir(const char *path)
|
||
|
{
|
||
|
char ospath[SIM_TMPBUF_MAX_PATH];
|
||
|
if (sim_get_os_path(ospath, path, sizeof (ospath)) < 0)
|
||
|
return -1;
|
||
|
|
||
|
int rc = os_rmdir(ospath);
|
||
|
|
||
|
#ifdef HAVE_DIRCACHE
|
||
|
if (rc >= 0)
|
||
|
dircache_rmdir(xxxxxx);
|
||
|
#endif
|
||
|
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
int sim_samedir(DIR *dirp1, DIR *dirp2)
|
||
|
{
|
||
|
struct dirstr_desc *dirstr1 = get_dirstr(dirp1);
|
||
|
if (!dirstr1)
|
||
|
return -1;
|
||
|
|
||
|
struct dirstr_desc *dirstr2 = get_dirstr(dirp2);
|
||
|
if (!dirstr2)
|
||
|
return -1;
|
||
|
|
||
|
return os_fsamefile(dirstr1->osfd, dirstr2->osfd);
|
||
|
}
|
||
|
|
||
|
bool sim_dir_exists(const char *dirname)
|
||
|
{
|
||
|
char osdirname[SIM_TMPBUF_MAX_PATH];
|
||
|
if (sim_get_os_path(osdirname, dirname, sizeof (osdirname)) < 0)
|
||
|
return false;
|
||
|
|
||
|
OS_DIR_T *dirp = os_opendir(osdirname);
|
||
|
if (!dirp)
|
||
|
return false;
|
||
|
|
||
|
os_closedir(dirp);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
struct dirinfo dir_get_info(DIR *dirp, struct sim_dirent *entry)
|
||
|
{
|
||
|
int rc;
|
||
|
struct dirstr_desc *dirstr = get_dirstr(dirp);
|
||
|
if (!dirstr)
|
||
|
FILE_ERROR(ERRNO, RC);
|
||
|
|
||
|
if (entry->d_name[0] == 0)
|
||
|
FILE_ERROR(ENOENT, RC);
|
||
|
|
||
|
OS_DIRENT_T *osdirent = entry->info.osdirent;
|
||
|
if (osdirent == NULL)
|
||
|
return (struct dirinfo){ .attribute = ATTR_MOUNT_POINT };
|
||
|
|
||
|
struct dirinfo info;
|
||
|
info.attribute = 0;
|
||
|
|
||
|
OS_STAT_T s;
|
||
|
|
||
|
#ifdef os_fstatat
|
||
|
if (os_fstatat(dirstr->osfd, entry->d_name, &s, 0))
|
||
|
#else /* no fstatat; build file name for stat() */
|
||
|
char statpath[SIM_TMPBUF_MAX_PATH];
|
||
|
if (path_append(statpath, dirstr->osdirname, entry->d_name,
|
||
|
sizeof (statpath)) >= sizeof (statpath))
|
||
|
{
|
||
|
FILE_ERROR(ENAMETOOLONG, RC);
|
||
|
}
|
||
|
|
||
|
if (os_stat(statpath, &s)) /* get info */
|
||
|
#endif /* os_fstatat */
|
||
|
{
|
||
|
/* File size larger than 2 GB? */
|
||
|
#ifdef EOVERFLOW
|
||
|
if (errno == EOVERFLOW)
|
||
|
DEBUGF("stat overflow for \"%s\"\n", entry->d_name);
|
||
|
#endif
|
||
|
FILE_ERROR(ERRNO, RC);
|
||
|
}
|
||
|
|
||
|
if (S_ISDIR(s.st_mode))
|
||
|
info.attribute |= ATTR_DIRECTORY;
|
||
|
|
||
|
info.size = s.st_size;
|
||
|
|
||
|
struct tm tm;
|
||
|
if (localtime_r(&s.st_mtime, &tm) == NULL)
|
||
|
FILE_ERROR(ERRNO, RC);
|
||
|
|
||
|
info.mtime = mktime(&tm);
|
||
|
|
||
|
return info;
|
||
|
|
||
|
file_error:
|
||
|
return (struct dirinfo){ .size = 0 };
|
||
|
}
|
||
|
|
||
|
int os_volume_path(IF_MV(int volume, ) char *buffer, size_t bufsize)
|
||
|
{
|
||
|
if (!buffer || !bufsize IF_MV( || !volume_present(volume) ))
|
||
|
return -1;
|
||
|
|
||
|
char tmpbuf[SIM_TMPBUF_MAX_PATH];
|
||
|
tmpbuf[0] = '\0';
|
||
|
|
||
|
#ifdef HAVE_MULTIVOLUME
|
||
|
char volname[VOL_MAX_LEN + 1];
|
||
|
get_volume_name(volume, volname);
|
||
|
|
||
|
if (path_append(tmpbuf, PA_SEP_HARD, volname, sizeof (volname))
|
||
|
>= sizeof (volname))
|
||
|
return -1;
|
||
|
#endif /* HAVE_MULTIVOLUME */
|
||
|
|
||
|
if (path_append(tmpbuf, PA_SEP_HARD, ".", sizeof (tmpbuf))
|
||
|
>= sizeof (tmpbuf))
|
||
|
return -1;
|
||
|
|
||
|
return sim_get_os_path(buffer, tmpbuf, bufsize);
|
||
|
}
|