Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
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 ee9687b
Show file tree
Hide file tree
Showing 17 changed files with 370 additions and 183 deletions.
175 changes: 127 additions & 48 deletions src/physfs.c
Expand Up @@ -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. */
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand All @@ -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);

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

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

return 1;
} /* enumFilesCallback */


Expand All @@ -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 */
Expand All @@ -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;
Expand All @@ -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 */


Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit ee9687b

Please sign in to comment.