📄 dir.c
字号:
#include "vxd.h"
#include "dir.h"
#include "cache.h"
#include "super.h"
#include "inode.h"
#include "block.h"
#include "util.h"
#include "unixstat.h"
#include "shared\vxddebug.h"
/*
* First time I ever wrote two functions with
* cross-dependency :-)
*/
static TInode* FollowLink(TInode *DirInode, TInode *LinkInode);
static TInode* doDirName2Inode(TInode *DirInode, char *DirName, int DirNameLength);
#define MUSTMATCH(ir_attr,attr) ((((ir_attr & (attr)<<8) \
^ ir_attr) \
& FILE_ATTRIBUTE_MUSTMATCH) == 0)
/**********************************
*
* GLOBAL DATA
*
**********************************/
// none
/**********************************
*
* STATIC DATA
*
**********************************/
static TCache *sDirCache;
static char sDirCacheName[] = "DirCache";
/********************************
*
* STATIC HELPERS
*
*********************************/
static BOOL DirIsEntryNameMatch(TFindContext *FindContext, BOOL isShortName)
{
int ShortNameLen;
BOOL Succes;
if (isShortName)
{
/*
* Search on short Filenames
*/
LongToShort(FindContext->FoundDirEntry->name, FindContext->FoundDirEntry->name_len, FindContext->ShortName, FindContext->FoundDirEntry->inode);
VxdDebugPrint(D_DIR, "shortfilename=%s", FindContext->ShortName);
ShortNameLen = strlen(FindContext->ShortName);
Succes = isEntryMatch(FindContext->ShortName, FindContext->SearchName, FindContext->SearchFlags, ShortNameLen);
}
else
{
/*
* Search on LFN Filenames
*/
Succes = isEntryMatch(FindContext->FoundDirEntry->name, FindContext->SearchName, FindContext->SearchFlags, FindContext->FoundDirEntry->name_len);
}
return Succes;
}
static BOOL DirIsEntryAttribMatch(TFindContext *FindContext, BOOL isShortName)
{
BOOLEAN Success;
ULONG Attrib;
BYTE ExcludeMask;
Attrib = FindContext->FoundInode->DosAttrib;
if (isShortName)
{
if (Attrib)
Success = (FindContext->SearchFlags & Attrib);
else
Success = TRUE;
}
else
{
ExcludeMask = (~FindContext->SearchFlags) & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_SYSTEM);
Success = (!(Attrib & ExcludeMask) && MUSTMATCH(FindContext->SearchFlags, Attrib));
}
return Success;
}
/*
* Lookup an directory entry and return the object's inode nr
*
* PRE:
* DirInode is indeed an directory inode. EntryName is zero-terminated
*
* POST:
* <none>
*
* IN:
* DirInode : pointer to a fully initialised directory inode
* EntryName : zero-terminated entry name which is to be looked up
*
* OUT:
* <none>
*
* RETURNS
* Succes : inode number of the entry name
* otherwise : 0
*
*/
static ULONG DirLookupEntry(TInode *DirInode, char *EntryName, int NameLen, BOOL isShortName)
{
char ShortName[13];
char zEntryName[13];
ULONG InodeNo, nrBlocks, BlockSize, BlockNo;
USHORT RecLen, ShortNameLen;
TBlock *Block;
ext2_dir_entry *DirEntry;
ext2_inode *Ext2Inode;
char *DirData;
#ifdef DEBUG
char zName[256];
memcpy(zName, EntryName, NameLen);
zName[NameLen] = 0;
#endif
VxdDebugPrint(D_DIR, "DirLookupEntry(%s): inode=(dev=%s, ino=%lu), name=%s",
isShortName ? "short" : "lfn",
DirInode->Super->DeviceName,
DirInode->InodeNo,
zName);
/*
* The EntryName is not zero-terminated
*/
if (isShortName)
{
memcpy(zEntryName, EntryName, NameLen);
zEntryName[NameLen] = 0;
}
Ext2Inode = InodeLock(DirInode);
/*
* init some locals
*/
InodeNo = 0;
BlockSize = DevGetBlockSize(DirInode->Super->Device);
nrBlocks = Ext2Inode->i_size / BlockSize;
/*
* Sanity check
*/
if (nrBlocks * BlockSize != Ext2Inode->i_size)
{
VxdDebugPrint(D_ERROR, "DirLookupEntry: i_size=%lu not multiple of BlockSize, done", Ext2Inode->i_size);
goto lookup_done;
}
/*
* itterate over directory blocks
*/
for (BlockNo=0 ; BlockNo<nrBlocks ; BlockNo++)
{
VxdDebugPrint(D_DIR, "DirLookupEntry: looking in block=%lu of blocks=%lu", BlockNo, nrBlocks);
if (!(Block = InodeGetBlock(DirInode, BlockNo)))
{
VxdDebugPrint(D_ERROR, "DirLookupEntry: could not read block %i, continuing on next block", BlockNo);
continue;
}
/*
* For each block we loop over the contained
* directory entries and look for a match.
*/
DirData = BlockLock(Block);
RecLen = 0;
do
{
DirEntry = (ext2_dir_entry *) (DirData + RecLen);
/*
* Add each entry to the cache
*/
CacheAddByName(sDirCache, (ULONG) DirInode->Super->Device, DirInode->InodeNo, DirEntry->name, DirEntry->name_len, &DirEntry->inode);
/*
* The name we are looking for is short, so
* convert the entryname to 8.3
* Then do a case-insensitive search
*/
if (isShortName)
{
LongToShort(DirEntry->name, DirEntry->name_len, ShortName, DirEntry->inode);
ShortNameLen = strlen(ShortName);
if (isEntryMatch(ShortName, zEntryName, 0, ShortNameLen))
{
/*
* Ok, this is the one we need, break
* to the exit code
*/
InodeNo = DirEntry->inode;
BlockUnlock(Block);
goto lookup_done;
}
else
{
VxdDebugPrint(D_DIR, "DirLookupEntry(short): name=%s, no match", ShortName);
}
}
else
{
if (DirEntry->name_len == NameLen)
{
/*
* We must have an exact match
*/
if (strncmp(DirEntry->name, EntryName, NameLen) == 0)
{
/*
* Ok, this is the one we need, break
* to the exit code
*/
InodeNo = DirEntry->inode;
BlockUnlock(Block);
goto lookup_done;
}
else
{
#ifdef DEBUG
memcpy(zName, DirEntry->name, DirEntry->name_len);
zName[DirEntry->name_len] = 0;
VxdDebugPrint(D_DIR, "DirLookupEntry(lfn): name=%s, no match", zName);
#endif
}
}
else
{
#ifdef DEBUG
memcpy(zName, DirEntry->name, DirEntry->name_len);
zName[DirEntry->name_len] = 0;
VxdDebugPrint(D_DIR, "DirLookupEntry(lfn): name=%s, len=%i, no len match", zName, DirEntry->name_len);
#endif
}
}
RecLen += DirEntry->rec_len;
} while (RecLen < BlockSize);
BlockUnlock(Block);
}
lookup_done:
VxdDebugPrint(D_DIR, "DirLookupEntry done, ino=%lu", InodeNo);
InodeUnlock(DirInode);
return InodeNo;
}
static ULONG DirName2InodeNo(TInode *Inode, char *PathName, int PathLen, BOOL isShortName)
{
ULONG InodeNo;
/*
* First, see if it's in the cache
*/
InodeNo = 0;
if (!CacheLookupByName(sDirCache, (ULONG) Inode->Super->Device, Inode->InodeNo, PathName, PathLen, &InodeNo))
{
/*
* No, not in the cache, read the directory
*/
InodeNo = DirLookupEntry(Inode, PathName, PathLen, isShortName);
}
return InodeNo;
}
/*
* Get Inode identified by the PathName
*
* IN:
*
* OUT:
*
* RETURNS:
*/
static TInode* doDirName2Inode(TInode *DirInode, char *DirName, int DirNameLength)
{
char ShortName[13];
char *PathStart, *PathEnd;
int PathLen;
ULONG InodeNo;
TInode *ParentInode;
TInode *ChildInode;
TInode *FollowedInode;
char Name[65];
/*
* DirName2Inode explicitly requests for a BCS pathname
* and not for a Unicode pathname. This has the disadvantage
* that all FSD requests (dir/open/delete etc) have to
* do the unicode->bcs conversion themselves. However,
* in order to following links, it is easiest to recursively
* call DirName2Inode (for absolute links only!). In that
* case we simply cannot have a MAX_PATH array on the stack.
*/
/*
* If path is empty we return the directory inode
*/
if (!DirName || !DirNameLength)
return InodeCloneInode(DirInode);
/*
* If Dir name is absolute, get the root inode
*/
if (*DirName == '/')
{
DirName++;
DirNameLength--;
ParentInode = InodeGetRootInode(DirInode->Super);
}
else
ParentInode = InodeCloneInode(DirInode);
PathEnd = PathStart = DirName;
while(DirNameLength)
{
/*
* Make sure we the inode is a directory
*/
if (!S_ISDIR(ParentInode->Mode))
{
VxdDebugPrint(D_ERROR, "DirName2Inode: inode(dev=%s, ino=%lu) is not directory", ParentInode->Super->DeviceName, ParentInode->InodeNo);
ChildInode = 0;
break;
}
/*
* Stop criterium is reached iff PathEnd == 0,
* that is, there are no more path separators
*/
if ((PathEnd = strnchr(PathStart, '/', DirNameLength)))
{
PathLen = PathEnd - PathStart;
}
else
{
PathLen = DirNameLength;
}
/*
* Now, do some serious name 2 inode resolving.
*/
if (PathLen > 12)
{
/*
* This one is easy, it is a pure LFN filename, so
* we have to do an exact match
*/
if (!(InodeNo = DirName2InodeNo(ParentInode, PathStart, PathLen, FALSE)))
{
ChildInode = 0;
break;
}
}
else
{
/*
* More difficult.
* The path may be:
* a) a name issued by a LFN filename aware app.
* b) a converted LFN name (containing a ~)
* c) a name issued by a non-LFN aware application
*
* Case b is simple and can be detected. Case
* a and b are indistinguishable.
* Consider open "FTP", this could issued from a
* dos application that is returned "FTP" (remember
* that we return "FTP" in ffirst/fnext calls when
* the entryname is "ftp"!!). It could also be
* issued by a win32 application and the filename
* is indeed "FTP" (its real name)
*/
if (strnchr(PathStart, '~', PathLen))
{
if (!(InodeNo = DirName2InodeNo(ParentInode, PathStart, PathLen, TRUE)))
{
ChildInode = 0;
break;
}
}
else
{
/*
* First try an exact match
*/
if (!(InodeNo = DirName2InodeNo(ParentInode, PathStart, PathLen, FALSE)))
{
/*
* Now, iff the path is _all_ uppercased
* we try an exact match with the lower
* cased filename
*/
// if (StrConvAndTestToLower(ShortName, PathStart, PathLen))
// {
// if (!(InodeNo = DirName2InodeNo(ParentInode, ShortName, PathLen, FALSE)))
// {
// ChildInode = 0;
// break;
// }
// }
// else
{
/*
* Try matching names using short file
* names
*/
if (!(InodeNo = DirName2InodeNo(ParentInode, PathStart, PathLen, TRUE)))
{
ChildInode = 0;
break;
}
}
}
}
}
/*
* Next, Get the TInode of the just found inode
*/
if (!(ChildInode = InodeGetInode(ParentInode, InodeNo, PathStart, PathLen)))
{
ChildInode = 0;
break;
}
/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -