mirror of
https://github.com/simh/simh.git
synced 2026-01-26 12:02:14 +00:00
FIO: Fix sim_get_filelist potential problem when directory contents change
- Avoid inconsistencies in the internal cache when directory contents change - Cleanup FIO testing and add tests for new filename comparison routine
This commit is contained in:
268
sim_fio.c
268
sim_fio.c
@@ -304,6 +304,8 @@ return total;
|
||||
/* Forward Declaration */
|
||||
|
||||
t_offset sim_ftell (FILE *st);
|
||||
static int _sim_filename_compare (const char *filename1, const char *filename2);
|
||||
static void _flush_filelist_directory_cache_entry (const char *directory);
|
||||
|
||||
/* Get file size */
|
||||
|
||||
@@ -564,29 +566,98 @@ static struct relative_path_test {
|
||||
{NULL},
|
||||
};
|
||||
|
||||
static struct filename_compare_test {
|
||||
const char *testname;
|
||||
const char *filename1;
|
||||
const char *filename2;
|
||||
int expected_result;
|
||||
} name_compare_test[] = {
|
||||
{"name-equal-drive letter different case",
|
||||
"C:\\Xyz\\zzz.x",
|
||||
"c:\\Xyz\\zzz.x",
|
||||
0},
|
||||
{"name-diff-drive letter different",
|
||||
"C:\\Xyz\\zzz.x",
|
||||
"E:\\Xyz\\zzz.x",
|
||||
-1},
|
||||
{"name-diff-drive letter different-vs-path",
|
||||
"C:\\Xyz\\zzz.x",
|
||||
"\\Xyz\\zzz.x",
|
||||
-1},
|
||||
{"name-equal-separator-different-2",
|
||||
"C:/Xyz/zzz.x",
|
||||
"c:\\Xyz\\zzz.x",
|
||||
#if defined(_WIN32)
|
||||
2},
|
||||
#else
|
||||
1},
|
||||
#endif
|
||||
{"name-equal-separator-different-1",
|
||||
"c:\\Xyz\\zzz.x",
|
||||
"C:/Xyz/zzz.x",
|
||||
#if defined(_WIN32)
|
||||
1},
|
||||
#else
|
||||
2},
|
||||
#endif
|
||||
{"name-different-equal-path-diff-filename",
|
||||
"/a/b/cdd/dzzz.x",
|
||||
"/a/b/cdd/zzzz.x",
|
||||
-1},
|
||||
{"name-diff-nostarting-separator",
|
||||
"a/b/cdd/dzzz.x",
|
||||
"a/b/cdd/zzzz.x",
|
||||
-1},
|
||||
{"name-equal-nostarting-separator",
|
||||
"a/b/cdd/dzzz.x",
|
||||
"a/b/cdd/dzzz.x",
|
||||
0},
|
||||
{"name-equal-noseparator",
|
||||
"zzz.x",
|
||||
"zzz.x",
|
||||
0},
|
||||
{"name-diff-nostarting-separator",
|
||||
"a/b/cdd/dzzz.x",
|
||||
"abcddzzzz.x",
|
||||
-1},
|
||||
{"name-diff-nostarting-same-length",
|
||||
"abcddzzzz.x/b/cdd/dzzz.x",
|
||||
"abcddzzzz.x",
|
||||
-1},
|
||||
{"name-diff-nostarting-same-length-firsttoken",
|
||||
"abcde.x/b/cdd/dzzz.x",
|
||||
"abcde.x",
|
||||
-1},
|
||||
{NULL},
|
||||
};
|
||||
|
||||
static struct get_filelist_test {
|
||||
const char *name;
|
||||
const char *files[10];
|
||||
const char *search;
|
||||
int expected_count;
|
||||
} get_test[] = {
|
||||
{"test-1",
|
||||
{"file.txt",
|
||||
{"test-single file in subdirectory",
|
||||
{"a0a/file.txt",
|
||||
NULL},
|
||||
"file.txt", 1},
|
||||
{"test-2",
|
||||
{"test-similar deep file names",
|
||||
{"aab/bbc/ccd/eef/file.txt",
|
||||
"aab/bbc/ccd/eef/file2.txt",
|
||||
"aac/bbd/cce/eef/file2.txt",
|
||||
NULL},
|
||||
"file.txt", 1},
|
||||
{"test-3",
|
||||
{"test-single file no subdirectories",
|
||||
{"file.txt",
|
||||
NULL},
|
||||
"file.txt", 1},
|
||||
{"test-3 text files in the same 4 deep subdirectory",
|
||||
{"aab/bbc/ccd/eef/file.txt",
|
||||
"aab/bbc/ccd/eef/file2.txt",
|
||||
"aac/bbd/cce/eef/file2.txt",
|
||||
NULL},
|
||||
"*.txt", 3},
|
||||
{"test-4",
|
||||
{"test-2 text files",
|
||||
{"xab/bbc/ccd/eef/file.txt",
|
||||
"xab/bbc/ccd/eef/file2.bbb",
|
||||
"xac/bbd/cce/eef/file2.txt",
|
||||
@@ -601,19 +672,22 @@ t_stat sim_fio_test (const char *cptr)
|
||||
{
|
||||
struct pack_test *pt;
|
||||
struct relative_path_test *rt;
|
||||
struct filename_compare_test *nt;
|
||||
struct get_filelist_test *gt;
|
||||
t_stat r = SCPE_OK;
|
||||
char test_desc[512];
|
||||
uint8 result[512];
|
||||
int tests;
|
||||
|
||||
sim_register_internal_device (&sim_fio_test_dev);
|
||||
sim_fio_test_dev.dctrl |= (sim_switches & SWMASK ('D')) ? FIO_DBG_PACK : 0;
|
||||
sim_fio_test_dev.dctrl |= (sim_switches & SWMASK ('S')) ? FIO_DBG_SCAN : 0;
|
||||
sim_set_deb_switches (SWMASK ('F'));
|
||||
sim_messagef (SCPE_OK, "sim_buf_pack_unpack - tests\n");
|
||||
for (pt = p_test; pt->src; ++pt) {
|
||||
sim_messagef (SCPE_OK, "*** Running sim_buf_pack_unpack - tests\n");
|
||||
for (pt = p_test, tests = 0; pt->src; ++pt) {
|
||||
t_bool res;
|
||||
|
||||
++tests;
|
||||
snprintf (test_desc, sizeof (test_desc), "%dbit%s->%dbit%s %d words", pt->sbits, pt->slsb ? "LSB" : "MSB", pt->dbits, pt->dlsb ? "LSB" : "MSB", pt->scount);
|
||||
memset (result, 0x80, sizeof (result));
|
||||
res = sim_buf_pack_unpack (pt->src, result, pt->sbits, pt->slsb, pt->scount, pt->dbits, pt->dlsb);
|
||||
@@ -634,8 +708,11 @@ for (pt = p_test; pt->src; ++pt) {
|
||||
else
|
||||
r = sim_messagef (SCPE_IERR, "%s - BAD Status - Expected: %s got %s\n", test_desc, pt->exp_stat ? "True" : "False", res ? "True" : "False");
|
||||
}
|
||||
sim_messagef (SCPE_OK, "Testing relative path logic:\n");
|
||||
for (rt = r_test; rt->input; ++rt) {
|
||||
if (r != SCPE_OK)
|
||||
return r;
|
||||
sim_messagef (SCPE_OK, "*** All %d sim_buf_pack_unpack tests GOOD\n", tests);
|
||||
sim_messagef (SCPE_OK, "*** Testing relative path logic:\n");
|
||||
for (rt = r_test, tests = 0; rt->input; ++rt) {
|
||||
char input[PATH_MAX + 1];
|
||||
char cmpbuf[PATH_MAX + 1];
|
||||
char cwd[PATH_MAX + 1];
|
||||
@@ -647,6 +724,7 @@ for (rt = r_test; rt->input; ++rt) {
|
||||
const char *sep;
|
||||
t_stat mkdir_stat = SCPE_OK;
|
||||
|
||||
++tests;
|
||||
strlcpy (origcwd, cwd, sizeof (origcwd));
|
||||
if (rt->extra_dir != NULL)
|
||||
mkdir_stat = mkdir_cmd (0, rt->extra_dir);
|
||||
@@ -715,16 +793,38 @@ for (rt = r_test; rt->input; ++rt) {
|
||||
free (xdir);
|
||||
}
|
||||
}
|
||||
sim_messagef (SCPE_OK, "Testing get filelist:\n");
|
||||
for (gt = get_test; gt->name; ++gt) {
|
||||
if (r != SCPE_OK)
|
||||
return r;
|
||||
sim_messagef (SCPE_OK, "*** All %d relative path logic tests GOOD\n", tests);
|
||||
sim_messagef (SCPE_OK, "*** Testing filename compare:\n");
|
||||
for (nt = name_compare_test, tests = 0; nt->testname; ++nt) {
|
||||
int result;
|
||||
|
||||
++tests;
|
||||
result = _sim_filename_compare (nt->filename1, nt->filename2);
|
||||
if (result != nt->expected_result) {
|
||||
sim_messagef (SCPE_IERR, "Name Compare test %d %s\n", tests, nt->testname);
|
||||
sim_messagef (SCPE_IERR, " filename1: %s\n", nt->filename1);
|
||||
sim_messagef (SCPE_IERR, " filename2: %s\n", nt->filename2);
|
||||
r = sim_messagef (SCPE_IERR, " BAD result: %d\n", result);
|
||||
}
|
||||
}
|
||||
if (r != SCPE_OK)
|
||||
return r;
|
||||
sim_messagef (SCPE_OK, "*** All %d filename compare tests GOOD\n", tests);
|
||||
sim_messagef (SCPE_OK, "*** Testing sim_get_filelist:\n");
|
||||
for (gt = get_test, tests = 0; gt->name; ++gt) {
|
||||
char xpath[PATH_MAX + 1];
|
||||
int i;
|
||||
char **filelist;
|
||||
|
||||
sim_messagef (SCPE_OK, "FileList test %s\n", gt->name);
|
||||
++tests;
|
||||
sim_messagef (r, "FileList test %s\n", gt->name);
|
||||
for (i=0; gt->files[i]; ++i) {
|
||||
char *c, *end;
|
||||
char *filename = sim_filepath_parts (gt->files[i], "nx");
|
||||
|
||||
sim_messagef (r, "Creating: %s\n", gt->files[i]);
|
||||
snprintf (xpath, sizeof (xpath), "testfiles/%s", gt->files[i]);
|
||||
end = strrchr (xpath, '/');
|
||||
*end = '\0';
|
||||
@@ -743,14 +843,16 @@ for (gt = get_test; gt->name; ++gt) {
|
||||
snprintf (xpath, sizeof (xpath), "%s", gt->search);
|
||||
filelist = sim_get_filelist (xpath);
|
||||
sim_chdir ("..");
|
||||
r = sim_messagef ((gt->expected_count != sim_count_filelist (filelist)) ? SCPE_IERR : SCPE_OK,
|
||||
"sim_get_filelist (\"%s\") yielded %d entries:\n", xpath, sim_count_filelist (filelist));
|
||||
r |= sim_messagef ((gt->expected_count != sim_count_filelist (filelist)) ? SCPE_IERR : SCPE_OK,
|
||||
"sim_get_filelist (\"%s\") yielded %d entries, expected %d entries:\n", xpath, sim_count_filelist (filelist), gt->expected_count);
|
||||
sim_print_filelist (filelist);
|
||||
sim_free_filelist (&filelist);
|
||||
/* Cleanup created test files and directories */
|
||||
for (i=0; gt->files[i]; ++i) {
|
||||
char *c;
|
||||
char *filename = sim_filepath_parts (gt->files[i], "nx");
|
||||
snprintf (xpath, sizeof (xpath), "testfiles/%s", gt->files[i]);
|
||||
sim_messagef (r, "Removing: %s\n", gt->files[i]);
|
||||
unlink (xpath);
|
||||
c = strrchr (xpath, '/');
|
||||
*c = '\0';
|
||||
@@ -763,6 +865,8 @@ for (gt = get_test; gt->name; ++gt) {
|
||||
free (filename);
|
||||
}
|
||||
}
|
||||
if (r == SCPE_OK)
|
||||
sim_messagef (SCPE_OK, "All %d sim_get_filelist tests GOOD\n", tests);
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -801,9 +905,23 @@ return mkdir (pathbuf, 0777);
|
||||
int sim_rmdir(const char *path)
|
||||
{
|
||||
char pathbuf[PATH_MAX + 1];
|
||||
struct stat statb;
|
||||
char *fullpath;
|
||||
char FullPath[PATH_MAX + 1];
|
||||
|
||||
if (NULL == _sim_expand_homedir (path, pathbuf, sizeof (pathbuf)))
|
||||
return -1;
|
||||
|
||||
memset (&statb, 0, sizeof (statb));
|
||||
(void)stat (pathbuf, &statb);
|
||||
if ((statb.st_mode & S_IFDIR) == 0)
|
||||
return rmdir (pathbuf); /* This is an error, but let rmdir() set errno */
|
||||
|
||||
fullpath = sim_filepath_parts (pathbuf, "f");
|
||||
snprintf (FullPath, sizeof (FullPath), "%s/*", fullpath);
|
||||
free (fullpath);
|
||||
_flush_filelist_directory_cache_entry (FullPath);
|
||||
|
||||
return rmdir (pathbuf);
|
||||
}
|
||||
|
||||
@@ -846,6 +964,27 @@ _filelist_directory_cache = entry;
|
||||
sim_debug (FIO_DBG_SCAN, &sim_fio_test_dev, "_save_filelist_directory_cache(directory=\"%s\") saved with %d directories\n", directory, sim_count_filelist (dirlist));
|
||||
}
|
||||
|
||||
static void _flush_filelist_directory_cache_entry (const char *directory)
|
||||
{
|
||||
FILELIST_DIRECTORY_CACHE *entry = _filelist_directory_cache;
|
||||
FILELIST_DIRECTORY_CACHE *last = NULL;
|
||||
|
||||
while (entry != NULL) {
|
||||
if (strcmp (entry->directory, directory) == 0) {
|
||||
if (last == NULL)
|
||||
_filelist_directory_cache = entry->next;
|
||||
else
|
||||
last->next = entry->next;
|
||||
free (entry->directory);
|
||||
sim_free_filelist (&entry->dirlist);
|
||||
free (entry);
|
||||
return;
|
||||
}
|
||||
last = entry;
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void _flush_filelist_directory_cache (void)
|
||||
{
|
||||
FILELIST_DIRECTORY_CACHE *entry = _filelist_directory_cache;
|
||||
@@ -937,6 +1076,92 @@ if (*filename != '\0') {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare two file names ignoring possibly different path separators
|
||||
*
|
||||
* Return value
|
||||
* -1 - names are different
|
||||
* 0 - names are equal
|
||||
* 1 - names equal first one is preferred on this platform -
|
||||
* path separators are locally appropriate.
|
||||
* 2 - names equal second one is preferred on this platform -
|
||||
* path separators are locally appropriate.
|
||||
*/
|
||||
static int _sim_filename_compare (const char *name1,
|
||||
const char *name2)
|
||||
{
|
||||
const char *p1 = name1;
|
||||
const char *p2 = name2;
|
||||
const char *e1;
|
||||
const char *e2;
|
||||
char n1_sep = '\0';
|
||||
char n2_sep = '\0';
|
||||
int result = -2;
|
||||
|
||||
if (p1[1] == ':') { /* Windows Drive letter delimiter */
|
||||
if ((p2[1] == ':') &&
|
||||
(sim_toupper (p1[0]) == sim_toupper (p2[0]))) {
|
||||
p1 += 2;
|
||||
p2 += 2;
|
||||
}
|
||||
}
|
||||
if ((*p1 == '/') || (*p1 == '\\')) {
|
||||
n1_sep = *p1;
|
||||
++p1;
|
||||
}
|
||||
if ((*p2 == '/') || (*p2 == '\\')) {
|
||||
n2_sep = *p2;
|
||||
++p2;
|
||||
}
|
||||
if (n1_sep == '\0') {
|
||||
if (strchr (p1, '/'))
|
||||
n1_sep = '/';
|
||||
if (strchr (p1, '\\'))
|
||||
n1_sep = '\\';
|
||||
}
|
||||
if (n2_sep == '\0') {
|
||||
if (strchr (p2, '/'))
|
||||
n2_sep = '/';
|
||||
if (strchr (p2, '\\'))
|
||||
n2_sep = '\\';
|
||||
}
|
||||
if (strcmp (p1, p2) == 0)
|
||||
result = 0;
|
||||
while (result == -2) {
|
||||
e1 = strchr (p1, n1_sep);
|
||||
e2 = strchr (p2, n2_sep);
|
||||
if ((e1 != NULL) && (e2 != NULL)) {
|
||||
if ((e1 - p1) != (e2 - p2)) {
|
||||
result = -1;/* Directory or filename Lengths differ */
|
||||
break;
|
||||
}
|
||||
if (memcmp (p1, p2, (e1 - p1)) != 0) {
|
||||
result = -1;/* Directory or filename differ */
|
||||
break;
|
||||
}
|
||||
/* Move to next Directory or filename */
|
||||
p1 = e1 + ((n1_sep != '\0') ? 1 : 0);
|
||||
p2 = e2 + ((n2_sep != '\0') ? 1 : 0);
|
||||
continue;
|
||||
}
|
||||
if (((e1 != NULL) && (e2 == NULL)) ||
|
||||
((e2 != NULL) && (e1 == NULL))) {
|
||||
result = -1; /* At the end of one filename but not both */
|
||||
}
|
||||
if (0 != strcmp (p1, p2)) {
|
||||
result = -1; /* The filename parts are different */
|
||||
break;
|
||||
}
|
||||
if (n1_sep == sim_file_path_separator) {
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
result = 2;
|
||||
}
|
||||
sim_debug (FIO_DBG_SCAN, &sim_fio_test_dev, "_sim_filename_compare(\"%s\", \"%s\") result: %d\n", name1, name2, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void _sim_filelist_entry (const char *directory,
|
||||
const char *filename,
|
||||
t_offset FileSize,
|
||||
@@ -949,8 +1174,19 @@ int listcount = 0;
|
||||
|
||||
snprintf (FullPath, sizeof (FullPath), "%s%s", directory, filename);
|
||||
if (filelist != NULL) {
|
||||
while (filelist[listcount++] != NULL);
|
||||
--listcount;
|
||||
while (filelist[listcount] != NULL) {
|
||||
int same = _sim_filename_compare (filelist[listcount], FullPath);
|
||||
|
||||
if (same < 0) {
|
||||
++listcount;
|
||||
continue;
|
||||
}
|
||||
if (same == 2) {
|
||||
free (filelist[listcount]);
|
||||
filelist[listcount] = strdup (FullPath);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
filelist = (char **)realloc (filelist, (listcount + 2) * sizeof (filelist));
|
||||
filelist[listcount] = strdup (FullPath);
|
||||
|
||||
Reference in New Issue
Block a user