📄 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. * * tfsclean.c: * TFS can be configured to have a robust power-hit-safe cleanup * mechanism which requires a significant amount of flash, or a simple, * non-power-hit-safe mechanism. Both implementations are in tfsclean.c. * * 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. * * Author: Ed Sutter * email: esutter@lucent.com (home: lesutter@worldnet.att.net) * phone: 908-582-2351 (home: 908-889-5161) */#include "config.h"#include "cpu.h"#include "stddefs.h"#include "genlib.h"#include "tfs.h"#include "tfsdev.h"#include "flash.h"#if INCLUDE_TFSchar *(*tfsGetAtime)(long,char *,int);long (*tfsGetLtime)(void);void (*tfsDocommand)(char *,int);TDEV tfsDeviceTbl[TFSDEVTOT];TFILE **tfsAlist;int ScriptExitFlag;struct tfsdat tfsSlots[TFS_MAXOPEN];long tfsTrace;static long tfsFmodCount;static int tfsAlistSize, tfsOldDelFlagCheckActive;/* 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_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" }, { 0,0 }};/* 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 longdummyLtime(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. */inttfsflasheraseall(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((char *)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 (addrtosector((char *)tdp->spare,&snum,0,0) < 0) return(TFSERR_MEMFAIL); if (AppFlashErase(snum) == -1) return(TFSERR_MEMFAIL); return(TFS_OKAY);}inttfsflasherase(int snum){ if (tfsTrace > 2) printf("tfsflasherase(%d)\n",snum); tfsFmodCount++; return(AppFlashErase(snum));}inttfsflashwrite(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 inttfsmakeStale(TFILE *tfp){ ulong flags; flags = TFS_FLAGS(tfp) & ~TFS_NSTALE; if (tfsflashwrite(&tfp->flags,&flags,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 inttfsflagsatob(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);}/* 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. */intvalidtfshdr(TFILE *hdr){ TFILE hdrcpy; ulong hdrcrc; /* A few quick checks... */ if (!hdr || hdr->hdrsize == ERASED16) return(0); /* Run a crc32 test... * The header crc 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 validateion. * 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. */ hdrcpy = *hdr; hdrcrc = hdr->hdrcrc; hdrcpy.next = 0; hdrcpy.hdrcrc = 0; hdrcpy.flags |= (TFS_NSTALE | TFS_ACTIVE); if (crc32((uchar *)&hdrcpy,TFSHDRSIZ) == 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. */inttfsflasherased(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);}/* 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. */inttfsmemuse(TDEV *tdpin, TINFO *tinfo, int verbose){ int devtot; 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; 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 + sizeof(struct defraghdr));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -