twl_wrapsdk/build/libraries/fatfs/ARM7/rtvfat.c
Shirait 4fe5688d29 add SD driver and FATFS library (tentative)
git-svn-id: file:///Users/lillianskinner/Downloads/platinum/twl/twl_wrapsdk/trunk@96 4ee2a332-4b2b-5046-8439-1ba90f034370
2007-05-30 10:22:28 +00:00

1450 lines
47 KiB
C

/*
* EBS - RTFS (Real Time File Manager)
*
* Copyright EBS Inc. 1987-2003
* All rights reserved.
* This code may not be redistributed in source or linkable object form
* without the consent of its author.
*/
/* RTVFAT.C - Contains routines specific to the vfat implementation */
#include <rtfs.h>
#if (VFAT)
#define FIRST_NAMESEG 0x40
#define NAMESEG_ORDER 0x3F
/* LFNINODE - This is an image of lfn extended names in a subdirectory */
/* Note: lfn file names are null terminated unicode 00, the lfninode is
padded mod 13 with ffff */
typedef struct lfninode {
/* The first LNF has 0x40 + N left */
byte lfnorder; /* 0x45, 0x04, 0x03, 0x02, 0x01 they are stored in
reverse order */
byte lfname1[10];
byte lfnattribute; /* always 0x0F */
byte lfnres; /* reserved */
byte lfncksum; /* All lfninode in one dirent have the same chksum */
byte lfname2[12];
word lfncluster; /* always 0x0000 */
byte lfname3[4];
} LFNINODE;
void pcdel2lfi(LFNINODE *lfi, int nsegs);
BOOLEAN pc_deleteseglist(DDRIVE *pdrive, SEGDESC *s);
byte *text2lfi(byte *lfn, LFNINODE *lfi, int nsegs, byte ncksum, byte order);
BOOLEAN pc_seglist2disk(DDRIVE * pdrive, SEGDESC *s, byte *lfn);
byte *lfi2text(byte *lfn, int *current_lfn_length, LFNINODE *lfi, int nsegs);
byte *pc_seglist2text(DDRIVE * pdrive, SEGDESC *s, byte *lfn);
void pc_zeroseglist(SEGDESC *s);
void pc_zeroseglist(SEGDESC *s);
void pc_addtoseglist(SEGDESC *s, BLOCKT my_block, int my_index);
void pc_reduceseglist(SEGDESC *s);
BOOLEAN pc_patcmp_vfat_8_3(byte *pat, byte *name, BOOLEAN dowildcard);
/***************************************************************************
PC_FINDIN - Find a filename in the same directory as the argument.
Description
Look for the next match of filename or pattern filename:ext in the
subdirectory containing pobj. If found update pobj to contain the
new information (essentially getnext.) Called by pc_get_inode().
Note: Filename and ext must be right filled with spaces to 8 and 3 bytes
respectively. Null termination does not matter.
Note the use of the action variable. This is used so we do not have to
use match patterns in the core code like *.* and ..
GET_INODE_MATCH Must match the pattern exactly
GET_INODE_WILD Pattern may contain wild cards
GET_INODE_STAR Like he passed *.* (pattern will be null)
GET_INODE_DOTDOT Like he past .. (pattern will be null
Returns
Returns TRUE if found or FALSE.
****************************************************************************/
/* Find filename in the directory containing pobj. If found, load the inode
section of pobj. If the inode is already in the inode buffers we free the current inode
and stitch the existing one in, bumping its open count */
BOOLEAN pc_findin( DROBJ *pobj, byte *filename, byte *fileext, int action) /*__fn__*/
{
BLKBUFF *rbuf;
DIRBLK *pd;
DOSINODE *pi;
FINODE *pfi;
BOOLEAN matchfound;
BOOLEAN dowildcard;
BLKBUFF *scratch;
byte *lfn;
byte sfn[26]; /* Leave room in case unicode */
LFNINODE *lfn_node;
SEGDESC s;
byte lastsegorder;
byte *p;
RTFS_ARGSUSED_PVOID((void *) fileext);
rtfs_set_errno(0); /* Clear it here just in case */
scratch = pc_scratch_blk();
if (!scratch)
return(FALSE);
lfn = (byte *)scratch->data;
pc_zeroseglist(&s);
if (action == GET_INODE_WILD)
dowildcard = TRUE;
else
dowildcard = FALSE;
/* For convenience. We want to get at block info here */
pd = &pobj->blkinfo;
/* Read the data */
pobj->pblkbuff = rbuf = pc_read_blk(pobj->pdrive, pobj->blkinfo.my_block);
lastsegorder = 0;
while (rbuf)
{
pi = (DOSINODE *) &rbuf->data[0];
/* Look at the current inode */
pi += pd->my_index;
/* And look for a match */
while ( pd->my_index < INOPBLOCK )
{
matchfound = FALSE;
/* End of dir if name is 0 */
if (!pi->fname[0])
{
rtfs_set_errno(PENOENT); /* pc_findin: file or dir not found */
pc_free_scratch_blk(scratch);
pc_release_buf(rbuf);
return(FALSE);
}
if (pi->fname[0] == PCDELETE)
{
pc_zeroseglist(&s);
}
else
{
/* Support long file names */
if (pi->fattribute == CHICAGO_EXT)
{
lfn_node = (LFNINODE *) pi;
if (lfn_node->lfnorder & FIRST_NAMESEG)
{
pc_addtoseglist(&s, pd->my_block, pd->my_index);
/* .. we could build up the name here too */
lastsegorder = lfn_node->lfnorder;
s.ncksum = lfn_node->lfncksum;
}
else
{
if (s.nsegs)/* if a chain already exists */
{
if ( ((lfn_node->lfnorder & NAMESEG_ORDER) == (lastsegorder & NAMESEG_ORDER) - 1) &&
(lfn_node->lfncksum == s.ncksum) )
{
/* Add new segment to lfn chain */
lastsegorder = lfn_node->lfnorder;
pc_addtoseglist(&s, pd->my_block, pd->my_index);
}
else
{
/* disconnect chain... segments do not match */
lastsegorder = 0;
pc_zeroseglist(&s);
}
}
}
}
else
{
/* Note: Patcmp wo not match on deleted */
if (s.nsegs)
{
if (action == GET_INODE_STAR)
matchfound = TRUE;
else if (action == GET_INODE_DOTDOT)
matchfound = FALSE; /* 8.3, not lfn */
else
{
p = (byte*)pc_seglist2text(pobj->pdrive, &s,lfn);
if (!p)
matchfound = FALSE; /* 8.3, not lfn */
else
matchfound = pc_patcmp_vfat(filename, p ,dowildcard);
}
}
else
matchfound = FALSE; /* 8.3, not lfn */
if (matchfound)
matchfound = (BOOLEAN)(pc_cksum( (byte*) pi ) == s.ncksum);
else
{
if (action == GET_INODE_STAR)
matchfound = TRUE;
else if (action == GET_INODE_DOTDOT)
{
if (pi->fname[0] == CS_OP_ASCII('.') && pi->fname[1] == CS_OP_ASCII('.'))
matchfound = TRUE; /* 8.3, not lfn */
}
else
{
pc_cs_mfile(sfn,pi->fname,pi->fext);
matchfound = pc_patcmp_vfat_8_3(filename, sfn, dowildcard);
}
}
if (matchfound && pi->fattribute & AVOLUME &&
action != GET_INODE_STAR && action != GET_INODE_WILD)
{
/* Don't match volume labels if we are finding a specific match. */
matchfound = FALSE;
}
if (matchfound)
{
/* We found it */
/* See if it already exists in the inode list.
If so.. we use the copy from the inode list */
pfi = pc_scani(pobj->pdrive, rbuf->blockno, pd->my_index);
if (pfi)
{
pc_freei(pobj->finode);
pobj->finode = pfi;
}
else /* No inode in the inode list. Copy the data over
and mark where it came from */
{
pfi = pc_alloci();
if (pfi)
{
pc_freei(pobj->finode); /* Release the current */
pobj->finode = pfi;
pc_dos2inode(pobj->finode , pi );
pc_marki(pobj->finode , pobj->pdrive , pd->my_block,
pd->my_index );
if (s.nsegs)
{
if (pc_cksum( (byte*) pi ) != s.ncksum)
{
pc_zeroseglist(&s);
lastsegorder = 0;
}
pobj->finode->s = s;
}
else
pc_zeroseglist(&pobj->finode->s);
}
else
{
pc_free_scratch_blk(scratch);
pc_release_buf(rbuf);
return (FALSE);
}
}
/* Free, no error */
pc_free_scratch_blk(scratch);
pc_release_buf(rbuf);
return (TRUE);
} /* if (match) */
else /* disconnect chain... segments do not match */
{
pc_zeroseglist(&s);
}
} /* else (CHICAGO_EXT) */
} /* if (!PCDELETE) */
pd->my_index++;
pi++;
}
/* Not in that block. Try again */
pc_release_buf(rbuf);
/* Update the objects block pointer */
if (!pc_next_block(pobj))
break;
pd->my_index = 0;
pobj->pblkbuff = rbuf = pc_read_blk(pobj->pdrive, pobj->blkinfo.my_block);
}
if (!get_errno())
rtfs_set_errno(PENOENT); /* pc_findin: file or dir not found */
pc_free_scratch_blk(scratch);
return (FALSE);
}
/***************************************************************************
PC_INSERT_INODE - Insert a new inode into an existing directory inode.
Description
Take mom , a fully defined DROBJ, and pobj, a DROBJ with a finode
containing name, ext, etc, but not yet stitched into the inode buffer
pool, and fill in pobj and its inode, write it to disk and make the inode
visible in the inode buffer pool. (see also pc_mknode() )
Returns
Returns TRUE if all went well, FALSE on a write error, disk full error or
root directory full.
**************************************************************************/
/* Note: the parent directory is locked before this routine is called */
BOOLEAN pc_insert_inode(DROBJ *pobj , DROBJ *pmom, byte attr, CLUSTERTYPE initcluster, byte *filename, byte *fileext)
{
BLKBUFF *pbuff;
DIRBLK *pd;
DOSINODE *pi;
int i;
CLUSTERTYPE cluster;
DDRIVE *pdrive;
DATESTR crdate;
byte vffilename[9],vffileext[4];
byte cksum;
BOOLEAN end_of_dir;
int n_segs;
cluster = 0;
/* How many segments do we need */
n_segs = (rtfs_cs_strlen(filename) + 12 )/13;
RTFS_ARGSUSED_PVOID((void *) fileext);
if (!pc_malias(vffilename,vffileext, (byte *)filename,pmom))
return (FALSE); /* pc_malias set errno */
pc_init_inode( pobj->finode, vffilename, vffileext,
attr, initcluster, /*size*/ 0L ,pc_getsysdate(&crdate) );
/* Set up pobj */
pdrive = pobj->pdrive = pmom->pdrive;
pobj->isroot = FALSE;
pd = &pobj->blkinfo;
/* Now get the start of the dir */
pd->my_block = pd->my_frstblock = pc_firstblock(pmom);
if (!pd->my_block)
{
rtfs_set_errno(PEINVALIDBLOCKNUMBER); /* pc_insert_inode: Internal error, invalid block id */
return (FALSE);
}
else
pd->my_index = 0;
/* Read the data */
pobj->pblkbuff = pbuff = pc_read_blk(pobj->pdrive, pobj->blkinfo.my_block);
if (!pbuff)
return(FALSE);
/* If the alias contains Kanji E5 this goes on disk as 05 so we have to
checksum it with 05 */
if (pobj->finode->fname[0] == 0xe5)
{
pobj->finode->fname[0] = 0x05;
cksum = pc_cksum((byte*)pobj->finode);
pobj->finode->fname[0] = 0xe5;
}
else
cksum = pc_cksum((byte*)pobj->finode);
end_of_dir = FALSE;
pc_zeroseglist(&pobj->finode->s);
pobj->finode->s.ncksum = cksum;
while (pbuff)
{
i = pd->my_index = 0;
pi = (DOSINODE *) &pbuff->data[0];
/* look for a slot */
while ( i < INOPBLOCK )
{
if (pi->fname[0] == CS_OP_ASCII('\0'))
end_of_dir = TRUE;
if (pi->fname[0] == PCDELETE || end_of_dir)
{
/* Note that we fake an allocation of n_segs + 1 segments.
this scheme makes sure that there is room for the DOSINODE
immediately after the segment list. We reduce the segment
count before we write the segments */
if (pobj->finode->s.nsegs != (n_segs +1))
{
pc_addtoseglist(&pobj->finode->s, pd->my_block, i);
}
/* Write the lfn and dos inode if we have the space */
if (pobj->finode->s.nsegs == (n_segs +1))
{
/* Write the long file name entries to their respective
locations */
/* Drop the segment count by one before we write.
with this scheme we fall out of the loop and
put the DOSINODE in the correct location */
pc_reduceseglist(&pobj->finode->s);
if (!pc_seglist2disk(pobj->pdrive, &pobj->finode->s, filename))
{
pc_discard_buf(pbuff);
goto clean_and_fail;
}
pd->my_index = i;
/* Update the DOS disk */
pc_ino2dos( pi, pobj->finode );
/* Write the data */
/* Mark the inode in the inode buffer */
if (pc_write_blk(pbuff))
{
pc_marki(pobj->finode , pobj->pdrive , pd->my_block,
pd->my_index );
pc_release_buf(pbuff);
return(TRUE);
}
else
{
pc_release_buf(pbuff);
return(FALSE);
}
}
}
else
{
pc_zeroseglist(&pobj->finode->s);
}
i++;
pi++;
}
/* Not in that block. Try again */
pc_release_buf(pbuff);
/* Update the objects block pointer */
rtfs_set_errno(0); /* Should already be 0 but don't chance it */
if (!pc_next_block(pobj))
{
if (get_errno()) /* The zero was an error return */
return (FALSE);
/* Ok:There are no slots in mom. We have to make one. And copy our stuff in */
cluster = pc_grow_dir(pobj->pdrive, pmom);
if (!cluster)
return (FALSE);
/* Do not forget where the new item is */
pd->my_block = pc_cl2sector(pobj->pdrive , cluster);
pd->my_index = 0;
/* Zero out the cluster */
if (!pc_clzero( pobj->pdrive , cluster ) )
goto clean_and_fail;
/* Copy the item into the first block */
pobj->pblkbuff = pbuff = pc_init_blk( pobj->pdrive , pd->my_block);
if (!pbuff)
goto clean_and_fail;
}
else /* 02-06-2003 - Added Else, had been lost */
pobj->pblkbuff = pbuff = pc_read_blk(pobj->pdrive, pobj->blkinfo.my_block);
}
clean_and_fail:
if (cluster)
pc_truncate_dir(pdrive, pmom, cluster);
return (FALSE);
}
/****************************************************************************
PC_NIBBLEPARSE - Nibble off the left most part of a pathspec
Description
Take a pathspec (no leading D:). and parse the left most element into
filename and files ext. (SPACE right filled.).
Returns
Returns a pointer to the rest of the path specifier beyond file.ext
****************************************************************************/
/* Parse a path. Return NULL if problems or a pointer to the next */
byte *pc_nibbleparse(byte *filename, byte *fileext, byte *path) /* __fn__*/
{
byte *p;
byte *t = filename;
RTFS_ARGSUSED_PVOID((void *)fileext);
p = path;
if (!p) /* Path must exist */
return (0);
while (CS_OP_IS_NOT_EOS(p))
{
if (CS_OP_CMP_ASCII(p,'\\'))
{
CS_OP_INC_PTR(p);
break;
}
else
{
CS_OP_CP_CHR(t, p);
CS_OP_INC_PTR(t);
CS_OP_INC_PTR(p);
}
}
CS_OP_TERM_STRING(t);
return (p);
}
/*****************************************************************************
PC_CKSUM - Compute checksum on string
Description
Computes a single UTINY checksum based on the first 11 characters in
the input string (test). This value is used to link LFNINODE's on the
disk directory table.
Returns
The value of the checksum
*****************************************************************************/
byte pc_cksum(byte *test) /*__fn__*/
{
byte sum,i;
for (sum = i = 0; i < 11; i++)
{
sum = (byte)((((sum & 0x01)<<7)|((sum & 0xFE)>>1)) + test[i]);
}
return(sum);
}
void pcdel2lfi(LFNINODE *lfi, int nsegs) /* __fn__ */
{
for (;nsegs;nsegs--, lfi--)
lfi->lfnorder = PCDELETE;
}
BOOLEAN pc_deleteseglist(DDRIVE *pdrive, SEGDESC *s) /* __fn__ */
{
BLKBUFF *rbuf;
LFNINODE *lfi;
int ntodo_0,ntodo_1,ntodo_2;
if (!s->nsegs)
return(TRUE);
/* Read segblock[0] and copy text */
rbuf = pc_read_blk(pdrive, s->segblock[0]);
if (!rbuf)
return(FALSE);
lfi = (LFNINODE *) &rbuf->data[0];
lfi += s->segindex;
/* If the lfn segments span two or more blocks then segblock[0] contains the
last block, segblock[1] contains next to last, segblock[3] contains
the last if there are three. We delete all of the segments up to
and including segindex in last block value stored in segblock[0]. */
if (s->nsegs > s->segindex+1)
ntodo_0 = s->segindex+1;
else
ntodo_0 = s->nsegs;
if (ntodo_0 > 0) /* Test just in case should never be <= 0 */
pcdel2lfi(lfi, ntodo_0);
if ( !pc_write_blk(rbuf) )
{
pc_discard_buf(rbuf);
return (FALSE);
}
else
{
pc_release_buf(rbuf);
}
if (s->segblock[1])
{
rbuf = pc_read_blk(pdrive, s->segblock[1]);
if (!rbuf)
return (FALSE);
lfi = (LFNINODE *) &rbuf->data[0];
lfi += 15; /* The last index */
/* Delete the next N segments. Clip it at 16 since there are only
16 segments per block */
ntodo_1 = s->nsegs - ntodo_0;
if (ntodo_1 > 16)
ntodo_1 = 16;
if (ntodo_1 > 0) /* Test just in case should never be <= 0 */
pcdel2lfi(lfi, ntodo_1);
if ( !pc_write_blk ( rbuf ) )
{
pc_discard_buf(rbuf);
return (FALSE);
}
else
pc_release_buf(rbuf);
if (s->segblock[2])
{
rbuf = pc_read_blk(pdrive, s->segblock[2]);
if (!rbuf)
return (FALSE);
lfi = (LFNINODE *) &rbuf->data[0];
lfi += 15; /* The last index */
ntodo_2 = s->nsegs - (ntodo_1 + ntodo_0);
/* Delete the next N segments. Clip it at 16 since there are only
16 segments per block - this should not happen but just to be safe*/
if (ntodo_2 > 16)
ntodo_2 = 16;
if (ntodo_2 > 0) /* Test just in case should never be <= 0 */
pcdel2lfi(lfi, ntodo_2);
if ( !pc_write_blk ( rbuf ) )
{
pc_discard_buf(rbuf);
return (FALSE);
}
else
pc_release_buf(rbuf);
}
}
return(TRUE);
}
byte *text2lfi(byte *lfn, LFNINODE *lfi, int nsegs, byte ncksum, byte order) /* __fn__ */
{
int n;
BOOLEAN end_of_lfn = FALSE;
byte *pfi;
for (;nsegs && !end_of_lfn;nsegs--, lfi--, order++)
{
pfi = lfi->lfname1;
for(n=0; n<10; n += 2, pfi += 2)
{
if (end_of_lfn)
*pfi = *(pfi+1) = 0xff;
else
{
CS_OP_TO_LFN(pfi, lfn);
CS_OP_INC_PTR(lfn);
if ((*pfi== 0) && (*(pfi+1) == 0))
end_of_lfn = TRUE;
}
}
pfi = lfi->lfname2;
for(n=0; n<12; n += 2, pfi += 2)
{
if (end_of_lfn)
*pfi = *(pfi+1) = 0xff;
else
{
CS_OP_TO_LFN(pfi, lfn);
CS_OP_INC_PTR(lfn);
if ((*pfi== 0) && (*(pfi+1) == 0))
end_of_lfn = TRUE;
}
}
pfi = lfi->lfname3;
for(n=0; n<4; n += 2, pfi += 2)
{
if (end_of_lfn)
*pfi = *(pfi+1) = 0xff;
else
{
CS_OP_TO_LFN(pfi, lfn);
CS_OP_INC_PTR(lfn);
if ((*pfi== 0) && (*(pfi+1) == 0))
end_of_lfn = TRUE;
}
}
if (CS_OP_IS_EOS(lfn))
{
end_of_lfn = TRUE;
}
if (end_of_lfn)
order |= FIRST_NAMESEG;
lfi->lfnorder = order;
lfi->lfnattribute = 0x0F;
lfi->lfnres = 0;
lfi->lfncksum = ncksum;
lfi->lfncluster = 0x0000;
}
return(lfn);
}
BOOLEAN pc_seglist2disk(DDRIVE * pdrive, SEGDESC *s, byte *lfn) /* __fn__*/
{
BLKBUFF *rbuf;
LFNINODE *lfi;
int ntodo_0, ntodo_1, ntodo_2;
byte *psegtext;
if (!s->nsegs)
return(FALSE);
/* Read segblock[0] and copy text */
rbuf = pc_read_blk(pdrive, s->segblock[0]);
if (!rbuf)
return(FALSE);
lfi = (LFNINODE *) &rbuf->data[0];
lfi += s->segindex;
/* If the lfn segments span two or more blocks then segblock[0]
contains the last block and segblock[1] contains the next to last
if there are 3 blocks segblock[2] contains the first */
if (s->nsegs > s->segindex+1)
ntodo_0 = s->segindex+1;
else
ntodo_0 = s->nsegs;
psegtext = lfn; /* Initialize the variable so compile doesn't complain */
if (ntodo_0 > 0) /* should always be true */
psegtext = text2lfi(lfn, lfi, ntodo_0, s->ncksum, 1);
if ( !pc_write_blk ( rbuf ) )
{
pc_discard_buf(rbuf);
return (FALSE);
}
else
pc_release_buf(rbuf);
if (s->segblock[1])
{
rbuf = pc_read_blk(pdrive, s->segblock[1]);
if (!rbuf)
return(FALSE);
lfi = (LFNINODE *) &rbuf->data[0];
lfi += 15; /* The last index */
/* Do 16 more segments (means more blocks to follow) or whatever is
left */
ntodo_1 = s->nsegs - ntodo_0;
if (ntodo_1 > 16)
ntodo_1 = 16;
if (ntodo_1 > 0) /* Should always be true */
psegtext = text2lfi(psegtext, lfi, ntodo_1, s->ncksum, (byte) (ntodo_0+1));
if ( !pc_write_blk ( rbuf ) )
{
pc_discard_buf(rbuf);
return (FALSE);
}
else
pc_release_buf(rbuf);
if (s->segblock[2])
{
rbuf = pc_read_blk(pdrive, s->segblock[2]);
if (!rbuf)
return(FALSE);
lfi = (LFNINODE *) &rbuf->data[0];
lfi += 15; /* The last index */
/* Do 16 more segments (means more blocks to follow) or whatever is
left */
ntodo_2 = s->nsegs - (ntodo_1 + ntodo_0);
if (ntodo_2 > 16) /* Should always be <= 16 */
ntodo_2 = 16;
if (ntodo_2 > 0) /* Should always be true */
psegtext = text2lfi(psegtext, lfi, ntodo_2, s->ncksum, (byte) (ntodo_1+ntodo_0+1));
if ( !pc_write_blk ( rbuf ) )
{
pc_discard_buf(rbuf);
return (FALSE);
}
else
pc_release_buf(rbuf);
}
}
return(TRUE);
}
byte *lfi2text(byte *lfn, int *current_lfn_length, LFNINODE *lfi, int nsegs) /* __fn__ */
{
int n;
byte *pfi;
for (;nsegs;nsegs--, lfi--)
{
pfi = (byte *) lfi->lfname1;
for(n=0; n<10; n += 2,pfi+=2)
{
if((*pfi==0x00)&&(*(pfi+1)==0x00))
goto lfi2text_eos;
if (*current_lfn_length == FILENAMESIZE_CHARS)
return(0);
CS_OP_LFI_TO_TXT(lfn, pfi);
CS_OP_INC_PTR(lfn);
*current_lfn_length += 1;
}
pfi = (byte *) lfi->lfname2;
for(n=0; n<12; n += 2,pfi+=2)
{
if((*pfi==0x00)&&(*(pfi+1)==0x00))
goto lfi2text_eos;
if (*current_lfn_length == FILENAMESIZE_CHARS)
return(0);
CS_OP_LFI_TO_TXT(lfn, pfi);
CS_OP_INC_PTR(lfn);
*current_lfn_length += 1;
}
pfi = (byte *) lfi->lfname3;
for(n=0; n<4; n += 2,pfi+=2)
{
if((*pfi==0x00)&&(*(pfi+1)==0x00))
goto lfi2text_eos;
if (*current_lfn_length == FILENAMESIZE_CHARS)
return(0);
CS_OP_LFI_TO_TXT(lfn, pfi);
CS_OP_INC_PTR(lfn);
*current_lfn_length += 1;
}
CS_OP_TERM_STRING(lfn);
}
return(lfn);
lfi2text_eos:
CS_OP_TERM_STRING(lfn);
return(lfn);
}
byte *pc_seglist2text(DDRIVE * pdrive, SEGDESC *s, byte *lfn) /* __fn__ */
{
BLKBUFF *rbuf;
LFNINODE *lfi;
byte *p;
int ntodo_0,ntodo_1,ntodo_2;
int current_lfn_length;
CS_OP_TERM_STRING(lfn);
p = lfn;
if (!s->nsegs)
goto sl2_done;
/* Read segblock[0] and copy text */
rbuf = pc_read_blk(pdrive, s->segblock[0]);
if (!rbuf)
goto sl2_done;
lfi = (LFNINODE *) &rbuf->data[0];
lfi += s->segindex;
/* If the lfn segments span two or blocks then segblock[0] contains the
last block and segblock[1] contains next to last and if their are
three segblock[2] contains the first. */
if (s->nsegs > s->segindex+1)
ntodo_0 = s->segindex+1;
else
ntodo_0 = s->nsegs;
current_lfn_length = 0;
p = lfi2text(p, &current_lfn_length, lfi, ntodo_0);
pc_release_buf(rbuf);
if (!p)
return(0);
if (s->segblock[1])
{
rbuf = pc_read_blk(pdrive, s->segblock[1]);
if (!rbuf)
goto sl2_done;
lfi = (LFNINODE *) &rbuf->data[0];
lfi += 15; /* The last index */
/* Read the next N segments. Clip it at 16 since there are only
16 segments per block */
ntodo_1 = s->nsegs - ntodo_0;
if (ntodo_1 > 16)
ntodo_1 = 16;
if (ntodo_1)
p = lfi2text(p, &current_lfn_length, lfi, ntodo_1);
pc_release_buf(rbuf);
if (!p)
return(0);
if (s->segblock[2])
{
rbuf = pc_read_blk(pdrive, s->segblock[2]);
if (!rbuf)
goto sl2_done;
lfi = (LFNINODE *) &rbuf->data[0];
lfi += 15; /* The last index */
/* Read the next N segments. Clip it at 16 since there are only
16 segments per block */
ntodo_2 = s->nsegs - (ntodo_1 + ntodo_0);
if (ntodo_2 > 16)
ntodo_2 = 16;
if (ntodo_2)
p = lfi2text(p, &current_lfn_length, lfi, ntodo_2);
pc_release_buf(rbuf);
if (!p)
return(0);
}
}
sl2_done:
return(lfn);
}
void pc_zeroseglist(SEGDESC *s) /* __fn__ */
{
/* Note: we do not zero the checksum field here */
s->nsegs = 0;
s->segblock[0] =
s->segblock[1] =
s->segblock[2] =
s->segindex = 0;
}
void pc_addtoseglist(SEGDESC *s, BLOCKT my_block, int my_index) /*__fn__*/
{
s->nsegs += 1;
/* The block list is a LIFO stack so if it's empty start it
otherwise ripple copy in */
if (!s->segblock[0])
{
s->segblock[0] = my_block;
}
else if ( s->segblock[0] != my_block &&
s->segblock[1] != my_block &&
s->segblock[2] != my_block)
{
s->segblock[2] = s->segblock[1];
s->segblock[1] = s->segblock[0];
s->segblock[0] = my_block;
}
s->segindex = my_index;
}
/* This function is used by pc_insert_inode(). That function builds a seglist
that is 1 segment longer then the lfn. The extra segment is where the
dosinode will be placed. Before we write the the lfn to disk we call this
function to reduce the segment list by one. */
void pc_reduceseglist(SEGDESC *s) /*__fn__ */
{
if (s->nsegs) /* This should always be true */
{
s->nsegs -= 1;
if (s->segblock[2] && s->segindex == 0)
{
s->segblock[0] = s->segblock[1];
s->segblock[1] = s->segblock[2];
s->segblock[2] = 0;
s->segindex = INOPBLOCK-1;
}
else if (s->segblock[1] && s->segindex == 0)
{
s->segblock[0] = s->segblock[1];
s->segblock[1] = s->segblock[2] = 0;
s->segindex = INOPBLOCK-1;
}
else
{
if (s->segindex) /* This should always be true */
s->segindex -= 1;
}
}
}
/****************************************************************************
PC_PARSEPATH - Parse a path specifier into path,file,ext
Description
Take a path specifier in path and break it into three null terminated
strings topath,filename and file ext.
The result pointers must contain enough storage to hold the results.
Filename and fileext are BLANK filled to [8,3] spaces.
Rules:
SPEC PATH FILE EXT
B:JOE B: 'JOE ' ' '
B:\JOE B:\ 'JOE ' ' '
B:\DIR\JOE B:\DIR 'JOE ' ' '
B:DIR\JOE B:DIR 'JOE ' ' '
Returns
Returns TRUE.
****************************************************************************/
BOOLEAN pc_parsepath(byte *topath, byte *filename, byte *fileext, byte *path) /*__fn__*/
{
int i,keep_slash;
byte /**lastchar,*/*pfile,*pslash,*pcolon,*p,*pto,*pfilespace;
RTFS_ARGSUSED_PVOID((void *)fileext);
/* Check the path length, compare it EMAXPATH_CHARS (255) the
maximum filename length for VFAT */
if (rtfs_cs_strlen(path) > EMAXPATH_CHARS)
return(FALSE);
pslash = pfile = 0;
p = path;
pcolon = 0;
keep_slash = 0;
/* if A:\ or \ only keep slash */
i = 0;
while (CS_OP_IS_NOT_EOS(p))
{
if (CS_OP_CMP_ASCII(p,'\\'))
pslash = p;
else if (CS_OP_CMP_ASCII(p,':'))
{
if (i != 1) /* A: B: C: .. x: y: z: only */
return (FALSE);
pcolon = p;
}
CS_OP_INC_PTR(p);
i++;
}
if (pslash == path)
keep_slash = 1;
else if (pcolon && pslash)
{
CS_OP_INC_PTR(pcolon);
if (pslash == pcolon)
keep_slash = 1;
}
/*lastchar = 0;*/
p = path;
/* Find the file section, after the colon or last backslash */
while (CS_OP_IS_NOT_EOS(p))
{
if (CS_OP_CMP_ASCII(p,'\\') || CS_OP_CMP_ASCII(p,':') )
{
CS_OP_INC_PTR(p);
pfile = p;
}
else
{
CS_OP_INC_PTR(p);
}
}
/* Now copy the path. Up to the file or NULL if no file */
pto = topath;
if (pfile)
{
p = path;
while (p < pfile)
{
/* Don't put slash on the end if more than one */
if (p == pslash && !keep_slash)
break;
CS_OP_CP_CHR(pto, p);
CS_OP_INC_PTR(pto);
CS_OP_INC_PTR(p);
}
}
CS_OP_TERM_STRING(pto);
/* Now copy the file portion or the whole path to file if no path portion */
pto = filename;
if (pfile)
p = pfile;
else
p = path;
/* Skip leading spaces */
while (CS_OP_CMP_ASCII(p,' '))
CS_OP_INC_PTR(p);
/* Check the file length */
if (rtfs_cs_strlen(p) > FILENAMESIZE_CHARS)
return(FALSE);
pfilespace = 0;
while (CS_OP_IS_NOT_EOS(p))
{
CS_OP_CP_CHR(pto, p);
CS_OP_INC_PTR(p);
if (CS_OP_CMP_ASCII(pto,' '))
{
if (!pfilespace)
pfilespace = pto;
}
else
pfilespace = 0;
CS_OP_INC_PTR(pto);
}
/* If the trailing character is a space NULL terminate */
if (pfilespace)
{CS_OP_TERM_STRING(pfilespace);}
else
CS_OP_TERM_STRING(pto);
return(TRUE);
}
/******************************************************************************
PC_PATCMP - Compare a pattern with a string
Description
Compare size bytes of p against pattern. Applying the following rules.
If size == 8.
(To handle the way dos handles deleted files)
if p[0] = DELETED, never match
if pattern[0] == DELETED, match with 0x5
'?' in pattern always matches the current char in p.
'*' in pattern always matches the rest of p.
Returns
Returns TRUE if they match
****************************************************************************/
BOOLEAN pc_patcmp_vfat(byte *in_pat, byte *name, BOOLEAN dowildcard) /*__fn__*/
{
byte *pat, *p, *pp, *pn, *pp2, *pn2;
byte star[4];
BOOLEAN res = FALSE;
/* Convert *.* to just * */
p = pat = in_pat;
if (dowildcard && CS_OP_CMP_ASCII(p,'*'))
{
CS_OP_INC_PTR(p);
if (CS_OP_CMP_ASCII(p,'.'))
{
CS_OP_INC_PTR(p);
if (CS_OP_CMP_ASCII(p,'*'))
{
CS_OP_INC_PTR(p);
if (CS_OP_IS_EOS(p))
{
/* Change *.* to * but since the argument may have been
const we do it in a private buffer */
p = pat = star;
CS_OP_ASSIGN_ASCII(p,'*');
CS_OP_INC_PTR(p);
CS_OP_TERM_STRING(p);
}
}
}
}
/* * matches everything */
p = pat;
if (dowildcard && CS_OP_CMP_ASCII(p,'*'))
{
CS_OP_INC_PTR(p);
if (CS_OP_IS_EOS(p))
return(TRUE);
}
for (pp=pat,pn=name;CS_OP_IS_NOT_EOS(pp); CS_OP_INC_PTR(pn),CS_OP_INC_PTR(pp))
{
if(CS_OP_CMP_ASCII(pp,'*') && dowildcard)
{
pp2 = pp;
CS_OP_INC_PTR(pp2);
if (CS_OP_IS_EOS(pp2))
return(TRUE); /* '*' at end */
pn2 = pn;
/* We hit star. Now go past it and see if there is another
exact match. IE: a*YYY matches abcdefgYYY but not abcdefgXXX */
for (;!res && CS_OP_IS_NOT_EOS(pn2); CS_OP_INC_PTR(pn2))
{
res = res+pc_patcmp_vfat(pp2,pn2,TRUE);
}
return(res);
}
else if (CS_OP_CMP_CHAR(pp, pn))
;
else if (CS_OP_CMP_ASCII(pp,'?') && dowildcard)
;
else if (CS_OP_CMP_CHAR_NC(pp, pn))
;
else
return(FALSE);
}
if(CS_OP_IS_EOS(pn))
return(TRUE);
else
return(FALSE);
}
BOOLEAN pc_patcmp_vfat_8_3(byte *pat, byte *name, BOOLEAN dowildcard) /*__fn__*/
{
byte save_char;
BOOLEAN ret_val;
if (CS_OP_CMP_ASCII(name,PCDELETE))
return (FALSE);
save_char = *name;
if (*name == 0x05)
*name = 0xe5;
ret_val = pc_patcmp_vfat(pat, name, dowildcard);
*name = save_char;
return(ret_val);
}
/*****************************************************************************
PC_MALIAS - Create a unique alias for input_file
Description
Fills fname and fext with a valid short file name alias that is unique
in the destination directory (dest). Not to be confused with pc_galias,
which finds the currently used short file name alias for an existing
file.
Returns
TRUE if a unique alias could be found, FALSE otherwise
*****************************************************************************/
BOOLEAN pc_malias(byte *fname, byte *fext, byte *input_file, DROBJ *dest) /*__fn__*/
{
int try;
DROBJ *pobj;
BOOLEAN aliasunique;
byte alias[26];
byte ascii_alias[26];
/* See if already a valid alias. If so we use */
/* Note: This always fails for unicode */
if (pc_cs_malias(alias, input_file, -1)) /*__fn__*/
{
pc_ascii_str2upper(alias,alias);
pobj = pc_get_inode(0,dest,alias,0,GET_INODE_MATCH);
if(pobj)
{
pc_freeobj(pobj);
}
else
{
if (get_errno() != PENOENT)
return(FALSE);
rtfs_set_errno(0); /* Clear PENOENT */
pc_ascii_fileparse(fname,fext,(byte *)input_file);
pc_ascii_str2upper(fname,fname);
pc_ascii_str2upper(fext,fext);
return(TRUE);
}
}
/* Loop building up alias names and testing if unique */
try=0; /* i=0; i is the alias index */
do
{
try++;
if (!pc_cs_malias(alias, input_file, try))
{
/* Ran out of valid alias names. Use PENOSPC as errno */
rtfs_set_errno(PENOSPC);
return(FALSE);
}
pobj = pc_get_inode(0,dest,alias,0,GET_INODE_MATCH);
if(pobj)
{
aliasunique = FALSE;
pc_freeobj(pobj);
}
else
{
if (get_errno() != PENOENT)
return(FALSE);
aliasunique = TRUE;
}
}
while(!aliasunique);
rtfs_set_errno(0); /* Clear PENOENT */
/* Parse the alias into 8.3 */
CS_OP_CS_TO_ASCII_STR(ascii_alias,alias);
pc_ascii_fileparse(fname,fext,ascii_alias);
return(TRUE);
}
/* Byte oriented */
static BOOLEAN pc_allspace(byte *p, int i) /* __fn__*/
{while (i--) if (*p++ != ' ') return (FALSE); return (TRUE); }
BOOLEAN _illegal_lfn_char(byte ch)
{
if (pc_strchr(rtfs_strtab_user_string(USTRING_SYS_BADLFN), ch))
return(TRUE);
else
return(FALSE);
}
BOOLEAN pc_isdot(byte *fname, byte *fext) /* __fn__*/
{
RTFS_ARGSUSED_PVOID((void *)fext);
return (BOOLEAN)((*fname == '.') &&
((*(fname+1) == '\0') || (pc_allspace((fname+1),10))) );
}
BOOLEAN pc_isdotdot(byte *fname, byte *fext) /* __fn__*/
{
RTFS_ARGSUSED_PVOID((void *)fext);
return (BOOLEAN)( (*fname == '.') && (*(fname+1) == '.') &&
((*(fname+2) == '\0') || (pc_allspace((fname+2),9)) ) );
}
BOOLEAN pc_delete_lfn_info(DROBJ *pobj)
{
return(pc_deleteseglist(pobj->pdrive, &pobj->finode->s));
}
void pc_zero_lfn_info(FINODE *pdir)
{
pc_zeroseglist(&pdir->s);
}
BOOLEAN pc_get_lfn_filename(DROBJ *pobj, byte *path)
{
if (pobj->finode->s.nsegs)
{
if (pc_seglist2text(pobj->pdrive, &pobj->finode->s, path))
return(TRUE);
else
return(FALSE);
}
else
return(FALSE);
}
/* scan_for_bad_lfns(DROBJ *pmom)
*
* Scan through a directory and count all Win95 long file name errors.
*
* Errors detected:
* Bad lfn checksums
* Bad lfn sequence numbers
* Stray lfn chains
* Incomplete lfn chains
*
* If gl.delete_bad_lfn is set, free up the directory space used up by
* invalid long file name information by deleting invalid chains
*
* Returns: number of invalid lfn chains found
*
* This fn is called by chkdsk. It is based on pc_findin
*/
dword scan_for_bad_lfns(DROBJ *pmom, int delete_bad_lfn) /*__fn__*/
{
DROBJ *pobj;
BLKBUFF *rbuf;
DIRBLK *pd;
DOSINODE *pi;
LFNINODE *lfn_node;
SEGDESC s;
byte lastsegorder;
dword bad_lfn_count;
pc_zeroseglist(&s);
lastsegorder = 0;
/* pobj will be used to scan through the directory */
pobj = pc_mkchild(pmom);
if (!pobj)
return(0);
bad_lfn_count = 0;
/* For convenience. We want to get at block info here */
pd = &pobj->blkinfo;
/* Read the data */
pobj->pblkbuff = rbuf = pc_read_blk(pobj->pdrive, pobj->blkinfo.my_block);
while (rbuf)
{
pi = (DOSINODE *) &rbuf->data[0];
/* Look at the current inode */
pi += pd->my_index;
while ( pd->my_index < INOPBLOCK )
{
/* End of dir if name is 0 */
if (!pi->fname[0])
{
pc_release_buf(rbuf);
pc_freeobj(pobj);
return(bad_lfn_count);
}
if (pi->fname[0] == PCDELETE)
{
if (s.nsegs)
{
/* lfn chain interrupted by empty dir entry */
bad_lfn_count++;
if (delete_bad_lfn)
pc_deleteseglist(pobj->pdrive, &s);
pc_zeroseglist(&s);
lastsegorder = 0;
}
}
else
{
if (pi->fattribute == CHICAGO_EXT)
{
/* Found a piece of an lfn */
lfn_node = (LFNINODE *) pi;
if (lfn_node->lfnorder & FIRST_NAMESEG)
{
if (s.nsegs) /* if a chain already exists */
{
/* lfn chain begins in the middle of another one;
Delete the one that was interrupted, keep the
new one. */
bad_lfn_count++;
if (delete_bad_lfn)
pc_deleteseglist(pobj->pdrive, &s);
pc_zeroseglist(&s);
}
lastsegorder = lfn_node->lfnorder;
pc_addtoseglist(&s, pd->my_block, pd->my_index);
s.ncksum = lfn_node->lfncksum;
}
else
{
/* optimization - the current segment should first be
linked onto the chain in each of the branches
below */
pc_addtoseglist(&s, pd->my_block, pd->my_index);
if ((s.nsegs - 1) &&
(lfn_node->lfnorder & NAMESEG_ORDER) == (lastsegorder & NAMESEG_ORDER) - 1 &&
lfn_node->lfncksum == s.ncksum )
{
/* Sequence number and checksum match; Add new
segment to lfn chain */
lastsegorder = lfn_node->lfnorder;
}
else
{
/* New segment has a checksum or sequence number
that doesn't match the current chain; Delete
the whole chain plus new segment */
bad_lfn_count++;
if (delete_bad_lfn)
pc_deleteseglist(pobj->pdrive, &s);
pc_zeroseglist(&s);
lastsegorder = 0;
}
}
}
else
/* if (pi->fattribute != CHICAGO_EXT) */
{
/* Found a file entry - make sure our lfn chain matches
(if we have one) */
if (s.nsegs) /* if a chain has been built up */
{
if (pc_cksum((byte*)pi) != s.ncksum ||
(lastsegorder & NAMESEG_ORDER) != 1)
{
/* chain's checksum doesn't match the DOSINODE,
or the last sequence number isn't 1; Delete
the lfn chain */
bad_lfn_count++;
if (delete_bad_lfn)
pc_deleteseglist(pobj->pdrive, &s);
}
/* We want to release this chain, whether good or bad */
pc_zeroseglist(&s);
lastsegorder = 0;
}
} /* if (!CHICAGO_EXT) */
} /* if (!PCDELETE) */
pd->my_index++;
pi++;
}
/* Current block is clean; go to next one */
pc_release_buf(rbuf);
/* Update the objects block pointer */
if (!pc_next_block(pobj))
break;
pd->my_index = 0;
pobj->pblkbuff = rbuf = pc_read_blk(pobj->pdrive, pobj->blkinfo.my_block);
}
pc_freeobj(pobj);
return (bad_lfn_count);
}
#endif