You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

185 lines
6.4 KiB

/*
CPMREDIR: CP/M filesystem redirector
Optional Open file tracker
Copyright (C) 2021, Mark Ogden <mark.pm.ogden@btinternet.com>
This is an addition to the CPMREDIR
Copyright (C) 1998, John Elliott <jce@seasip.demon.co.uk>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "cpmint.h"
/* CP/M does not require that files opened for reading need to be closed,
* this has two impacts
* 1) a lot of file handles can remain opened even when the file is no
* longer used. For modern OS builds this isn't a major problem as
* the system limits are quite high. It is however wasteful
* 2) for windows it can lead to issues when trying to delete / rename a file
* as normally windows will not allow files to be deleted/renamed if the
* file is currently open. Unix variants don't have this restriction.
*
* As an example the build of cgen.com using my decompiled sources
* the linq phase without tracking left 42 open files
* with tracking this was reduced to 2
*
* This code keeps track of files that are opened until they are explicitly
* closed or the FCB used to open the file is reused, or the file needs to be
* renamed or deleted.
* To do this it keeps track of the expanded filename, fcb location and allocated
* file handle
*
* Two public functions are used to manage the file list, and are called from
* within the bdos emulation
*
* trackFile(char *fname, void *fcb, int fd)
* removes existing tracking with matchin fcb or fd and
* if (fname != NULL) - add the info to the head of the open files list
* it returns fd
*
* the function is called in the following circumstances
* 1) before closing a file (fname is NULL)
* 2) just after the file has been opened/created.
* 3) to remove association with a given fcb trackFile(NULL, fcb, -1)
*
* note a helper macro releaseFCB(fcb) can be used for (3) above
*
* releaseFile(char *fname)
* this scans through the list of open files and for each open file
* with a matching fname, the file is closed
*
* the function is called before deleting a file or renaming a file
*
*
* there is a helper function that removes the info from the list
*
* Notes:
* For most applications the tracker could in principle automatically
* close existing open files at the start of a new executable invocation.
* Unfortunately this does not support the case where there is a scripting
* engine intercepting the warm reboots, as it may need to keep the script
* source file open.
*
* Note in theory it would be possible for a CP/M program to open a file
* with a given fcb, move the fcb internally and then open another file
* with the original fcb. If this happens the FCB tracking could cause
* a problem. I am not aware of any real programs that do this.
* Please let me know if the situation arises.
*/
/*
* The FILETRACKER functionality was implemented primarily because
* MSDOS file interface does not allow opening files in shared mode.
* This port of zxcc deprecates MSDOS and uses WIN32 API calls to handle
* all file I/O. So, this means that FILETRACKER is now optional for
* for Windows as well as Unix. I have found some edge cases where
* FILETRACKER caused a CP/M program to misbehave. Specifically, ZSM4
* reuses FCBs if files are included and does it such a way that the
* FILETRACKER is unable to solve the problem. For maximum
* compatibility, FILETRACKER may now be left off with the implication
* that a lot of file handles will be left open.
*/
#ifdef FILETRACKER
#include "cpmint.h"
typedef struct _track {
struct _track* next;
int handle;
void* fcb;
char* fname;
} track_t;
track_t* openFiles;
static track_t* rmHandle(track_t* s) {
track_t* next = s->next;
free(s->fname);
free(s);
return next;
}
void releaseFile(char* fname) {
track_t* s = (track_t*)&openFiles;
DBGMSGV("releaseFile: \"%s\"\n", fname);
while (s->next)
if (strcmp(s->next->fname, fname) == 0) {
DBGMSGV(" closing file #%i: \"%s\"\n", s->next->handle, s->next->fname);
#ifdef _WIN32
{
BOOL b;
b = CloseHandle((HANDLE)s->next->handle);
if (!b)
DBGMSGV(" failed to close file #%i (Error=%lu): %s\n", s->next->handle, GetLastError(), GetErrorStr(GetLastError()));
}
#else
if (close(s->next->handle))
DBGMSGV(" failed to close file #%i (errno=%lu): %s\n", s->next->handle, errno, strerror(errno));
#endif
s->next = rmHandle(s->next);
}
else
s = s->next;
}
int trackFile(char* fname, void* fcb, int fd) {
track_t* s = (track_t*)&openFiles;
DBGMSGV("trackFile: \"%s\", FCB=0x%X, Handle=%i\n", fname, (byte*)fcb - RAM, fd);
while (s->next) { /* find any existing fcb or fd */
if (s->next->fcb == fcb || s->next->handle == fd) {
if (s->next->handle != fd) {
DBGMSGV(" closing file #%i: \"%s\"\n", s->next->handle, s->next->fname);
#ifdef _WIN32
{
BOOL b;
b = CloseHandle((HANDLE)s->next->handle);
if (!b)
DBGMSGV(" failed to close file #%i (Error=%lu): %s\n", s->next->handle, GetLastError(), GetErrorStr(GetLastError()));
}
#else
if (close(s->next->handle))
DBGMSGV(" failed to close file #%i (errno=%lu): %s\n", s->next->handle, errno, strerror(errno));
#endif
}
DBGMSGV(" released file \"%s\", Handle=%i\n", s->next->fname, s->next->handle);
s->next = rmHandle(s->next); /* release the tracker */
}
else
s = s->next;
}
if (fname && fd >= 0) {
if ((s = malloc(sizeof(track_t))) == NULL) {
fprintf(stderr, "out of memory\n");
exit(1);
}
s->next = openFiles;
s->fname = strdup(fname);
s->fcb = fcb;
s->handle = fd;
openFiles = s;
}
return fd;
}
#else
void releaseFile(char* fname) {}
int trackFile(char* fname, void* fcb, int fd) { return fd; }
#endif