www.pudn.com > tfs.rar > TFS.C
/* tfs.c:
* Tiny File System
* TFS supports the ability to store/access files in flash. The TFS
* functions provide a command at the monitor's user interface (the
* "tfs" command) as well as a library of functions that are available to
* the monitor/application code on this target (TFS api).
*
* The code that supports TFS in the MicroMonitor package spans across
* several files. This is done so that various pieces of TFS can optionally
* be compiled in or out (using INCLUDE_XXX macros in config.h) of the
* monitor package...
*
* tfs.c:
* Core TFS code that cannot be optionally omitted without eliminating
* the TFS facility from the monitor.
*
* tfsapi.c:
* This file contains the code that supports the application's ability
* to use the TFS api. Since some of the api is used by the monitor
* itself, not all of the api-specific code is there, some of it is
* in tfs.c.
*
* tfscleanX.c:
* TFS can be configured with one of several different flash defrag
* mechanisms. Currently, tfsclean[123].c are available.
*
* tfscli.c:
* If you don't need the "tfs" command in your command line interface,
* then the code in this file can be omitted.
*
* tfsloader.c:
* TFS can support COFF, ELF or A.OUT binary file formats. The code
* to load each of these formats from flash to RAM is here.
*
* tfslog.c:
* If there is a need to log flash interaction to a file, then this
* file contains code to support that.
*
*
* NOTES:
* * Dealing with multiple task access:
* Since the monitor is inherently a single threaded program
* potentially being used in a multi-tasking environment, the monitor's
* access functions (API) must be provided with a lock/unlock
* wrapper that will guarantee sequential access to all of the monitor
* facilities. Refer to monlib.c to see this implementation. This
* provides the protection needed by TFS to keep multiple "mon_"
* functions from being executed by different tasks.
* Note that originally this was supported with tfsctrl(TFS_MUTEX ) and
* it only protected the tfs API functions. This turned out to be
* insufficient because it did not prevent other tasks from calling
* other non-tfs functions in the monitor while tfs access (and
* potentially, flash update) was in progress. This meant that a flash
* update could be in progress and some other task could call mon_getenv()
* (for example). This could screw up the flash update because
* mon_getenv() might be fetched out of the same flash device that
* the TFS operation is being performed on.
*
* * Dealing with cache coherency:
* I believe the only concern here is that Icache must be invalidated
* and Dcache must be flushed whenever TFS does a memory copy that may
* ultimately be executable code. This is handled at the end of the
* tfsmemcpy function by calling flushDcache() and invalidateIcache().
* It is the application's responsibility to give the monitor the
* appropriate functions (see assigncachefuncs()) if necessary.
*
* * Configuring a device to run as TFS memory:
* Assuming you are using power-safe cleanup...
* TFS expects that on any given device used for storage of files, the
* device is broken up into some number of sectors with the last sector
* being the largest and used as the spare sector for defragmentation.
* All other sector sizes must be smaller than the SPARE sector and the
* sector just prior to the spare is used for defragmentation state
* overhead. This sector should be large enough to allow the overhead
* space to grow down from the top without filling the sector. For most
* flash devices, these two sectors (spare and overhead) are usually the
* same size and are large. For FlashRam, the device should be configured
* so that these two sectors are large. The spare sector will never be
* allowed to contain any file information (because it is 100% dedicated to
* the defragmentation process) and the sector next to this can have files
* in it, but the overhead space is also in this sector.
*
* * Testing TFS:
* There are three files dedicated to testing the file system. Two of them
* (tfstestscript & tfstestscript1) are scripts that are put into the
* file system and run. The third file (tfstest.c) is a piece of code
* that can be built into a small application that runs out of TFS to test
* all of the API functionality.
* - tfstestscript:
* This script is used to simply bang on normal defragmentation. It
* builds files with sizes and names based on the content of memory
* starting at $APPRAMBASE. Changing the content of memory starting at
* $APPRAMBASE will change the characteristics of this test so it is
* somewhat random. It is not 100% generic, but can be used as a
* base for testing TFS on various systems.
* - tfstestscript1:
* This script is used to bang on the power-safe defragmentation of
* TFS. It simulates power hits that might occur during defragmentation.
* This script assumes that the monitor has been built with the
* DEFRAG_TEST_ENABLED flag set.
* - tfstest.c:
* This code can be built into a small application that will thoroughly
* exercise the TFS API. This file can also be used as a reference for
* some examples of TFS api usage.
*
* General notice:
* This code is part of a boot-monitor package developed as a generic base
* platform for embedded system designs. As such, it is likely to be
* distributed to various projects beyond the control of the original
* author. Please notify the author of any enhancements made or bugs found
* so that all may benefit from the changes. In addition, notification back
* to the author will allow the new user to pick up changes that may have
* been made by other users after this version of the code was distributed.
*
* Note1: the majority of this code was edited with 4-space tabs.
* Note2: as more and more contributions are accepted, the term "author"
* is becoming a mis-representation of credit.
*
* Original author: Ed Sutter
* Email: esutter@lucent.com
* Phone: 908-582-2351
*/
#include "config.h"
//#include "cpu.h"
#include "stddefs.h"
#include "genlib.h"
#include "tfs.h"
#include "tfsprivate.h"
#include "tfsdev.h"
#include "flash.h"
#include "frmwrk.h"
//#include "cli.h"
#if INCLUDE_TFS
char *(*tfsGetAtime)(long,char *,int);
long (*tfsGetLtime)(void);
int (*tfsDocommand)(char *,int);
TDEV tfsDeviceTbl[TFSDEVTOT];
TFILE **tfsAlist;
struct tfsdat tfsSlots[TFS_MAXOPEN];
long tfsTrace;
int TfsCleanEnable;
static long tfsFmodCount;
static int tfsAlistSize, tfsOldDelFlagCheckActive;
#define APPLICATION_RAMSTART SDRAM_SADDR
/* tfsflgtbl & tfserrtbl:
* Tables that establish an easy lookup mechanism to convert from
* bitfield to string or character.
* Note that TFS_ULVL0 is commented out. I leave it in here as a place
* holder (comment), but it actually is not needed becasue ulvl_0 is the
* default if no other ulvl is specified.
*/
struct tfsflg tfsflgtbl[] = {
{ TFS_BRUN, 'b', "run_at_boot", TFS_BRUN },
{ TFS_QRYBRUN, 'B', "qry_run_at_boot", TFS_QRYBRUN },
{ TFS_EXEC, 'e', "executable", TFS_EXEC },
{ TFS_SYMLINK, 'l', "symbolic link", TFS_SYMLINK },
{ TFS_EBIN, 'E', TFS_EBIN_NAME, TFS_EBIN },
{ TFS_IPMOD, 'i', "inplace_modifiable", TFS_IPMOD },
{ TFS_UNREAD, 'u', "ulvl_unreadable", TFS_UNREAD },
/* { TFS_ULVL0, '0', "ulvl_0", TFS_ULVLMSK }, */
{ TFS_ULVL1, '1', "ulvl_1", TFS_ULVLMSK },
{ TFS_ULVL2, '2', "ulvl_2", TFS_ULVLMSK },
{ TFS_ULVL3, '3', "ulvl_3", TFS_ULVLMSK },
{ TFS_CPRS, 'c', "compressed", TFS_CPRS },
{ 0, 0, 0, 0 }
};
static struct tfserr tfserrtbl[] = {
{ TFS_OKAY, "no error" },
{ TFSERR_NOFILE, "file not found" },
{ TFSERR_NOSLOT, "max fps opened" },
{ TFSERR_EOF, "end of file" },
{ TFSERR_BADARG, "bad argument" },
{ TFSERR_NOTEXEC, "not executable" },
{ TFSERR_BADCRC, "bad crc" },
{ TFSERR_FILEEXISTS, "file already exists" },
{ TFSERR_FLASHFAILURE, "flash operation failed" },
{ TFSERR_WRITEMAX, "max write count exceeded" },
{ TFSERR_RDONLY, "file is read-only" },
{ TFSERR_BADFD, "invalid descriptor" },
{ TFSERR_BADHDR, "bad binary executable header" },
{ TFSERR_CORRUPT, "corrupt file" },
{ TFSERR_MEMFAIL, "memory failure" },
{ TFSERR_NOTIPMOD, "file is not in-place-modifiable" },
{ TFSERR_FLASHFULL, "out of flash space" },
{ TFSERR_USERDENIED, "user level access denied" },
{ TFSERR_NAMETOOBIG, "name or info field too big" },
{ TFSERR_FILEINUSE, "file in use" },
{ TFSERR_SCRIPTINSUB, "can't put script in subroutine" },
{ TFSERR_NOTAVAILABLE, "tfs facility not available" },
{ TFSERR_BADFLAG, "bad flag" },
{ TFSERR_CLEANOFF, "defragmentation is disabled" },
{ TFSERR_FLAKEYSOURCE, "dynamic source data" },
{ 0,0 }
};
/* getAppRamStart():
* First looks for the content of APPRAMBASE shell variable;
* if present, that string is converted to a long and returned,
* else the value of APPLICATION_RAMSTART is returned.
*/
ulong
getAppRamStart(void)
{
char *apprambase;
ulong value;
apprambase = getenv("APPRAMBASE");
if (apprambase)
value = strtoul(apprambase,0,0);
else
value = APPLICATION_RAMSTART;
return(value);
}
/* dummyAtime() & dummyLtime():
* These two functions are loaded into the function pointers as defaults
* for the time-retrieval stuff used in TFS.
*/
static char *
dummyAtime(long tval,char *buf,int buflen)
{
/* strcpy(buf,"Fri Sep 13 00:00:00 1986"); */
*buf = 0;
return(buf);
}
static long
dummyLtime(void)
{
return(TIME_UNDEFINED);
}
/* getdfsdev():
* Input is a file pointer; based on that pointer return the appropriate
* device header pointer. If error, just return 0.
* A "device" in TFS is some block of some type of memory that is assumed
* to be contiguous space that can be configured as a block of sectors (to
* look like flash). For most systems, there is only one (the flash);
* other systems may have battery-backed RAM, etc...
* Note that this is not fully implemented.
*/
static TDEV *
gettfsdev(TFILE *fp)
{
TDEV *tdp;
for(tdp=tfsDeviceTbl;tdp->start != TFSEOT;tdp++) {
if ((fp >= (TFILE *)tdp->start) &&
(fp < (TFILE *)tdp->end))
return(tdp);
}
return(0);
}
TDEV *
gettfsdev_fromprefix(char * prefix, int verbose)
{
TDEV *tdp;
for(tdp=tfsDeviceTbl;tdp->start != TFSEOT;tdp++) {
if (!strcmp(prefix,tdp->prefix))
return(tdp);
}
if (verbose)
printf("Bad device prefix: %s\n",prefix);
return(0);
}
/* tfsflasherase(), tfsflasheraseall() & tfsflashwrite():
* Wrappers for corresponding flash operations. The wrappers are used
* to provide one place for the incrmentation of tfsFmodCount.
*/
int
tfsflasheraseall(TDEV *tdp)
{
int snum, last;
if (tfsTrace > 2)
printf(" tfsflasheraseall(%s)\n",tdp->prefix);
tfsFmodCount++;
/* Erase the sectors within the device that are used for file store... */
if (addrtosector((uchar *)tdp->start,&snum,0,0) < 0)
return(TFSERR_MEMFAIL);
last = snum + tdp->sectorcount;
while(snum < last) {
if (AppFlashErase(snum++) == -1)
return(TFSERR_MEMFAIL);
}
/* Erase the spare (if there is one)...
* (if this system is configured with tfsclean2.c, then
* there is no need for a spare sector).
*/
if (tdp->spare) {
if (addrtosector((uchar *)tdp->spare,&snum,0,0) < 0)
return(TFSERR_MEMFAIL);
if (AppFlashErase(snum) == -1)
return(TFSERR_MEMFAIL);
}
return(TFS_OKAY);
}
int
tfsflasherase(int snum)
{
if (tfsTrace > 2)
printf(" tfsflasherase(%d)\n",snum);
tfsFmodCount++;
return(AppFlashErase(snum));
}
int
tfsflashwrite(ulong *dest,ulong *src,long bytecnt)
{
if (tfsTrace > 2)
printf(" tfsflashwrite(0x%lx,0x%lx,%ld)\n",
(ulong)dest,(ulong)src,bytecnt);
if (bytecnt < 0)
return(-1);
tfsFmodCount++;
return(AppFlashWrite(dest,src,bytecnt));
}
/* tfserrmsg():
* Return the error message string that corresponds to the incoming
* tfs error number.
*/
char *
tfserrmsg(int errno)
{
struct tfserr *tep;
tep = tfserrtbl;
while(tep->msg) {
if (errno == tep->err)
return(tep->msg);
tep++;
}
return("unknown tfs errno");
}
/* tfsmakeStale():
* Modify the state of a file to be stale.
* Do this by clearing the TFS_NOTSTALE flag in the tfs header.
* This function is used by tfsadd() when in the process of
* updating a file that already exists in the flash.
* See comments above tfsadd() for more details on the TFS_NOTSTALE flag.
*/
static int
tfsmakeStale(TFILE *tfp)
{
ulong flags;
flags = TFS_FLAGS(tfp) & ~TFS_NSTALE;
if (tfsflashwrite((ulong *)&tfp->flags,&flags,(long)sizeof(long)) < 0)
return(TFSERR_FLASHFAILURE);
return(TFS_OKAY);
}
/* tfsflagsbtoa():
* Convert binary flags to ascii and return the string.
*/
char *
tfsflagsbtoa(long flags,char *fstr)
{
int i;
struct tfsflg *tfp;
if ((!flags) || (!fstr))
return((char *)0);
i = 0;
tfp = tfsflgtbl;
*fstr = 0;
while(tfp->sdesc) {
if ((flags & tfp->mask) == tfp->flag)
fstr[i++] = tfp->sdesc;
tfp++;
}
fstr[i] = 0;
return(fstr);
}
/* tfsflagsatob():
* Convert ascii flags to binary and return the long.
*/
static int
tfsflagsatob(char *fstr, long *flag)
{
struct tfsflg *tfp;
*flag = 0;
if (!fstr)
return(TFSERR_BADFLAG);
while(*fstr) {
tfp = tfsflgtbl;
while(tfp->sdesc) {
if (*fstr == tfp->sdesc) {
*flag |= tfp->flag;
break;
}
tfp++;
}
if (!tfp->flag)
return(TFSERR_BADFLAG);
fstr++;
}
return(TFS_OKAY);
}
/* hdrcrc():
* The crc of the file header was originally calculated (in tfsadd())
* with the header crc and next pointer nulled out; so a copy must
* be made and these two fields cleared. Also, note that the
* TFS_NSTALE and TFS_ACTIVE flags are forced to be set in the copy.
* This is done because it is possible that either of these bits may
* have been cleared due to other TFS interaction; hence, they need
* to be set prior to crc calculation.
* Note also that earlier versions of TFS deleted a file by clearing
* the entire flags field. This made it impossible to do a header crc
* check on a deleted file; deletion has been changed to simply clear
* the TFS_ACTIVE bit in the flags, so now a deleted file's header can
* can be crc tested by simply forcing the TFS_ACTIVE bit high as was
* mentioned above.
*/
ulong
tfshdrcrc(TFILE *hdr)
{
TFILE hdrcpy;
hdrcpy = *hdr;
hdrcpy.next = 0;
hdrcpy.hdrcrc = 0;
hdrcpy.flags |= (TFS_NSTALE | TFS_ACTIVE);
return(crc32((uchar *)&hdrcpy,TFSHDRSIZ));
}
/* validtfshdr():
* Return 1 if the header pointed to by the incoming header pointer is valid.
* Else return 0. The header crc is calculated based on the hdrcrc
* and next members of the structure being zero.
* Note that if the file is deleted, then just ignore the crc and return 1.
*/
int
validtfshdr(TFILE *hdr)
{
/* A few quick checks... */
if (!hdr || hdr->hdrsize == ERASED16)
return(0);
if (tfshdrcrc(hdr) == hdr->hdrcrc) {
return(1);
}
else {
/* Support transition to new deletion flag method... */
if ((hdr->flags == 0) && tfsOldDelFlagCheckActive)
return(1);
printf("Bad TFS hdr crc @ 0x%lx\n",(ulong)hdr);
return(0);
}
}
/* nextfp():
* Used as a common means of retrieving the next file header pointer. It
* does some sanity checks based on the fact that all pointers must fall
* within the TFSSTART<->TFSEND memory range and since each file is placed
* just after the previous one in linear memory space, fp->next should
* always be greater than fp.
*/
TFILE *
nextfp(TFILE *fp, TDEV *tdp)
{
if (!tdp)
tdp = gettfsdev(fp);
/* Make some basic in-range checks... */
if ((!tdp) || (fp < (TFILE *)tdp->start) || (fp > (TFILE *)tdp->end) ||
(fp->next < (TFILE *)tdp->start) || (fp->next > (TFILE *)tdp->end) ||
(fp->next <= fp)) {
printf("Bad TFS hdr ptr @ 0x%lx\n",(ulong)fp);
return(0);
}
return(fp->next);
}
/* tfsflasherased():
* Jump to the point in flash after the last file in TFS, then verify
* that all remaining flash that is dedicated to TFS is erased (0xff).
* If erased, return 1; else return 0.
*/
int
tfsflasherased(TDEV *tdp, int verbose)
{
ulong *lp;
TFILE *tfp;
tfp = (TFILE *)tdp->start;
while(validtfshdr(tfp))
tfp = nextfp(tfp,tdp);
lp = (ulong *)tfp;
while (lp < (ulong *)tdp->end) {
if (*lp != ERASED32) {
if (verbose)
printf("End of TFS on %s not erased at 0x%lx\n",
tdp->prefix,(ulong)lp);
return(0);
}
#ifdef WATCHDOG_MACRO
WATCHDOG_MACRO();
#endif
lp++;
}
return(1);
}
static int
tfsftot(TDEV *tdpin)
{
int ftot;
TFILE *tfp;
TDEV *tdp;
ftot = 0;
for (tdp=tfsDeviceTbl;tdp->start != TFSEOT;tdp++) {
if (!tdpin || (tdpin == tdp)) {
tfp = (TFILE *)tdp->start;
while(validtfshdr(tfp)) {
if (TFS_FILEEXISTS(tfp))
ftot++;
tfp = nextfp(tfp,tdp);
}
}
}
return(ftot);
}
/* tfsmemuse():
* Step through one (or all) TFS devices and tally up various memory usage
* totals. See definition of tfsmem structure for more details.
* If incoming tdpin pointer is NULL, then tally up for all TFS devices;
* otherwise, tally up for only the one device pointed to by tdpin.
*/
int
tfsmemuse(TDEV *tdpin, TINFO *tinfo, int verbose)
{
int devtot;
char *cfgerr;
TFILE *tfp;
TDEV *tdp;
/* Start by clearing incoming structure... */
tinfo->pso = 0;
tinfo->sos = 0;
tinfo->memtot = 0;
tinfo->liveftot = 0;
tinfo->deadftot = 0;
tinfo->livedata = 0;
tinfo->deaddata = 0;
tinfo->liveovrhd = 0;
tinfo->deadovrhd = 0;
if (verbose) {
printf("TFS Memory Usage...\n ");
printf(" name start end spare spsize scnt type\n");
}
devtot = 0;
for (tdp=tfsDeviceTbl;tdp->start != TFSEOT;tdp++) {
if (!tdpin || (tdpin == tdp)) {
devtot++;
tfp = (TFILE *)tdp->start;
cfgerr = (char *)0;
/* Do some sanity checks on the configuration... */
if ((tdp->spare >= tdp->start) && (tdp->spare <= tdp->end)) {
cfgerr = "spare within storage space";
}
if (cfgerr) {
printf("Bad %s TFS config: %s.\n",tdp->prefix,cfgerr);
}
if (verbose) {
printf("%10s: 0x%08lx|0x%08lx|0x%08lx|0x%06lx|%4ld|0x%lx\n",
tdp->prefix,(ulong)(tdp->start),(ulong)(tdp->end),
(ulong)(tdp->spare),tdp->sparesize,
tdp->sectorcount,(ulong)(tdp->devinfo));
}
tinfo->memtot += ((tdp->end - tdp->start) + 1) + tdp->sparesize;
tinfo->pso += (tdp->sectorcount * 4) + 16;
tinfo->sos += tdp->sparesize;
while(validtfshdr(tfp)) {
if (TFS_FILEEXISTS(tfp)) {
tinfo->liveftot++;
tinfo->livedata += TFS_SIZE(tfp);
tinfo->liveovrhd += (TFSHDRSIZ + DEFRAGHDRSIZ);
}
else {
tinfo->deadftot++;
tinfo->deaddata += TFS_SIZE(tfp);
tinfo->deadovrhd += TFSHDRSIZ;
}
tfp = nextfp(tfp,tdp);
}
}
}
tinfo->memused = tinfo->livedata + tinfo->liveovrhd +
tinfo->deaddata + tinfo->deadovrhd + tinfo->pso + tinfo->sos;
tinfo->memfree = tinfo->memtot - tinfo->memused;
/* Remaining space may not even be big enough to contain the
* file overhead, if this is the case, show a remaining space
* of zero rather than a negative number...
*/
tinfo->memfordata =
tinfo->memfree - (devtot * (TFSHDRSIZ + DEFRAGHDRSIZ));
if (tinfo->memfordata < 0)
tinfo->memfordata = 0;
if (verbose) {
printf("\n Total memory: %d bytes (used=%d, avail=%d (%d for data)).\n",
tinfo->memtot,tinfo->memused,tinfo->memfree, tinfo->memfordata);
printf(" Per-device overhead: %d bytes ",tinfo->pso+tinfo->sos);
printf("(defrag-state=%d spare-sector=%d).\n",tinfo->pso,tinfo->sos);
printf(" File data space: %d bytes (live=%d, dead=%d).\n",
tinfo->livedata+tinfo->deaddata,
tinfo->livedata,tinfo->deaddata);
printf(" File overhead space: %d bytes (live=%d, dead=%d).\n",
tinfo->liveovrhd+tinfo->deadovrhd,
tinfo->liveovrhd,tinfo->deadovrhd);
printf(" File count: %d (live=%d, dead=%d).\n",
tinfo->liveftot+tinfo->deadftot,tinfo->liveftot,tinfo->deadftot);
printf(" Defrag will release %d bytes\n",
tinfo->deadovrhd+tinfo->deaddata);
printf("\n");
}
return(tinfo->liveftot + tinfo->deadftot);
}
/* tfscheck():
* Step through each file in a particular device making a few checks...
* - First look at the header. If hdrsize is erased, it "should" indicate
* the end of the linear list of files. To be anal about it, verify that
* the entire header is erased. If it is, we truly are at the end of the
* list; otherwise, header error.
* - Second, do a crc32 on the header.
* - Third, if the file is not deleted, then do a crc32 on the data portion
* of the file (if the file is deleted, then it really doesn't matter if
* there is a crc32 error on that data).
* - Finally, if the header is not corrupted, index to the next pointer and
* continue. If the header is corrupt, see if enough information
* in the header is valid to allow us to step to the next file. Do this
* by calculating where the next pointer should be (using current pointer,
* file+header size and mod16 adjustment) and then see if that matches the
* value stored in the actual "next" pointer. If yes, go to next file;
* else break out of the loop.
*
* The purpose is to do more sophisticated file system checks than are
* done in normal TFS operations.
*/
#define TFS_CORRUPT 1
#define HDR_CORRUPT 2
#define DATA_CORRUPT 4
int
tfscheck(TDEV *tdp, int verbose)
{
int tfscorrupt, filtot;
TFILE *fp, *fp1;
if (!tdp)
return(TFSERR_BADARG);
if (verbose)
printf("TFS device %s check:\n",tdp->prefix);
filtot = tfscorrupt = 0;
fp = (TFILE *)tdp->start;
while(1) {
tfscorrupt &= ~(HDR_CORRUPT | DATA_CORRUPT);
/* If hdrsize is ERASED16, then verify that the whole header is
* also ERASED16, if yes, we're at the end of the linear list of
* files; otherwise, we have a corrupt header.
*/
if (fp->hdrsize == ERASED16) {
int i;
ushort *sp;
/* If this is right at the edge of the end of the TFS device,
* then break with no further checks to this header.
*/
if ((fp+1) > (TFILE *)tdp->end)
break;
/* Make sure the entire header is erased... */
sp = (ushort *)fp;
for(i=0;ihdrcrc) {
if (verbose)
printf(" CRC error in hdr @ 0x%lx\n",(ulong)fp);
tfscorrupt = HDR_CORRUPT | TFS_CORRUPT;
goto nextfile;
}
/* If file exists, and it's not IPMOD, run a crc check on data... */
if (TFS_FILEEXISTS(fp) && !(fp->flags & TFS_IPMOD)) {
filtot++;
if (verbose)
printf(" %s...",fp->name);
if ((!(fp->flags & TFS_IPMOD)) &&
(crc32((uchar*)TFS_BASE(fp),fp->filsize) != fp->filcrc)) {
if (verbose)
printf(" CRC error in data");
tfscorrupt = DATA_CORRUPT | TFS_CORRUPT;
}
else {
if (verbose)
printf(" ok");
}
}
/* Prior to incrementing to the next file pointer, if the header
* is corrupted, attempt to salvage the next pointer...
* If the value of the next pointer matches what is calculated
* from the file size and header size, then assume it is ok
* and allow the tfscheck() loop to continue; otherwise break.
*/
nextfile:
if (tfscorrupt & HDR_CORRUPT) {
if (fp->next) {
ulong modnext;
modnext = (ulong)((int)(fp+1) + fp->filsize);
if (modnext & 0xf) {
modnext += 16;
modnext &= ~0xf;
}
if (verbose)
printf(" (next ptr ");
if (fp->next != (TFILE *)modnext) {
if (verbose)
printf("damaged)\n");
break;
}
else {
if (verbose)
printf("salvaged)");
}
}
}
fp1 = nextfp(fp,tdp);
if (!fp1) {
tfscorrupt = HDR_CORRUPT | TFS_CORRUPT;
break;
}
if ((verbose) && (TFS_FILEEXISTS(fp) || tfscorrupt))
putchar('\n');
fp = fp1;
}
tfsflasherased(tdp,verbose);
if (tfscorrupt)
return(TFSERR_CORRUPT);
if (verbose)
printf(" PASSED\n");
return (TFS_OKAY);
}
void
tfsclear(TDEV *tdp)
{
int i;
/* Clear the fileslot[] table indicating that no files are opened.
* Only clear the slots applicable to the incoming TDEV pointer.
*/
for (i = 0; i < TFS_MAXOPEN; i++) {
ulong offset;
offset = tfsSlots[i].offset;
if (offset != (ulong)-1) {
if ((tdp == (TDEV *)0) ||
((offset >= tdp->start) && (offset <= tdp->end)))
tfsSlots[i].offset = -1;
}
}
/* If the incoming TDEV pointer is NULL, then we can assume a global
* clear and go ahead and cleanup everything; otherwise, we just return
* here.
*/
if (tdp != (TDEV *)0)
return;
/* Turn off tracing. */
tfsTrace = 0;
/* Init the time retrieval function pointers to their dummy values. */
tfsGetAtime = dummyAtime;
tfsGetLtime = dummyLtime;
/* Default to using standard docommand() within scripts. */
//comment by xxd 2003/9/10
//tfsDocommand = docommand;
/* Start off with a buffer for 16 files. This is probably more than
* will be used, so it avoids reallocations in tfsreorder().
*/
tfsAlistSize = 16;
tfsAlist = (TFILE **)malloc((tfsAlistSize+1) * sizeof(TFILE **));
if (!tfsAlist) {
printf("tfsclear(): tfsAlist allocation failed\n");
tfsAlistSize = 0;
}
}
/* tfsstalecheck():
* Called at startup to clean up any file that may be in STALE mode.
* A file is stale if it was in the process of being modified
* and a power hit occurred. Refer to notes in tfsadd() for details.
* There are a few cases to be covered here...
* 1. there is no stale file; so there is nothing to do.
* 2. there is a stale file, but no other file with the same name...
* In this case, the stale file must be copied to another file (with the
* TFS_NSTALE flag set) and the stale file is deleted.
* 3. there is stale file and another file with the same name...
* In this case, the stale file is simply deleted because the other file
* with the same name is newer.
*/
static void
tfsstalecheck(int verbose)
{
int err;
ulong flags;
TDEV *tdp;
TFILE *tfp, *tfpa;
char buf[16];
tfpa = (TFILE *)0;
for(tdp=tfsDeviceTbl;tdp->start != TFSEOT;tdp++) {
tfp = (TFILE *)tdp->start;
tfpa = (TFILE *)0;
while(validtfshdr(tfp)) {
if (TFS_FILEEXISTS(tfp)) {
if (tfpa) {
if (!strcmp(TFS_NAME(tfp),TFS_NAME(tfpa))) {
_tfsunlink(TFS_NAME(tfpa));
return;
}
}
else if (TFS_STALE(tfp)) {
tfpa = tfp;
}
}
tfp = nextfp(tfp,tdp);
}
if (tfpa)
break;
}
if (tfpa) {
char name[TFSNAMESIZE+1];
strcpy(name,TFS_NAME(tfpa));
if (verbose)
printf("TFS stale fixup (%s)...\n",name);
flags = TFS_FLAGS(tfpa) | TFS_NSTALE;
err = tfsadd(TFS_NAME(tfpa),TFS_INFO(tfpa),tfsflagsbtoa(flags,buf),
(uchar *)(TFS_BASE(tfpa)),TFS_SIZE(tfpa));
/* If rewrite was successful, then remove the stale one;
* else, leave it there and report the error.
*/
if (err == TFS_OKAY) {
_tfsunlink(TFS_NAME(tfpa));
}
else {
printf("TFS stalecheck(%s) error: %s\n",name,tfserrmsg(err));
}
}
}
/* tfsdevtblinit():
* Transfer the information in tfsdevtbl (in tfsdev.h) to tfsDeviceTbl[].
* In most cases, this will be a simple copy. If the device flag is set
* to indicate that the initalization is dynamic, then use the flash
* ops to retrieve the information from the specified bank.
*
* For dynamic configuration, the "start" member of the tfsdev structure
* must be set in tfsdev.h and the "devinfo & TFS_DEVINFO_BANKMASK" area
* must contain the number of the last flash bank that is to be part of
* the TFS device. Typically this value is the same bank number as the
* starting bank, but it could span across multiple contiguous banks
* if the hardware is set up that way.
*
* To support the use of top-boot devices, plus the TFS requirement that
* the SPARE sector be at-least as large as any other sector in the device,
* this code will automatically step down the sector list until it finds
* the first large sector below all the small ones usually at the top of
* a top-boot device. The call to lastlargesector() takes care of this.
*
* NOTE:
* This dynamic configuration assumes that the end of the TFS space is
* just below the beginning of the spare space.
*
*/
void
tfsdevtblinit(void)
{
int i, startsector, endsector, bank;
TDEV *tDp, *tdp;
for(i=0;idevinfo & TFS_DEVINFO_DYNAMIC) {
bank = tDp->devinfo & TFS_DEVINFO_BANKMASK;
/* The spare sector may not be the last sector in the device...
* device. Especially if the device is TopBoot type.
*/
if (lastlargesector(bank,&endsector,
(int *)&tDp->sparesize,(uchar **)&tDp->spare) == -1)
break;
tDp->end = tDp->spare - 1;
if (addrtosector((uchar *)tDp->start,&startsector,0,0) == -1)
break;
tDp->sectorcount = endsector - startsector;
}
}
}
/* tfsstartup():
* Called at system startup to get things properly initialized.
*/
void
tfsstartup()
{
tfsdevtblinit();
tfsclear((TDEV *)0);
tfsfixup(3,0);
tfsstalecheck(1);
}
/* tfsexec: Treat the file as machine code that is COFF or ELF. */
static int
tfsexec(TFILE *fp,int verbose)
{
int err, (*entry)();
long address;
err = tfsloadebin(fp,verbose,&address,0);
if (err != TFS_OKAY)
return(err);
entry = (int(*)())address;
ctxAPP(); /* Change context to APPLICATION. */
entry(); /* Call entrypoint (may not return). */
ctxMON(); /* Change context to APPLICATION. */
return(TFS_OKAY);
}
/* tfsmemset():
* Superset of memset(). Includes verbose option plus verification after
* set.
*/
int
tfsmemset(uchar *to,uchar val,int count,int verbose,int verifyonly)
{
int failed;
uchar *end;
failed = 0;
if (verbose) {
printf("%s %7d bytes at 0x%08lx to 0x%02x",
verifyonly ? "vrfy" : "set ",count,(ulong)to,val);
}
if (count == 0)
goto done;
end = to+count;
if (verifyonly) {
while(to < end) {
if (*to++ != val) {
failed = 1;
break;
}
}
}
else {
while(to < end) {
*to = val;
if (*to++ != val) {
failed = 1;
break;
}
}
}
done:
if (verbose) {
if (failed)
printf(" failed");
else if (verifyonly)
printf(" OK");
printf("\n");
}
if (failed)
return(TFSERR_MEMFAIL);
else
return(TFS_OKAY);
}
/* tfsmemcpy():
* Superset of memcpy(). Includes verbose option plus verification after
* copy. Takes advantage of address alignment when possible.
*/
int
tfsmemcpy(uchar *to,uchar *from,int count,int verbose,int verifyonly)
{
int err;
register uchar *end;
if (verbose)
printf("%s %7d bytes from 0x%08lx to 0x%08lx",
verifyonly ? "vrfy" : "copy", count,(ulong)from,(ulong)to);
if (verifyonly) {
while(count) {
if (*to != *from)
break;
to++;
from++;
count--;
}
if (count) {
if (verbose) {
printf(" FAILED\n");
printf(" (0x%02x @ 0x%08lx should be 0x%02x)\n",
*to,(ulong)to,*from);
}
return(TFSERR_MEMFAIL);
}
else
if (verbose)
printf(" OK\n");
return(TFS_OKAY);
}
if (count == 0)
goto done;
if (to != from) {
err = 0;
if (!((int)to & 3) && !((int)from & 3) && !(count & 3)) {
register ulong *lto, *lfrom, *lend;
count >>= 2;
lto = (ulong *)to;
lfrom = (ulong *)from;
lend = lto + count;
while(lto < lend) {
*lto = *lfrom;
if (*lto != *lfrom) {
err = 1;
break;
}
lto++;
lfrom++;
}
}
else if (!((int)to & 1) && !((int)from & 1) && !(count & 1)) {
register ushort *sto, *sfrom, *send;
count >>= 1;
sto = (ushort *)to;
sfrom = (ushort *)from;
send = sto + count;
while(sto < send) {
*sto = *sfrom;
if (*sto != *sfrom) {
err = 1;
break;
}
sto++;
sfrom++;
}
}
else {
end = to + count;
while(to < end) {
*to = *from;
if (*to != *from) {
err = 1;
break;
}
to++;
from++;
}
}
if (err) {
if (verbose)
printf(" failed\n");
return(TFSERR_MEMFAIL);
}
}
done:
if (verbose)
printf("\n");
return(TFS_OKAY);
}
/* struct tfsran:
Used by tfsrunboot only. No need to put this in tfs.h.
*/
struct tfsran {
char name[TFSNAMESIZE+1];
};
/* tfsrunboot():
* This function is called at monitor startup. It scans the list of
* files built by tfsreorder() and executes each file in the list that has
* the BRUN flag set. As each file is run its name is added to the
* ranlist[] table.
*
* After each file is run, there is a check made to see if the flash has
* been modified. If yes, then tfsreorder() is run again and we start
* over at the top of the list of files organized by tfsreorder(). As
* we step through the tfsAlist[] array, if the file has a BRUN flag set
* but it is already in the ranlist[] table, it is not run again.
*
* This scheme allows a file in the initial list of BRUN files to modify
* the file list without confusing the list of files that are to be run.
* Files (even new BRUN files) can be added to the list by some other BRUN
* file, and these new files will be run.
*/
int
tfsrunboot()
{
static struct tfsran *ranlist;
char *argv[2];
int rancnt, aidx, ridx, err, fmodcnt;
/* The argv[] array is used by tfsrun(); argv[0] is name of file to be
* executed, argv[1] must be nulled to indicate no command line args
* passed to the BRUN file/script.
*/
argv[1] = (char *)0;
/* Keep a local copy of tfsFmodCount so that we can determine if flash
* was modified by one of the BRUN files executed.
*/
fmodcnt = tfsFmodCount;
/* Create list of file pointers (tfsAlist[]) in alphabetical order
* based on name...
*/
if ((err = tfsreorder()) < 0) {
printf("tfsrunboot() reorder1: %s\n",tfserrmsg(err));
return(-1);
}
/* Clear the ranlist pointer. This pointer is the base address of a
* list of file names that have been run.
*/
rancnt = 0;
ranlist = (struct tfsran *)0;
restartloop:
for (aidx=0;tfsAlist[aidx];aidx++) {
char fname[TFSNAMESIZE+1];
int alreadyran;
TFILE *fp;
struct tfsran *rp;
fp = tfsAlist[aidx];
strcpy(fname,TFS_NAME(fp));
/* If the file has no BRUN flag set, just continue. If a BRUN flag
* is set, then see if the file has already been run. If yes, then
* just continue; else run the file.
*/
alreadyran = 0;
if (fp->flags & (TFS_BRUN | TFS_QRYBRUN)) {
for(ridx=0;ridxflags & TFS_QRYBRUN) {
char query[TFSNAMESIZE+8];
sprintf(query,"%s?",fname);
if (pollConsole(query))
continue;
}
/* Increase the size of the ranlist[] table and add the file that
* is about to be run to that list...
*/
rancnt++;
rp = (struct tfsran*)realloc((char *)ranlist,
rancnt*sizeof(struct tfsran));
if (!rp) {
if (ranlist)
free((char *)ranlist);
printf("tfsrunboot() runlist realloc failure\n");
return(-1);
}
ranlist = rp;
strcpy(ranlist[rancnt-1].name,fname);
/* Run the executable... */
if ((err = tfsrun(argv,0)) != TFS_OKAY)
printf("%s: %s\n",fname,tfserrmsg(err));
/* If flash has been modified, then we must re-run tfsreorder() and
* start over...
*/
if (fmodcnt != tfsFmodCount) {
if ((err = tfsreorder()) < 0) {
printf("tfsrunboot() reorder2: %s\n",tfserrmsg(err));
return(err);
}
fmodcnt = tfsFmodCount;
goto restartloop;
}
}
if (ranlist)
free((char *)ranlist);
return(rancnt);
}
/* tfsreorder():
* Populate the tfsAlist[] array with the list of currently active file
* pointers, but put in alphabetical (lexicographical using strcmp()) order
* based on the filename.
* Note that after each file addition/deletion, this must be re-run.
*/
int
tfsreorder(void)
{
TFILE *fp;
TDEV *tdp;
int i, j, tot;
/* Determine how many valid files exist, and create tfsAlist array: */
tot = 0;
for(tdp=tfsDeviceTbl;tdp->start != TFSEOT;tdp++) {
fp = (TFILE *)tdp->start;
while(validtfshdr(fp)) {
if (TFS_FILEEXISTS(fp))
tot++;
fp = nextfp(fp,tdp);
}
}
/* If tfsAlist already exists, and is already big enough, then
* don't do any allocation; otherwise, create the array with one extra
* slot for a NULL pointer used elsewhere as an end-of-list indicator.
*/
if (tot > tfsAlistSize) {
tfsAlist = (TFILE **)realloc((char *)tfsAlist,
(tot+1) * sizeof(TFILE **));
if (!tfsAlist) {
tfsAlistSize = 0;
return(TFSERR_MEMFAIL);
}
tfsAlistSize = tot;
}
/* Clear the entire table (plus the extra one at the end): */
for(i=0;i<=tot;i++)
tfsAlist[i] = (TFILE *)0;
/* Populate tfsAlist[] with a pointer to each active file
* in flash as they exist in memory...
*/
i = 0;
for(tdp=tfsDeviceTbl;tdp->start != TFSEOT;tdp++) {
fp = (TFILE *)tdp->start;
while(validtfshdr(fp)) {
if (TFS_FILEEXISTS(fp)) {
tfsAlist[i++] = fp;
}
fp = nextfp(fp,tdp);
}
}
/* Now run a bubble sort on that list based on the lexicographical
* ordering returned by strcmp...
*/
for(i=1;i=i;--j) {
if (strcmp(TFS_NAME(tfsAlist[j-1]),TFS_NAME(tfsAlist[j])) > 0) {
fp = tfsAlist[j-1];
tfsAlist[j-1] = tfsAlist[j];
tfsAlist[j] = fp;
}
}
}
return(tot);
}
/* tfsheadroom():
* Based on the current offset into the file specified by the incoming
* descriptor, return the gap between the current offset and the end
* of the file.
*/
static long
tfsheadroom(int fd)
{
struct tfsdat *tdat;
if ((fd < 0) || (fd >= TFS_MAXOPEN))
return(TFSERR_BADARG);
tdat = &tfsSlots[fd];
if (tdat->flagmode & TFS_RDONLY)
return(tdat->hdr.filsize - tdat->offset);
else
return(tdat->hwp - tdat->offset);
}
/* tfstell():
* Return the offset into the file that is specified by the incoming
* descriptor.
* MONLIB NOTICE: this function is accessible through monlib.c.
*/
long
tfstell(int fd)
{
if ((fd < 0) || (fd >= TFS_MAXOPEN))
return(TFSERR_BADARG);
return(tfsSlots[fd].offset);
}
/* tfscompare():
* Compare the content of the file specified by tfp with the content pointed
* to by the remaining arguments. If identical, return 0; else return -1.
*/
static int
tfscompare(TFILE *tfp,char *name, char *info, char *flags, uchar *src, int size)
{
char flgbuf[16];
/* Compare size, name, info field, flags and data: */
/* Size... */
if (TFS_SIZE(tfp) != size)
return(-1);
/* Name... */
if (strcmp(name,TFS_NAME(tfp)))
return(-1);
/* Info field... */
if (info) {
if (strcmp(info,TFS_INFO(tfp)))
return(-1);
}
else {
if (TFS_INFO(tfp)[0] != 0)
return(-1);
}
/* Flags... */
tfsflagsbtoa(TFS_FLAGS(tfp),flgbuf);
if (flags) {
if (strcmp(flags,flgbuf))
return(-1);
}
else if (flgbuf[0] != 0)
return(-1);
/* Data... */
if (memcmp(TFS_BASE(tfp),(char *)src,size))
return(-1);
return(0);
}
/* tfsinit():
* Clear out all the flash that is dedicated to the file system.
* This removes all currently stored files and erases the flash.
* MONLIB NOTICE: this function is accessible through monlib.c.
*/
int
_tfsinit(TDEV *tdpin)
{
int ret;
TDEV *tdp;
/* Step through the table of TFS devices and erase each sector... */
for(tdp=tfsDeviceTbl;tdp->start != TFSEOT;tdp++) {
if (!tdpin || (tdp == tdpin)) {
ret = tfsflasheraseall(tdp);
if (ret != TFS_OKAY)
return(ret);
}
}
return(TFS_OKAY);
}
int
tfsinit(void)
{
if (tfsTrace > 0)
printf("tfsinit()\n");
return(_tfsinit(0));
}
/* tfsSpaceErased():
* Return 0 if the space pointed to by the incoming arguments is not
* erased; else 1.
*/
int
tfsSpaceErased(uchar *begin,int size)
{
uchar *end;
end = begin+size;
while(begin < end) {
if (*begin != 0xff)
return(0);
begin++;
}
return(1);
}
/* tfsFtot():
* Return the number of files in a device, or all devices if tdpin is null.
*/
int
tfsFtot(TDEV *tdpin)
{
int ftot;
TFILE *fp;
TDEV *tdp;
ftot = 0;
for(tdp=tfsDeviceTbl;tdp->start != TFSEOT;tdp++) {
if (!tdpin || (tdpin == tdp)) {
fp = (TFILE *)tdp->start;
while (fp->hdrsize != ERASED16) {
ftot++;
fp = nextfp(fp,tdp);
}
}
}
return(ftot);
}
/* tfsFileIsOpened():
* Return 1 if file is currently opened; else 0.
*/
int
tfsFileIsOpened(char *name)
{
int i;
struct tfsdat *slot;
slot = tfsSlots;
for (i=0;ioffset >= 0) && !strcmp(slot->hdr.name,name))
return(1);
}
return(0);
}
/* tfsunopen():
* If the incoming file descriptor is valid, mark that file as no-longer
* opened and return TFS_OKAY; else return TFSERR_BADARG.
* descriptor.
*/
static long
tfsunopen(int fd)
{
if ((fd < 0) || (fd >= TFS_MAXOPEN))
return(TFSERR_BADARG);
if (tfsSlots[fd].offset == -1)
return(TFSERR_BADARG);
tfsSlots[fd].offset = -1;
return(TFS_OKAY);
}
/* tfsctrl():
* Provides an ioctl-like interface to tfs.
* Requests supported:
* TFS_ERRMSG: Return error message (char *) corresponding to
* the incoming error number (arg1).
* TFS_MEMUSE: Return the total amount of memory currently in use by
* TFS.
* TFS_MEMAVAIL: Return the amount of memory currently avaialable for
* use in TFS.
* TFS_MEMDEAD: Return the amount of memory currently in use by
* dead files in TFS.
* TFS_DEFRAG: Mechanism for the application to issue
* a defragmentation request.
* Arg1: if 1, then reset after defrag is complete.
* Arg2: verbosity level.
* TFS_TELL: Return the offset into the file specified by the
* incoming file descriptor (arg1).
* TFS_FATOB: Return the binary equivalent of the TFS flags string
* pointed to by arg1.
* TFS_FBTOA: Return the string equivalent of the TFS flags (long)
* in arg1, destination buffer in arg2.
* TFS_UNOPEN: In TFS, a the data is not actually written to FLASH
* until the tfsclose() function is called. This argument
* to tfsctrl() allows a file to be opened and possibly
* written to, then unopened without actually modifying
* the FLASH. The value of arg1 file descriptor to
* apply the "unopen" to.
* TFS_TIMEFUNCS: This ctrl call is used to tell TFS what function
* to call for time information...
* Arg1 is a pointer to:
* (long)getLtime(void)
* - Get Long Time...
* Returns a long representation of time.
* Arg2 is a pointer to:
* (char *)getAtime(long tval,char *buf).
* - Get Ascii Time...
* If tval is zero, the buf is loaded with a string
* representing the current time;
* If tval is non-zero, then buf is loaded with a
* string conversion of the value of tval.
* Note that since it is up to these functions to
* make the conversion between binary version of time
* and ascii version, we don't define the exact meaning
* of the value returne by getBtime().
* TFS_DOCOMMAND: Allows the application to redefine the function
* that is called to process each line of a script.
* This is useful if the application has its own
* command interpreter, but wants to use the scripting
* facilities of the monitor.
* Arg1 is a pointer to the docommand function to be
* used instead of the standard;
* Arg2 is a pointer to a location into which the current
* docommand function pointer can be stored.
* If arg1 is 0, load standard docommand;
* if arg2 is 0, don't load old value.
* TFS_INITDEV: Allows the application to initialize one of TFS's
* devices. Arg1 is a pointer to the device name prefix.
* TFS_DEFRAGDEV: Allows the application to defrag one of TFS's
* devices. Arg1 is a pointer to the device name prefix.
* TFS_CHECKDEV: Allows the application to check one of TFS's
* devices. Arg1 is a pointer to the device name prefix.
*
*
* MONLIB NOTICE: this function is accessible through monlib.c.
*/
long
tfsctrl(int rqst,long arg1,long arg2)
{
long retval, flag;
TDEV *tdp;
TINFO tinfo;
if (tfsTrace > 0)
printf("tfsctrl(%d,0x%lx,0x%lx)\n",rqst,arg1,arg2);
switch(rqst) {
case TFS_ERRMSG:
retval = (long)tfserrmsg(arg1);
break;
case TFS_MEMUSE:
tfsmemuse(0,&tinfo,0);
retval = tinfo.memused;
break;
case TFS_MEMAVAIL:
tfsmemuse(0,&tinfo,0);
retval = tinfo.memfordata;
break;
case TFS_MEMDEAD:
tfsmemuse(0,&tinfo,0);
retval = tinfo.deadovrhd+tinfo.deaddata;
break;
case TFS_INITDEV:
tdp = gettfsdev_fromprefix((char *)arg1,0);
if (!tdp)
retval = TFSERR_BADARG;
else
retval = _tfsinit(tdp);
break;
case TFS_CHECKDEV:
tdp = gettfsdev_fromprefix((char *)arg1,0);
if (!tdp)
retval = TFSERR_BADARG;
else
retval = tfscheck(tdp,0);
break;
case TFS_DEFRAGDEV:
tdp = gettfsdev_fromprefix((char *)arg1,0);
if (!tdp)
retval = TFSERR_BADARG;
else
retval = tfsclean(tdp,0);
break;
case TFS_DEFRAG:
for(tdp=tfsDeviceTbl;tdp->start != TFSEOT;tdp++)
tfsclean(tdp,(int)arg1);
retval = 0;
break;
case TFS_FCOUNT:
if (arg1) {
tdp = gettfsdev_fromprefix((char *)arg1,0);
if (!tdp)
retval = TFSERR_BADARG;
else
retval = tfsftot(tdp);
}
else {
retval = tfsftot(0);
}
break;
case TFS_DEFRAGON:
retval = tfsclean_on();
break;
case TFS_DEFRAGOFF:
retval = tfsclean_off();
break;
case TFS_UNOPEN:
retval = tfsunopen((int)arg1);
break;
case TFS_FATOB:
retval = tfsflagsatob((char *)arg1,&flag);
if (retval == TFS_OKAY)
retval = flag;
break;
case TFS_FBTOA:
retval = (long)tfsflagsbtoa(arg1,(char *)arg2);
if (retval == 0)
retval = TFSERR_BADARG;
break;
case TFS_HEADROOM:
retval = tfsheadroom(arg1);
break;
case TFS_TELL:
retval = tfstell(arg1);
break;
case TFS_TIMEFUNCS:
tfsGetLtime = (long(*)(void))arg1;
tfsGetAtime = (char *(*)(long,char *,int))arg2;
retval = TFS_OKAY;
break;
case TFS_DOCOMMAND:
/* if (arg2)
*(long *)arg2 = (long)tfsDocommand;
if (arg1)
tfsDocommand = (int(*)(char *,int))arg1;
else
tfsDocommand = docommand;
retval = TFS_OKAY;
*/
break;
default:
retval = TFSERR_BADARG;
break;
}
return(retval);
}
/* tfsadd():
* Add a file to the current list.
* If the file already exists AND everything is identical between the
* old and the new (flags, info and data), then return and do nothing;
* else remove the old file prior to adding the new one.
*
* Note:
* At the point when tfsadd is called for a file that currently exists,
* the old file must be removed and a new one is put in its place. This
* opens up the possibility of losing the file if a power-hit or reset was
* to occur between the point at which the old file was removed and the new
* one was put in its place. To overcome this problem, TFS files have a
* flag called TFS_NSTALE. It is a bit that is normally 1, but cleared
* if it becomes stale (hence the name TFS_NSTALE). A file is
* in this mode only for a short time... the time it takes to write the
* new file that replaces the file that was made stale.
* Now, if a reset occurs after the file is stale, depending on
* whether or not the new file was written, it will either be removed or
* used to recreate the original file because the write of the new file
* was chopped off by the power hit. Refer to the function tfsstalecheck()
* for details on the recovery after a reset or powerhit.
* MONLIB NOTICE: this function is accessible through monlib.c.
*/
int
tfsadd(char *name, char *info, char *flags, uchar *src, int size)
{
TDEV *tdp;
TFILE *fp, tf, *sfp;
ulong endoftfsflash, nextfileaddr, thisfileaddr;
ulong crc_pass1, crc_pass2, bflags, state_table_overhead;
int ftot, cleanupcount, err, stale, ssize;
if (!info) info = "";
if (!flags) flags = "";
if (tfsTrace > 0)
printf("tfsadd(%s,%s,%s,0x%lx,%d)\n", name,info,flags,(ulong)src,size);
/* Check for valid size and name: */
if ((size <= 0) || (!name))
return(TFSERR_BADARG);
/* If name or info field length is too long, abort now... */
if ((strlen(name) > TFSNAMESIZE) ||
((info) && (strlen(info) > TFSINFOSIZE)))
return(TFSERR_NAMETOOBIG);
/* If the file is currently opened, then don't allow the add... */
if (tfsFileIsOpened(name))
return(TFSERR_FILEINUSE);
/* If incoming flags are illegal, abort now... */
if (*flags == 0) {
bflags = 0;
}
else {
err = tfsflagsatob(flags,(long*)&bflags);
if (err != TFS_OKAY)
return(err);
}
stale = 0;
cleanupcount = 0;
/* Take snapshot of source crc. */
crc_pass1 = crc32(src, size);
/* Establish the device that is to be used for the incoming file
* addition request... The device used depends on the prefix of
* the incoming file name. If the incoming prefix doesn't match
* any of the devices in the table, then place the file in the
* first device in the table (assumed to be the default).
*/
for(tdp=tfsDeviceTbl;tdp->start != TFSEOT;tdp++) {
if (!strncmp(name,tdp->prefix,strlen(tdp->prefix)))
break;
}
if (tdp->start == TFSEOT)
tdp = tfsDeviceTbl;
#if INCLUDE_TFSAUTODEFRAG
tryagain:
#endif
fp = (TFILE *)tdp->start;
/* Find end of current storage: */
ftot = 0;
while (fp) {
if (fp->hdrsize == ERASED16)
break;
if (TFS_FILEEXISTS(fp)) {
ftot++;
if (!strcmp(TFS_NAME(fp),name)) {
/* If file of the same name exists AND it is identical to
* the new file to be added, then return TFS_OKAY and be
* done; otherwise, remove the old one and continue.
* Two exceptions to this:
* 1. If the current file is stale, then we are here
* because of a stale-file fixup at system startup.
* 2. If the src file is in-place-modify then source
* data is undefined.
*/
if (!(TFS_STALE(fp))) {
if (!(bflags & TFS_IPMOD) &&
(!tfscompare(fp,name,info,flags,src,size))) {
return(TFS_OKAY);
}
/* If a file of the same name exists but is different
* than the new file, set a flag to indicate that the
* file should be marked stale just prior to
* adding the new file.
*/
stale = 1;
}
}
}
fp = nextfp(fp,tdp);
}
if (!fp) /* If fp is 0, then nextfp() (above) detected corruption. */
return(TFSERR_CORRUPT);
/* Calculate location of next file (on mod16 address). This will be
* initially used to see if we have enough space left in flash to store
* the current request; then, if yes, it will become part of the new
* file's header.
*/
thisfileaddr = (ulong)(fp+1);
nextfileaddr = thisfileaddr + size;
if (nextfileaddr & 0xf)
nextfileaddr = (nextfileaddr | 0xf) + 1;
/* Make sure that the space is available for writing to flash...
* Remember that the end of useable flash space must take into
* account the fact that some space must be left over for the
* defragmentation state tables. Also, the total space needed for
* state tables cannot exceed the size of the sector that will contain
* those tables.
*/
state_table_overhead = ((ftot+1) * DEFRAGHDRSIZ) +
(tdp->sectorcount * sizeof(struct sectorcrc));
if (addrtosector((uchar *)(tdp->end),0,&ssize,0) < 0)
return(TFSERR_MEMFAIL);
if (state_table_overhead >= (ulong)ssize)
return(TFSERR_FLASHFULL);
endoftfsflash = (tdp->end + 1) - state_table_overhead;
if ((nextfileaddr >= endoftfsflash) ||
(nextfileaddr < thisfileaddr) ||
(!tfsSpaceErased((uchar *)fp,size+TFSHDRSIZ))) {
#if INCLUDE_TFSAUTODEFRAG
if (!cleanupcount) {
err = tfsclean(tdp,0);
if (err != TFS_OKAY) {
printf("tfsadd autoclean failed: %s\n",
(char *)tfsctrl(TFS_ERRMSG,err,0));
return(err);
}
cleanupcount++;
goto tryagain;
}
else
#endif
return(TFSERR_FLASHFULL);
}
memset((char *)&tf,0,TFSHDRSIZ);
/* Do another crc on the source data. If crc_pass1 != crc_pass2 then
* somehow the source is changing. This is typically caused by the fact
* that the source address is within TFS space that was automatically
* defragmented above. There is no need to check source data if the
* source is in-place-modifiable.
*/
if (!(bflags & TFS_IPMOD)) {
crc_pass2 = crc32(src,size);
if (crc_pass1 != crc_pass2)
return(TFSERR_FLAKEYSOURCE);
}
else
crc_pass2 = ERASED32;
/* Now that we have determined that we have enough space to do the
* copy, if the "stale" flag was set (indicating that there is already
* a file in TFS with the same name as the incoming file), we must now
* mark the file stale...
*/
if (stale) {
sfp = (TFILE *)tdp->start;
while (sfp) {
if (sfp->hdrsize == ERASED16)
break;
if (TFS_FILEEXISTS(sfp)) {
if (!strcmp(TFS_NAME(sfp),name)) {
err = tfsmakeStale(sfp);
if (err != TFS_OKAY)
return(err);
break;
}
}
sfp = nextfp(sfp,tdp);
}
if (!sfp)
return(TFSERR_CORRUPT);
}
/* Copy name and info data to header. */
strcpy(tf.name, name);
strcpy(tf.info, info);
tf.hdrsize = TFSHDRSIZ;
tf.hdrvrsn = TFSHDRVERSION;
tf.filsize = size;
tf.flags = bflags;
tf.flags |= (TFS_ACTIVE | TFS_NSTALE);
tf.filcrc = crc_pass2;
tf.modtime = tfsGetLtime();
#if TFS_RESERVED
{
int rsvd;
for(rsvd=0;rsvd 0)
printf("tfsunlink(%s)\n",name);
/* If the file is currently opened, then don't allow the deletion... */
if (tfsFileIsOpened(name))
return(TFSERR_FILEINUSE);
return(_tfsunlink(name));
}
int
_tfsunlink(char *name)
{
TFILE *fp;
ulong flags_marked_deleted;
if (tfsTrace > 0)
printf("_tfsunlink(%s)\n",name);
fp = _tfsstat(name,0);
if (!fp)
return(TFSERR_NOFILE);
if (TFS_USRLVL(fp) > getUsrLvl())
return(TFSERR_USERDENIED);
flags_marked_deleted = fp->flags & ~TFS_ACTIVE;
if (tfsflashwrite((ulong *)&fp->flags,&flags_marked_deleted,
sizeof(long)) < 0)
return(TFSERR_FLASHFAILURE);
tfslog(TFSLOG_DEL,name);
return (TFS_OKAY);
}
int
tfslink(char *src, char *target)
{
TFILE *tfp;
char linfo[TFSINFOSIZE+1];
tfp = tfsstat(src);
if (tfp) {
tfp = tfsstat(src);
strcpy(linfo,"->");
strncpy(linfo+2,src,TFSINFOSIZE-1);
linfo[TFSINFOSIZE] = 0;
return(tfsadd(target,linfo,"l",(uchar*)src,strlen(src)+1));
}
return(TFSERR_NOFILE);
}
/* tfsrun():
* Run the named file. Based on the file flags, the file is either
* executed as a COFF/ELF file with all relocation data in the file
* or run as a simple script of monitor commands.
* MONLIB NOTICE: this function is accessible through monlib.c.
*/
int
tfsrun(char **arglist,int verbose)
{
int i;
TFILE fstat, *fp;
char *name;
name = arglist[0];
fp = tfsstat(name);
if (!fp)
return (TFSERR_NOFILE);
tfsfstat(name,&fstat);
if (TFS_USRLVL(fp) > getUsrLvl())
return(TFSERR_USERDENIED);
/* Store away the argument list so that it is accessible by the script
* or executable application about to be run:
*/
for(i=0;arglist[i];i++)
putargv(i,arglist[i]);
putargv(i,(char *)0);
/* Executable file can be script or binary... */
if (!(fp->flags & (TFS_EXEC|TFS_EBIN)))
return(TFSERR_NOTEXEC);
if (!(fp->flags & TFS_IPMOD)) {
if (crc32((uchar*)TFS_BASE(fp), fp->filsize) != fp->filcrc)
return(TFSERR_BADCRC);
}
/* Machine code or script... */
if (fp->flags & TFS_EBIN)
return(tfsexec(fp,verbose));
else
return(tfsscript(&fstat,verbose));
}
/* tfsrunrcfile():
* Called at system startup to run the monitors run-control file (monrc).
* If monrc is not found, make one additional attempt by running
* "monrc.bak".
* If one or the other is found and run, post a warning to the console
* if the file is flagged as autobootable. This lets the user know that
* the monrc file is going to be run twice (probably not what they want, so
* when the warning is seen, the flag will be changed).
*/
void
tfsrunrcfile(void)
{
TFILE *tfp;
char *arglist[2], *name;
name = TFS_RCFILE;
tfp = tfsstat(name);
if (!tfp) {
name = TFS_RCFILE ".bak";
tfp = tfsstat(name);
if (!tfp)
return;
}
if (TFS_FLAGS(tfp) & (TFS_BRUN | TFS_QRYBRUN))
printf("Warning: %s is autobootable\n",name);
arglist[0] = name;
arglist[1] = (char *)0;
tfsrun(arglist,0);
return;
}
/* tfsnext():
* Called to retrieve the "next" file in the tfs list. If
* incoming argument is NULL then return the first file in the list. If no
* more files, return NULL; else return the tfshdr structure pointer to the
* next (or first) file in the tfs.
* MONLIB NOTICE: this function is accessible through monlib.c.
*/
TFILE *
tfsnext(TFILE *fp)
{
TDEV *tdp;
TFILE *fpnext;
if (!fp) {
tdp = tfsDeviceTbl;
fpnext = (TFILE *) tfsDeviceTbl[0].start;
}
else {
tdp = gettfsdev(fp);
fpnext = nextfp(fp,0);
}
while(tdp->start != TFSEOT) {
while(validtfshdr(fpnext)) {
if (TFS_FILEEXISTS(fpnext))
return (fpnext);
fpnext = nextfp(fpnext,0);
}
tdp++;
fpnext = (TFILE *)tdp->start;
}
return ((TFILE *) 0);
}
/* tfsstat():
* Steps through the list of files until it finds the specified
* filename or reaches the end of the list. If found, a pointer to that
* file's structure is returned; else return 0.
* MONLIB NOTICE: this function is accessible through monlib.c.
*/
TFILE *
tfsstat(char *name)
{
return(_tfsstat(name,1));
}
TFILE *
_tfsstat(char *name,int uselink)
{
TDEV *tdp;
TFILE *fp;
if (tfsTrace > 0)
printf("_tfsstat(%s,%d)\n",name,uselink);
for(tdp=tfsDeviceTbl;tdp->start != TFSEOT;tdp++) {
fp = (TFILE *) tdp->start;
while(validtfshdr(fp)) {
if (TFS_FILEEXISTS(fp) && (strcmp(name, fp->name) == 0)) {
if (uselink && TFS_ISLINK(fp))
return(tfsstat(TFS_BASE(fp)));
else
return(fp);
}
fp = nextfp(fp,tdp);
}
}
return ((TFILE *) 0);
}
/* tfsfstat():
* Very similar in purpose to tfsstat(). This version is provided to the
* API as a "defrag-safe" version of tfsstat()...
* If tfsstat() is called (returning a pointer into TFS memory space), then
* a defragmentation occurs, that pointer is stale; hence, the need for
* an alternative that will load the content of the TFILE structure into
* an application-supplied block of memory (usually a pointer to a local
* TFILE structure). Using tfsfstat avoids this because if a defrag occurs,
* it does not affect the content of the locally stored TFILE structure.
* NOTE:
* addition of this function to the TFS API was due to the fact that
* I did not consider the above described condition when first adding
* tfsstat() to the TFS API. In general, tfsfstat() should be considered
* a replacement for all tfsstat() situations that will dereference the
* pointer.
* NOTE1:
* The return value is similar to standard "stat"... Return 0 if
* successful, else -1.
*/
int
tfsfstat(char *name, TFILE *apptfp)
{
TFILE *tfp;
int otrace;
otrace = tfsTrace;
if (tfsTrace > 0) {
tfsTrace = 0;
printf("tfsfstat(%s)\n",name);
}
tfp = tfsstat(name);
tfsTrace = otrace;
if (!tfp)
return(-1);
*apptfp = *tfp;
return(0);
}
int
showTfsError(int errno, char *msg)
{
if (errno == TFS_OKAY)
return(TFS_OKAY);
if (msg)
printf("%s: %s\n",msg,tfserrmsg(errno));
else
printf("%s\n",tfserrmsg(errno));
return(errno);
}
/*
int
tfsclean_on(void)
{
TfsCleanEnable++;
return(TfsCleanEnable);
}
int
tfsclean_off(void)
{
TfsCleanEnable--;
return(TfsCleanEnable);
}
*/
/* _tfsclean():
* This is an alternative to the complicated defragmentation above.
* It simply scans through the file list and copies all valid files
* to RAM; then flash is erased and the RAM is copied back to flash.
* <<< WARNING >>>
* THIS FUNCTION SHOULD NOT BE INTERRUPTED AND IT WILL BLOW AWAY
* ANY APPLICATION CURRENTLY IN CLIENT RAM SPACE.
*/
/*
int
_tfsclean(TDEV *tdp, int notused, int verbose)
{
TFILE *tfp;
uchar *tbuf;
ulong appramstart;
int dtot, nfadd, len, err, chkstat;
if (TfsCleanEnable < 0)
return(TFSERR_CLEANOFF);
appramstart = getAppRamStart();
// Determine how many "dead" files exist.
dtot = 0;
tfp = (TFILE *)tdp->start;
while(validtfshdr(tfp)) {
if (!TFS_FILEEXISTS(tfp))
dtot++;
tfp = nextfp(tfp,tdp);
}
if (dtot == 0)
return(TFS_OKAY);
printf("Reconstructing device %s with %d dead file%s removed...\n",
tdp->prefix, dtot,dtot>1 ? "s":"");
tbuf = (uchar *)appramstart;
tfp = (TFILE *)(tdp->start);
nfadd = tdp->start;
while(validtfshdr(tfp)) {
if (TFS_FILEEXISTS(tfp)) {
len = TFS_SIZE(tfp) + sizeof(struct tfshdr);
if (len % TFS_FSIZEMOD)
len += TFS_FSIZEMOD - (len % TFS_FSIZEMOD);
nfadd += len;
err = tfsmemcpy(tbuf,(uchar *)tfp,len,0,0);
if (err != TFS_OKAY)
return(err);
((struct tfshdr *)tbuf)->next = (struct tfshdr *)nfadd;
tbuf += len;
}
tfp = nextfp(tfp,tdp);
}
// Erase the flash device:
err = _tfsinit(tdp);
if (err != TFS_OKAY)
return(err);
// Copy data placed in RAM back to flash:
err = AppFlashWrite((ulong *)(tdp->start),(ulong *)appramstart,
(tbuf-(uchar*)appramstart));
if (err < 0)
return(TFSERR_FLASHFAILURE);
// All defragmentation is done, so verify sanity of files...
chkstat = tfscheck(tdp,verbose);
return(chkstat);
}
*/
/* tfsclean():
* Wrapper for _tfsclean so that we can enable a call to appexit() if there
* is an error returned by tfsclean(). This is used for testing TFS...
* If a script is running to try to cause tfsclean to break, then we want
* it to halt if tfsclean does return an error.
*/
/*
int
tfsclean(TDEV *tdp,int verbose)
{
int cleanresult;
cleanresult = _tfsclean(tdp,0,verbose);
if (cleanresult != TFS_OKAY) {
if (getenv("APP_EXITONCLEANERROR"))
return -1;
//appexit(0);
#if INCLUDE_TFSSCRIPT
if (getenv("SCR_EXITONCLEANERROR"))
ScriptExitFlag = EXIT_SCRIPT;
#endif
}
return(cleanresult);
}
*/
#else // INCLUDE_TFS
char *
tfserrmsg(int errno)
{
return(0);
}
int
tfsinit(void)
{
return(TFSERR_NOTAVAILABLE);
}
int
tfsfstat(char *name, TFILE *apptfp)
{
return(TFSERR_NOTAVAILABLE);
}
TFILE *
tfsstat(char *name)
{
return ((TFILE *) 0);
}
int
tfslink(char *src, char *target)
{
return(TFSERR_NOTAVAILABLE);
}
TFILE *
tfsnext(TFILE *fp)
{
return ((TFILE *) 0);
}
int
tfsrun(char **arglist,int verbose)
{
return(TFSERR_NOTAVAILABLE);
}
int
tfsunlink(char *name)
{
return(TFSERR_NOTAVAILABLE);
}
int
tfsadd(char *name, char *info, char *flags, uchar *src, int size)
{
return(TFSERR_NOTAVAILABLE);
}
long
tfsctrl(int rqst,long arg1,long arg2)
{
return(TFSERR_NOTAVAILABLE);
}
#endif /* INCLUDE_TFS else */