Skip to content
Permalink
Browse files
Reworked enumeration to be more powerful.
Now callbacks can stop further enumeration and report errors, if they had a
catastrophic issue or just found what they needed and don't need to process
any more items.

Also, the actual enumerators can report errors instead of silently dropping
items. This led to several other fixes as all these implementations got
audited and reworked.

The original, non-callback enumerator now returns NULL if it can't produce a
complete list instead of dropping items and returning a partial list.
  • Loading branch information
icculus committed Aug 12, 2017
1 parent b082bc3 commit ee9687bca9a5cb3a39384bc02e91b82e84d6195e
@@ -742,6 +742,7 @@ PHYSFS_DECL const char *PHYSFS_getErrorByCode(PHYSFS_ErrorCode code)
case PHYSFS_ERR_OS_ERROR: return "OS reported an error";
case PHYSFS_ERR_DUPLICATE: return "duplicate resource";
case PHYSFS_ERR_BAD_PASSWORD: return "bad password";
case PHYSFS_ERR_APP_CALLBACK: return "app callback reported error";
} /* switch */

return NULL; /* don't know this error code. */
@@ -1430,7 +1431,7 @@ static int doRegisterArchiver(const PHYSFS_Archiver *_archiver)
BAIL_IF(!_archiver->info.author, PHYSFS_ERR_INVALID_ARGUMENT, 0);
BAIL_IF(!_archiver->info.url, PHYSFS_ERR_INVALID_ARGUMENT, 0);
BAIL_IF(!_archiver->openArchive, PHYSFS_ERR_INVALID_ARGUMENT, 0);
BAIL_IF(!_archiver->enumerateFiles, PHYSFS_ERR_INVALID_ARGUMENT, 0);
BAIL_IF(!_archiver->enumerate, PHYSFS_ERR_INVALID_ARGUMENT, 0);
BAIL_IF(!_archiver->openRead, PHYSFS_ERR_INVALID_ARGUMENT, 0);
BAIL_IF(!_archiver->openWrite, PHYSFS_ERR_INVALID_ARGUMENT, 0);
BAIL_IF(!_archiver->openAppend, PHYSFS_ERR_INVALID_ARGUMENT, 0);
@@ -1910,6 +1911,7 @@ int PHYSFS_setSaneConfig(const char *organization, const char *appName,
/* Root out archives, and add them to search path... */
if (archiveExt != NULL)
{
/* !!! FIXME-3.0: turn this into a callback */
char **rc = PHYSFS_enumerateFiles("/");
char **i;
size_t extlen = strlen(archiveExt);
@@ -2212,7 +2214,7 @@ static int locateInStringList(const char *str,
} /* locateInStringList */


static void enumFilesCallback(void *data, const char *origdir, const char *str)
static int enumFilesCallback(void *data, const char *origdir, const char *str)
{
PHYSFS_uint32 pos;
void *ptr;
@@ -2225,15 +2227,21 @@ static void enumFilesCallback(void *data, const char *origdir, const char *str)
*/
pos = pecd->size;
if (locateInStringList(str, pecd->list, &pos))
return; /* already in the list. */
return 1; /* already in the list, but keep going. */

ptr = allocator.Realloc(pecd->list, (pecd->size + 2) * sizeof (char *));
newstr = (char *) allocator.Malloc(strlen(str) + 1);
if (ptr != NULL)
pecd->list = (char **) ptr;

if ((ptr == NULL) || (newstr == NULL))
return; /* better luck next time. */
{
if (newstr)
allocator.Free(newstr);

pecd->errcode = PHYSFS_ERR_OUT_OF_MEMORY;
return -1; /* better luck next time. */
} /* if */

strcpy(newstr, str);

@@ -2245,6 +2253,8 @@ static void enumFilesCallback(void *data, const char *origdir, const char *str)

pecd->list[pos] = newstr;
pecd->size++;

return 1;
} /* enumFilesCallback */


@@ -2254,7 +2264,17 @@ char **PHYSFS_enumerateFiles(const char *path)
memset(&ecd, '\0', sizeof (ecd));
ecd.list = (char **) allocator.Malloc(sizeof (char *));
BAIL_IF(!ecd.list, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
PHYSFS_enumerateFilesCallback(path, enumFilesCallback, &ecd);
if (!PHYSFS_enumerate(path, enumFilesCallback, &ecd))
{
const PHYSFS_ErrorCode errcode = currentErrorCode();
PHYSFS_uint32 i;
for (i = 0; i < ecd.size; i++)
allocator.Free(ecd.list[i]);
allocator.Free(ecd.list);
BAIL_IF(errcode == PHYSFS_ERR_APP_CALLBACK, ecd.errcode, NULL);
return NULL;
} /* if */

ecd.list[ecd.size] = NULL;
return ecd.list;
} /* PHYSFS_enumerateFiles */
@@ -2263,79 +2283,98 @@ char **PHYSFS_enumerateFiles(const char *path)
/*
* Broke out to seperate function so we can use stack allocation gratuitously.
*/
static void enumerateFromMountPoint(DirHandle *i, const char *arcfname,
PHYSFS_EnumFilesCallback callback,
static int enumerateFromMountPoint(DirHandle *i, const char *arcfname,
PHYSFS_EnumerateCallback callback,
const char *_fname, void *data)
{
const size_t len = strlen(arcfname);
char *ptr = NULL;
char *end = NULL;
const size_t slen = strlen(i->mountPoint) + 1;
char *mountPoint = (char *) __PHYSFS_smallAlloc(slen);
int rc;

if (mountPoint == NULL)
return; /* oh well. */
BAIL_IF(!mountPoint, PHYSFS_ERR_OUT_OF_MEMORY, -1);

strcpy(mountPoint, i->mountPoint);
ptr = mountPoint + ((len) ? len + 1 : 0);
end = strchr(ptr, '/');
assert(end); /* should always find a terminating '/'. */
*end = '\0';
callback(data, _fname, ptr);
rc = callback(data, _fname, ptr);
__PHYSFS_smallFree(mountPoint);

BAIL_IF(rc == -1, PHYSFS_ERR_APP_CALLBACK, -1);
return rc;
} /* enumerateFromMountPoint */


typedef struct SymlinkFilterData
{
PHYSFS_EnumFilesCallback callback;
PHYSFS_EnumerateCallback callback;
void *callbackData;
DirHandle *dirhandle;
PHYSFS_ErrorCode errcode;
} SymlinkFilterData;

/* !!! FIXME-3.0: broken if in a virtual mountpoint (stat call fails). */
static void enumCallbackFilterSymLinks(void *_data, const char *origdir,
const char *fname)
static int enumCallbackFilterSymLinks(void *_data, const char *origdir,
const char *fname)
{
SymlinkFilterData *data = (SymlinkFilterData *) _data;
const DirHandle *dh = data->dirhandle;
PHYSFS_Stat statbuf;
const char *trimmedDir = (*origdir == '/') ? (origdir+1) : origdir;
const size_t slen = strlen(trimmedDir) + strlen(fname) + 2;
char *path = (char *) __PHYSFS_smallAlloc(slen);
int retval = 1;

if (path != NULL)
if (path == NULL)
{
SymlinkFilterData *data = (SymlinkFilterData *) _data;
const DirHandle *dh = data->dirhandle;
PHYSFS_Stat statbuf;
data->errcode = PHYSFS_ERR_OUT_OF_MEMORY;
return -1;
} /* if */

snprintf(path, slen, "%s%s%s", trimmedDir, *trimmedDir ? "/" : "", fname);
if (dh->funcs->stat(dh->opaque, path, &statbuf))
snprintf(path, slen, "%s%s%s", trimmedDir, *trimmedDir ? "/" : "", fname);

if (!dh->funcs->stat(dh->opaque, path, &statbuf))
{
data->errcode = currentErrorCode();
retval = -1;
} /* if */
else
{
/* Pass it on to the application if it's not a symlink. */
if (statbuf.filetype != PHYSFS_FILETYPE_SYMLINK)
{
/* Pass it on to the application if it's not a symlink. */
if (statbuf.filetype != PHYSFS_FILETYPE_SYMLINK)
data->callback(data->callbackData, origdir, fname);
retval = data->callback(data->callbackData, origdir, fname);
if (retval == -1)
data->errcode = PHYSFS_ERR_APP_CALLBACK;
} /* if */
} /* else */

__PHYSFS_smallFree(path);
} /* if */
__PHYSFS_smallFree(path);

return retval;
} /* enumCallbackFilterSymLinks */


/* !!! FIXME-3.0: this should report error conditions. */
void PHYSFS_enumerateFilesCallback(const char *_fname,
PHYSFS_EnumFilesCallback callback,
void *data)
int PHYSFS_enumerate(const char *_fn, PHYSFS_EnumerateCallback cb, void *data)
{
int retval = 1;
size_t len;
char *fname;

BAIL_IF(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, ) /*0*/;
BAIL_IF(!callback, PHYSFS_ERR_INVALID_ARGUMENT, ) /*0*/;
BAIL_IF(!_fn, PHYSFS_ERR_INVALID_ARGUMENT, 0);
BAIL_IF(!cb, PHYSFS_ERR_INVALID_ARGUMENT, 0);

len = strlen(_fname) + 1;
len = strlen(_fn) + 1;
fname = (char *) __PHYSFS_smallAlloc(len);
BAIL_IF(!fname, PHYSFS_ERR_OUT_OF_MEMORY, ) /*0*/;
BAIL_IF(!fname, PHYSFS_ERR_OUT_OF_MEMORY, 0);

if (sanitizePlatformIndependentPath(_fname, fname))
if (!sanitizePlatformIndependentPath(_fn, fname))
retval = 0;
else
{
DirHandle *i;
SymlinkFilterData filterdata;
@@ -2345,36 +2384,70 @@ void PHYSFS_enumerateFilesCallback(const char *_fname,
if (!allowSymLinks)
{
memset(&filterdata, '\0', sizeof (filterdata));
filterdata.callback = callback;
filterdata.callback = cb;
filterdata.callbackData = data;
} /* if */

for (i = searchPath; i != NULL; i = i->next)
for (i = searchPath; (retval > 0) && (i != NULL); i = i->next)
{
char *arcfname = fname;

if (partOfMountPoint(i, arcfname))
enumerateFromMountPoint(i, arcfname, callback, _fname, data);
retval = enumerateFromMountPoint(i, arcfname, cb, _fn, data);

else if (verifyPath(i, &arcfname, 0))
{
if ((!allowSymLinks) && (i->funcs->info.supportsSymlinks))
{
filterdata.dirhandle = i;
i->funcs->enumerateFiles(i->opaque, arcfname,
enumCallbackFilterSymLinks,
_fname, &filterdata);
filterdata.errcode = PHYSFS_ERR_OK;
retval = i->funcs->enumerate(i->opaque, arcfname,
enumCallbackFilterSymLinks,
_fn, &filterdata);
if (retval == -1)
{
if (currentErrorCode() == PHYSFS_ERR_APP_CALLBACK)
PHYSFS_setErrorCode(filterdata.errcode);
} /* if */
} /* if */
else
{
i->funcs->enumerateFiles(i->opaque, arcfname,
callback, _fname, data);
retval = i->funcs->enumerate(i->opaque, arcfname,
cb, _fn, data);
} /* else */
} /* else if */
} /* for */

__PHYSFS_platformReleaseMutex(stateLock);
} /* if */

__PHYSFS_smallFree(fname);

return (retval < 0) ? 0 : 1;
} /* PHYSFS_enumerate */


typedef struct
{
/* can't use the typedef because it might trigger deprecation warnings. */
void (*callback)(void *data, const char *origdir, const char *fname);
void *data;
} LegacyEnumFilesCallbackData;

static int enumFilesCallbackAlwaysSucceed(void *data, const char *origdir,
const char *fname)
{
LegacyEnumFilesCallbackData *cbdata = (LegacyEnumFilesCallbackData *) data;
cbdata->callback(cbdata->data, origdir, fname);
return 1;
} /* enumFilesCallbackAlwaysSucceed */

void PHYSFS_enumerateFilesCallback(const char *fname,
PHYSFS_EnumFilesCallback callback,
void *data)
{
LegacyEnumFilesCallbackData cbdata = { callback, data };
(void) PHYSFS_enumerate(fname, enumFilesCallbackAlwaysSucceed, &cbdata);
} /* PHYSFS_enumerateFilesCallback */


@@ -3138,21 +3211,27 @@ void *__PHYSFS_DirTreeFind(__PHYSFS_DirTree *dt, const char *path)
BAIL(PHYSFS_ERR_NOT_FOUND, NULL);
} /* __PHYSFS_DirTreeFind */

void __PHYSFS_DirTreeEnumerateFiles(void *opaque, const char *dname,
PHYSFS_EnumFilesCallback cb,
const char *origdir, void *callbackdata)
int __PHYSFS_DirTreeEnumerate(void *opaque, const char *dname,
PHYSFS_EnumerateCallback cb,
const char *origdir, void *callbackdata)
{
__PHYSFS_DirTree *tree = ((__PHYSFS_DirTree *) opaque);
__PHYSFS_DirTree *tree = (__PHYSFS_DirTree *) opaque;
const __PHYSFS_DirTreeEntry *entry = __PHYSFS_DirTreeFind(tree, dname);
if (entry && entry->isdir)
{
for (entry = entry->children; entry; entry = entry->sibling)
{
const char *ptr = strrchr(entry->name, '/');
cb(callbackdata, origdir, ptr ? ptr + 1 : entry->name);
const char *name = entry->name;
const char *ptr = strrchr(name, '/');
const int rc = cb(callbackdata, origdir, ptr ? ptr + 1 : name);
BAIL_IF(rc == -1, PHYSFS_ERR_APP_CALLBACK, -1);
if (rc == 0)
return 0;
} /* for */
} /* if */
} /* __PHYSFS_DirTreeEnumerateFiles */

return 1;
} /* __PHYSFS_DirTreeEnumerate */


void __PHYSFS_DirTreeDeinit(__PHYSFS_DirTree *dt)

0 comments on commit ee9687b

Please sign in to comment.