📄 tfs.c
字号:
/* 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,&bflags);
if (err != TFS_OKAY)
return(err);
/* If we're adding a link, then the size better be zero...
*/
if ((bflags & TFS_SYMLINK) && (size != 0))
return(TFSERR_LINKERROR);
}
/* If size is zero, but the request is not a link, then
* error...
*/
if ((size == 0) && ((bflags & TFS_SYMLINK) != TFS_SYMLINK))
return(TFSERR_BADARG);
stale = 0;
cleanupcount = 0;
/* Take snapshot of source crc. Note that we only run the CRC
* if the IPMOD flag is not set. If this flag is set, then the
* CRC is invalid...
*/
if (!(bflags & TFS_IPMOD))
crc_pass1 = crc32(src, size);
else
crc_pass1 = 0;
/* Establish the device that is to be used for the incoming file
* addition request...
*/
tdp = tfsNameToDevice(name);
#ifndef TFS_DISABLE_AUTODEFRAG
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 (!(TFS_STALE(fp))) {
/* If destination file exists, but we do not meet the
* user level requirements, return error now.
*/
if (TFS_USRLVL(fp) > getUsrLvl())
return(TFSERR_USERDENIED);
/* 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 (!(bflags & TFS_IPMOD) &&
(!tfscompare(fp,name,info,flags,src,size))) {
return(TFS_OKAY);
}
#ifdef TFS_DISABLE_MAKE_BEFORE_BREAK
err = _tfsunlink(name);
if (err != TFS_OKAY)
printf("%s: %s\n",name,tfserrmsg(err));
#else
/* 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;
#endif
}
}
}
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.
*/
if (TFS_DEVTYPE_ISRAM(tdp)) {
endoftfsflash = tdp->end;
}
else {
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))) {
#ifndef TFS_DISABLE_AUTODEFRAG
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)) {
if (TFS_DEVTYPE_ISRAM(tdp)) {
TFS_FLAGS(sfp) &= ~TFS_NSTALE;
}
else {
if ((err = tfsmakeStale(sfp)) != 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<TFS_RESERVED;rsvd++)
tf.rsvd[rsvd] = 0xffffffff;
}
#endif
tf.next = 0;
tf.hdrcrc = 0;
tf.hdrcrc = crc32((uchar *)&tf,TFSHDRSIZ);
tf.next = (TFILE *)nextfileaddr;
/* Now copy the file and header to flash.
* Note1: the header is copied AFTER the file has been
* successfully copied. If the header was written successfully,
* then the data write failed, the header would be incorrectly
* pointing to an invalid file. To avoid this, simply write the
* data first.
* Note2: if the file is in-place-modifiable, then there is no
* file data to be written to the flash. It will be left as all FFs
* so that the flash can be modified by tfsipmod() later.
*/
/* Write the file to flash if not TFS_IPMOD:
*/
if (!(tf.flags & TFS_IPMOD)) {
if (TFS_DEVTYPE_ISRAM(tdp))
memcpy((char *)(fp+1),src,size);
else {
rc = tfsflashwrite((uchar *)(fp+1),(uchar *)src,size);
if (rc != TFS_OKAY)
return(rc);
}
}
/* Write the file header to flash:
*/
if (TFS_DEVTYPE_ISRAM(tdp))
memcpy((char *)fp,(char *)&tf,TFSHDRSIZ);
else {
rc = tfsflashwrite((uchar *)fp,(uchar *)(&tf),TFSHDRSIZ);
if (rc != TFS_OKAY)
return(rc);
}
/* Double check the CRC now that it is in flash.
*/
if (!(tf.flags & TFS_IPMOD)) {
if (crc32((uchar *)(fp+1), size) != tf.filcrc)
return(TFSERR_BADCRC);
}
/* If the add was a file that previously existed, then the stale flag
* will be set and the old file needs to be deleted...
*/
if (stale) {
err = _tfsunlink(name);
if (err != TFS_OKAY)
printf("%s: %s\n",name,tfserrmsg(err));
}
tfslog(TFSLOG_ADD,name);
return(TFS_OKAY);
}
/* tfsunlink():
* Delete a file from the current list of files. Note that there
* is no attempt to de-fragment the flash; it simply nulls out the flags
* field of the file. If successful return 0; else return error number.
* MONLIB NOTICE: this function is accessible through monlib.c.
*/
int
tfsunlink(char *name)
{
if (tfsTrace > 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)
{
int rc;
TFILE *fp;
TDEV *tdp;
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;
tdp = tfsNameToDevice(name);
if (TFS_DEVTYPE_ISRAM(tdp))
memcpy((char *)&fp->flags,(char *)&flags_marked_deleted,sizeof(long));
else {
rc = tfsflashwrite((uchar *)&fp->flags,
(uchar *)&flags_marked_deleted, sizeof(long));
if (rc != TFS_OKAY)
return(rc);
}
tfslog(TFSLOG_DEL,name);
return (TFS_OKAY);
}
int
tfslink(char *src, char *target)
{
TFILE *tfp;
char linfo[TFSINFOSIZE+1];
char flags[16];
tfp = tfsstat(src);
if (tfp) {
if (TFS_ISLINK(tfp))
return(TFSERR_LINKERROR);
strncpy(linfo,src,TFSINFOSIZE-1);
linfo[TFSINFOSIZE] = 0;
flags[0] = 'l';
tfsflagsbtoa(tfp->flags,flags+1);
return(tfsadd(target,linfo,flags,0,0));
}
return(TFSERR_NOFILE);
}
/*
* tfsrun_abortableautoboot():
*
* Aborting execution of the monrc & non-query-autoboot files:
* The code wrapped within the #ifdef TFS_AUTOBOOT_ABORTABLE definition
* is an implementation of an idea from Jason DiDonato (Jan 2004).
* These files can be aborted by an escape character under two
* circumstances:
*
* 1. There is no password file installed
* 2. The user correctly enters the level-three password when
* prompted by the abort interaction below.
*
* This mechanism maintains security, but only when security is desired.
* A system that needs security will have a password file installed;
* hence, if no password file is found, the escape character
* (AUTOBOOT_ABORT_CHAR) is all that is needed at startup to abort an
* otherwise non-abortable file.
* If a password file is found, then the user must know the level-3 password
* to abort the execution; otherwise, after a few seconds, the interaction
* will terminate and the file will be executed.
*
* Prior to this implementation, autobootables were not abortable in any
* way. This is still the default, which is overridden by the definition
* of TFS_AUTOBOOT_ABORTABLE in config.h
*/
#define AUTOBOOT_ABORT_NULL 0
#define AUTOBOOT_ABORT_NO 1
#define AUTOBOOT_ABORT_YES 2
#define AUTOBOOT_ABORT_FAILED 3
#ifndef AUTOBOOT_ABORT_CHAR
#ifdef TFS_AUTOBOOT_CANCEL_CHAR
#define AUTOBOOT_ABORT_CHAR TFS_AUTOBOOT_CANCEL_CHAR
#else
#define AUTOBOOT_ABORT_CHAR 0x03 /* CTRL-C */
#endif
#endif
int
tfsrun_abortableautoboot(char **arglist,int verbose)
{
#ifdef TFS_AUTOBOOT_ABORTABLE
int err;
static int autoboot_abort;
/* If a character has been detected at the console, and the
* character is AUTOBOOT_ABORT_CHAR, then, if a password file
* exists, require that the user enter the level-3 password
* to abort the autoboot file...
* Note that this is only done on the first pass through this
* function. All subsequent passes use the result of the initial
* pass.
*/
if (autoboot_abort == AUTOBOOT_ABORT_NULL) {
autoboot_abort = AUTOBOOT_ABORT_NO;
if (gotachar() && (getchar() == AUTOBOOT_ABORT_CHAR)) {
if (passwordFileExists()) {
char passwd[16];
/* To allow the user to simply hold down on the abort
* character during a target, reset, this loop will
* absorb a burst of incoming characters. Then, when
* the burst halts, the user will be queried for the
* password.
*/
monDelay(500);
while(gotachar()) {
getchar();
monDelay(500);
}
getpass("autoboot-abort password: ",passwd,sizeof(passwd)-1,50);
if (validPassword(passwd,3))
autoboot_abort = AUTOBOOT_ABORT_YES;
else
autoboot_abort = AUTOBOOT_ABORT_FAILED;
}
else
autoboot_abort = AUTOBOOT_ABORT_YES;
}
}
err = TFS_OKAY;
switch(autoboot_abort) {
case AUTOBOOT_ABORT_NO:
err = tfsrun(arglist,verbose);
break;
case AUTOBOOT_ABORT_YES:
printf("%s aborted\n",arglist[0]);
break;
case AUTOBOOT_ABORT_FAILED:
printf("%s abort attempt failed\n",arglist[0]);
err = tfsrun(arglist,verbose);
break;
}
return(err);
#else
return(tfsrun(arglist,verbose));
#endif /* TFS_AUTOBOOT_ABORTABLE */
}
/* tfsrun():
* Run the named file. Based on the file flags, the file is either
* executed as
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -