mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 01:55:19 +00:00
1481 lines
47 KiB
C++
1481 lines
47 KiB
C++
////////////////////////////////////////////////////////////////////
|
|
// Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine
|
|
// All rights reserved
|
|
// This file was released under the GPLv2 on June 2015.
|
|
////////////////////////////////////////////////////////////////////
|
|
/*
|
|
Module name:
|
|
|
|
udf_info.cpp
|
|
|
|
Abstract:
|
|
|
|
This file contains filesystem-specific routines
|
|
for Directory tree & related structures support
|
|
|
|
*/
|
|
|
|
#include "udf.h"
|
|
|
|
#ifdef UDF_CHECK_UTIL
|
|
#include "..\namesup.h"
|
|
#else
|
|
#ifdef UDF_BUG_CHECK_ID
|
|
#undef UDF_BUG_CHECK_ID
|
|
#endif //UDF_BUG_CHECK_ID
|
|
#endif //UDF_CHECK_UTIL
|
|
|
|
#define UDF_BUG_CHECK_ID UDF_FILE_UDF_INFO_DIR
|
|
|
|
#define MEM_USDIRHASH_TAG "USDirHash"
|
|
|
|
#define UDF_DUMP_DIRTREE
|
|
#ifdef UDF_DUMP_DIRTREE
|
|
#define DirPrint(x) UDFPrint(x)
|
|
#else
|
|
#define DirPrint(x) {;}
|
|
#endif
|
|
|
|
/*
|
|
This routine initializes DirIndex array
|
|
*/
|
|
PDIR_INDEX_HDR
|
|
UDFDirIndexAlloc(
|
|
IN uint_di i
|
|
)
|
|
{
|
|
uint_di j,k;
|
|
PDIR_INDEX_HDR hDirNdx;
|
|
PDIR_INDEX_ITEM* FrameList;
|
|
|
|
if(!i)
|
|
return NULL;
|
|
#ifdef UDF_LIMIT_DIR_SIZE
|
|
if(i>UDF_DIR_INDEX_FRAME)
|
|
return NULL;
|
|
#endif //UDF_LIMIT_DIR_SIZE
|
|
|
|
j = i >> UDF_DIR_INDEX_FRAME_SH;
|
|
i &= (UDF_DIR_INDEX_FRAME-1);
|
|
|
|
hDirNdx = (PDIR_INDEX_HDR)MyAllocatePoolTag__(UDF_DIR_INDEX_MT, sizeof(DIR_INDEX_HDR)+(j+(i!=0))*sizeof(PDIR_INDEX_ITEM), MEM_DIR_HDR_TAG);
|
|
if(!hDirNdx) return NULL;
|
|
RtlZeroMemory(hDirNdx, sizeof(DIR_INDEX_HDR));
|
|
|
|
FrameList = (PDIR_INDEX_ITEM*)(hDirNdx+1);
|
|
for(k=0; k<j; k++, FrameList++) {
|
|
(*FrameList) = (PDIR_INDEX_ITEM)MyAllocatePoolTag__(UDF_DIR_INDEX_MT, UDF_DIR_INDEX_FRAME*sizeof(DIR_INDEX_ITEM), MEM_DIR_NDX_TAG);
|
|
if(!(*FrameList)) {
|
|
free_hdi:
|
|
// item pointet by FrameList is NULL, it could not be allocated
|
|
while(k) {
|
|
k--;
|
|
FrameList--;
|
|
MyFreePool__(*FrameList);
|
|
}
|
|
MyFreePool__(hDirNdx);
|
|
return NULL;
|
|
}
|
|
RtlZeroMemory((*FrameList), UDF_DIR_INDEX_FRAME*sizeof(DIR_INDEX_ITEM));
|
|
}
|
|
if(i) {
|
|
(*FrameList) = (PDIR_INDEX_ITEM)MyAllocatePoolTag__(UDF_DIR_INDEX_MT, AlignDirIndex(i)*sizeof(DIR_INDEX_ITEM), MEM_DIR_NDX_TAG);
|
|
if(!(*FrameList))
|
|
goto free_hdi;
|
|
RtlZeroMemory((*FrameList), i*sizeof(DIR_INDEX_ITEM));
|
|
}
|
|
|
|
hDirNdx->FrameCount = j+(i!=0);
|
|
hDirNdx->LastFrameCount = i ? i : UDF_DIR_INDEX_FRAME;
|
|
|
|
return hDirNdx;
|
|
} // UDFDirIndexAlloc()
|
|
|
|
/*
|
|
This routine releases DirIndex array
|
|
*/
|
|
void
|
|
UDFDirIndexFree(
|
|
PDIR_INDEX_HDR hDirNdx
|
|
)
|
|
{
|
|
uint32 k;
|
|
PDIR_INDEX_ITEM* FrameList;
|
|
|
|
FrameList = (PDIR_INDEX_ITEM*)(hDirNdx+1);
|
|
if(!hDirNdx) return;
|
|
for(k=0; k<hDirNdx->FrameCount; k++, FrameList++) {
|
|
if(*FrameList) MyFreePool__(*FrameList);
|
|
}
|
|
MyFreePool__(hDirNdx);
|
|
} // UDFDirIndexFree();
|
|
|
|
/*
|
|
This routine grows DirIndex array
|
|
*/
|
|
OSSTATUS
|
|
UDFDirIndexGrow(
|
|
IN PDIR_INDEX_HDR* _hDirNdx,
|
|
IN uint_di d // increment
|
|
)
|
|
{
|
|
uint_di j,k;
|
|
PDIR_INDEX_HDR hDirNdx = *_hDirNdx;
|
|
PDIR_INDEX_ITEM* FrameList;
|
|
|
|
if(d > UDF_DIR_INDEX_FRAME)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
j = hDirNdx->LastFrameCount+d;
|
|
|
|
if(j > UDF_DIR_INDEX_FRAME) {
|
|
#ifndef UDF_LIMIT_DIR_SIZE // release
|
|
// Grow header
|
|
k = hDirNdx->FrameCount;
|
|
if(!MyReallocPool__((int8*)hDirNdx, sizeof(DIR_INDEX_HDR) + k*sizeof(PDIR_INDEX_ITEM),
|
|
(int8**)(&hDirNdx), sizeof(DIR_INDEX_HDR) + (k+1)*sizeof(PDIR_INDEX_ITEM) ) )
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
FrameList = (PDIR_INDEX_ITEM*)(hDirNdx+1);
|
|
// Grow last frame
|
|
if(!MyReallocPool__((int8*)(FrameList[k-1]), AlignDirIndex(hDirNdx->LastFrameCount)*sizeof(DIR_INDEX_ITEM),
|
|
(int8**)(&(FrameList[k-1])), UDF_DIR_INDEX_FRAME*sizeof(DIR_INDEX_ITEM) ) )
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
RtlZeroMemory(&(FrameList[k-1][hDirNdx->LastFrameCount]),
|
|
(UDF_DIR_INDEX_FRAME-hDirNdx->LastFrameCount)*sizeof(DIR_INDEX_ITEM));
|
|
hDirNdx->LastFrameCount = UDF_DIR_INDEX_FRAME;
|
|
// Allocate new frame
|
|
FrameList[k] = (PDIR_INDEX_ITEM)MyAllocatePoolTag__(UDF_DIR_INDEX_MT, AlignDirIndex(j-UDF_DIR_INDEX_FRAME)*sizeof(DIR_INDEX_ITEM), MEM_DIR_NDX_TAG );
|
|
if(!FrameList[k])
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
hDirNdx->FrameCount++;
|
|
RtlZeroMemory(FrameList[k], (j-UDF_DIR_INDEX_FRAME)*sizeof(DIR_INDEX_ITEM));
|
|
hDirNdx->LastFrameCount = j-UDF_DIR_INDEX_FRAME;
|
|
(*_hDirNdx) = hDirNdx;
|
|
#else // UDF_LIMIT_DIR_SIZE
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
#endif // UDF_LIMIT_DIR_SIZE
|
|
} else {
|
|
k = hDirNdx->FrameCount;
|
|
FrameList = (PDIR_INDEX_ITEM*)(hDirNdx+1);
|
|
if(!MyReallocPool__((int8*)(FrameList[k-1]), AlignDirIndex(hDirNdx->LastFrameCount)*sizeof(DIR_INDEX_ITEM),
|
|
(int8**)(&(FrameList[k-1])), AlignDirIndex(j)*sizeof(DIR_INDEX_ITEM) ) )
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
RtlZeroMemory(&(FrameList[k-1][hDirNdx->LastFrameCount]),
|
|
(j-hDirNdx->LastFrameCount)*sizeof(DIR_INDEX_ITEM));
|
|
hDirNdx->LastFrameCount = j;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
} // end UDFDirIndexGrow()
|
|
|
|
/*
|
|
Thisd routine truncates DirIndex array
|
|
*/
|
|
OSSTATUS
|
|
UDFDirIndexTrunc(
|
|
IN PDIR_INDEX_HDR* _hDirNdx,
|
|
IN uint_di d // decrement
|
|
)
|
|
{
|
|
uint_di j,k;
|
|
|
|
if(d > UDF_DIR_INDEX_FRAME) {
|
|
OSSTATUS status;
|
|
while(d) {
|
|
k = (d > UDF_DIR_INDEX_FRAME) ? UDF_DIR_INDEX_FRAME : d;
|
|
if(!OS_SUCCESS(status = UDFDirIndexTrunc(_hDirNdx, k))) {
|
|
return status;
|
|
}
|
|
d -= k;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
PDIR_INDEX_HDR hDirNdx = *_hDirNdx;
|
|
PDIR_INDEX_ITEM* FrameList;
|
|
|
|
j = UDF_DIR_INDEX_FRAME+hDirNdx->LastFrameCount-d;
|
|
FrameList = (PDIR_INDEX_ITEM*)(hDirNdx+1);
|
|
k = hDirNdx->FrameCount-1;
|
|
|
|
if(j <= UDF_DIR_INDEX_FRAME) {
|
|
// free last frame
|
|
if(!k && (j < 2)) {
|
|
// someone tries to trunc. residual entries...
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
MyFreePool__(FrameList[k]);
|
|
FrameList[k] = NULL;
|
|
hDirNdx->LastFrameCount = UDF_DIR_INDEX_FRAME;
|
|
hDirNdx->FrameCount--;
|
|
// Truncate new last frame
|
|
if(!MyReallocPool__((int8*)(FrameList[k-1]), UDF_DIR_INDEX_FRAME*sizeof(DIR_INDEX_ITEM),
|
|
(int8**)(&(FrameList[k-1])), AlignDirIndex(j)*sizeof(DIR_INDEX_ITEM) ) )
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
hDirNdx->LastFrameCount = j;
|
|
// Truncate header
|
|
if(!MyReallocPool__((int8*)hDirNdx, sizeof(DIR_INDEX_HDR) + (k+1)*sizeof(PDIR_INDEX_ITEM),
|
|
(int8**)(&hDirNdx), sizeof(DIR_INDEX_HDR) + k*sizeof(PDIR_INDEX_ITEM) ) )
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
(*_hDirNdx) = hDirNdx;
|
|
|
|
} else {
|
|
|
|
j -= UDF_DIR_INDEX_FRAME;
|
|
if(!k && (j < 2)) {
|
|
// someone tries to trunc. residual entries...
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if(!MyReallocPool__((int8*)(FrameList[k]), AlignDirIndex(hDirNdx->LastFrameCount)*sizeof(DIR_INDEX_ITEM),
|
|
(int8**)(&(FrameList[k])), AlignDirIndex(j)*sizeof(DIR_INDEX_ITEM) ) )
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
hDirNdx->LastFrameCount = j;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
} // end UDFDirIndexTrunc()
|
|
|
|
#if defined _X86_ && !defined UDF_LIMIT_DIR_SIZE
|
|
#ifdef _MSC_VER
|
|
#pragma warning(disable:4035) // re-enable below
|
|
#endif
|
|
/*
|
|
This routine returns pointer to DirIndex item with index i.
|
|
*/
|
|
#if defined(_MSC_VER) && !defined(__clang__)
|
|
__declspec (naked)
|
|
#endif
|
|
PDIR_INDEX_ITEM
|
|
__fastcall
|
|
UDFDirIndex(
|
|
IN PDIR_INDEX_HDR hDirNdx, // ECX
|
|
IN uint32 i // EDX
|
|
)
|
|
{
|
|
#if defined(_MSC_VER) && !defined(__clang__)
|
|
__asm {
|
|
push ebx
|
|
push ecx
|
|
push edx
|
|
|
|
// mov ebx,hDirNdx
|
|
mov ebx,ecx
|
|
mov ecx,edx
|
|
or ebx,ebx
|
|
jz EO_udi_err
|
|
|
|
mov eax,ecx
|
|
shr ecx,UDF_DIR_INDEX_FRAME_SH ; ecx = j
|
|
mov edx,[ebx]hDirNdx.FrameCount ; edx = k
|
|
cmp ecx,edx
|
|
jae EO_udi_err
|
|
|
|
and eax,(1 shl UDF_DIR_INDEX_FRAME_SH)-1 ; eax = i
|
|
dec edx
|
|
cmp ecx,edx
|
|
jb No_check
|
|
|
|
cmp eax,[ebx].LastFrameCount
|
|
jae EO_udi_err
|
|
No_check:
|
|
add ebx,size DIR_INDEX_HDR ; ((PDIR_INDEX_ITEM*)(hDirNdx+1))...
|
|
mov ebx,[ebx+ecx*4] ; ...[j]...
|
|
mov edx,size DIR_INDEX_ITEM
|
|
mul edx ; ...[i]...
|
|
add eax,ebx ; &(...)
|
|
jmp udi_OK
|
|
EO_udi_err:
|
|
xor eax,eax
|
|
udi_OK:
|
|
pop edx
|
|
pop ecx
|
|
pop ebx
|
|
|
|
ret
|
|
}
|
|
#else
|
|
/* FIXME ReactOS */
|
|
uint_di j, k;
|
|
if( hDirNdx &&
|
|
((j = (i >> UDF_DIR_INDEX_FRAME_SH)) < (k = hDirNdx->FrameCount) ) &&
|
|
((i = (i & (UDF_DIR_INDEX_FRAME-1))) < ((j < (k-1)) ? UDF_DIR_INDEX_FRAME : hDirNdx->LastFrameCount)) )
|
|
return &( (((PDIR_INDEX_ITEM*)(hDirNdx+1))[j])[i] );
|
|
return NULL;
|
|
#endif
|
|
}
|
|
#ifdef _MSC_VER
|
|
#pragma warning(default:4035)
|
|
#endif
|
|
#endif // _X86_
|
|
|
|
/*
|
|
This routine returns pointer to DirIndex'es frame & index inside it
|
|
according to start Index parameter. It also initializes scan parameters
|
|
*/
|
|
PDIR_INDEX_ITEM
|
|
UDFDirIndexGetFrame(
|
|
IN PDIR_INDEX_HDR hDirNdx,
|
|
IN uint32 Frame,
|
|
OUT uint32* FrameLen,
|
|
OUT uint_di* Index,
|
|
IN uint_di Rel
|
|
)
|
|
{
|
|
if(Frame >= hDirNdx->FrameCount)
|
|
return NULL;
|
|
if(Index) {
|
|
#ifdef UDF_LIMIT_DIR_SIZE
|
|
(*Index) = Rel;
|
|
// if(FrameLen)
|
|
(*FrameLen) = hDirNdx->LastFrameCount;
|
|
#else //UDF_LIMIT_DIR_SIZE
|
|
(*Index) = Frame*UDF_DIR_INDEX_FRAME+Rel;
|
|
// if(FrameLen)
|
|
(*FrameLen) = (Frame < (hDirNdx->FrameCount-1)) ? UDF_DIR_INDEX_FRAME :
|
|
hDirNdx->LastFrameCount;
|
|
#endif //UDF_LIMIT_DIR_SIZE
|
|
}
|
|
return ((PDIR_INDEX_ITEM*)(hDirNdx+1))[Frame]+Rel;
|
|
} // end UDFDirIndexGetFrame()
|
|
|
|
/*
|
|
This routine initializes indexes for optimized DirIndex scan
|
|
according to start Index parameter
|
|
*/
|
|
|
|
BOOLEAN
|
|
UDFDirIndexInitScan(
|
|
IN PUDF_FILE_INFO DirInfo, //
|
|
OUT PUDF_DIR_SCAN_CONTEXT Context,
|
|
IN uint_di Index
|
|
)
|
|
{
|
|
Context->DirInfo = DirInfo;
|
|
Context->hDirNdx = DirInfo->Dloc->DirIndex;
|
|
if( (Context->frame = (Index >> UDF_DIR_INDEX_FRAME_SH)) >=
|
|
Context->hDirNdx->FrameCount) {
|
|
return FALSE;
|
|
}
|
|
if( (Context->j = Index & (UDF_DIR_INDEX_FRAME-1)) >=
|
|
((Context->frame < (Context->hDirNdx->FrameCount-1))
|
|
?
|
|
UDF_DIR_INDEX_FRAME : Context->hDirNdx->LastFrameCount) ) {
|
|
return FALSE;
|
|
}
|
|
Context->DirNdx = UDFDirIndexGetFrame(Context->hDirNdx,
|
|
Context->frame,
|
|
&(Context->d),
|
|
&(Context->i),
|
|
Context->j);
|
|
Context->i--;
|
|
Context->j--;
|
|
Context->DirNdx--;
|
|
|
|
return TRUE;
|
|
} // end UDFDirIndexInitScan()
|
|
|
|
PDIR_INDEX_ITEM
|
|
UDFDirIndexScan(
|
|
PUDF_DIR_SCAN_CONTEXT Context,
|
|
PUDF_FILE_INFO* _FileInfo
|
|
)
|
|
{
|
|
PUDF_FILE_INFO FileInfo;
|
|
PUDF_FILE_INFO ParFileInfo;
|
|
|
|
Context->i++;
|
|
Context->j++;
|
|
Context->DirNdx++;
|
|
|
|
if(Context->j >= Context->d) {
|
|
Context->j=0;
|
|
Context->frame++;
|
|
Context->DirNdx = UDFDirIndexGetFrame(Context->hDirNdx,
|
|
Context->frame,
|
|
&(Context->d),
|
|
&(Context->i),
|
|
Context->j);
|
|
}
|
|
if(!Context->DirNdx) {
|
|
if(_FileInfo)
|
|
(*_FileInfo) = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
if(_FileInfo) {
|
|
if((FileInfo = Context->DirNdx->FileInfo)) {
|
|
if(FileInfo->ParentFile != Context->DirInfo) {
|
|
ParFileInfo = UDFLocateParallelFI(Context->DirInfo,
|
|
Context->i,
|
|
FileInfo);
|
|
#ifdef UDF_DBG
|
|
if(ParFileInfo->ParentFile != Context->DirInfo) {
|
|
BrutePoint();
|
|
}
|
|
#endif // UDF_DBG
|
|
FileInfo = ParFileInfo;
|
|
}
|
|
}
|
|
(*_FileInfo) = FileInfo;
|
|
}
|
|
|
|
return (Context->DirNdx);
|
|
} // end UDFDirIndexScan()
|
|
|
|
/*
|
|
This routine calculates hashes for directory search
|
|
*/
|
|
uint8
|
|
UDFBuildHashEntry(
|
|
IN PVCB Vcb,
|
|
IN PUNICODE_STRING Name,
|
|
OUT PHASH_ENTRY hashes,
|
|
IN uint8 Mask
|
|
)
|
|
{
|
|
UNICODE_STRING UName;
|
|
WCHAR ShortNameBuffer[13];
|
|
uint8 RetFlags = 0;
|
|
|
|
if(!Name->Buffer) return 0;
|
|
|
|
if(Mask & HASH_POSIX)
|
|
hashes->hPosix = crc32((uint8*)(Name->Buffer), Name->Length);
|
|
|
|
if(Mask & HASH_ULFN) {
|
|
/* if(OS_SUCCESS(MyInitUnicodeString(&UName, L"")) &&
|
|
OS_SUCCESS(MyAppendUnicodeStringToStringTag(&UName, Name, MEM_USDIRHASH_TAG))) {*/
|
|
if(OS_SUCCESS(MyCloneUnicodeString(&UName, Name))) {
|
|
RtlUpcaseUnicodeString(&UName, &UName, FALSE);
|
|
/* if(!RtlCompareUnicodeString(Name, &UName, FALSE)) {
|
|
RetFlags |= UDF_FI_FLAG_LFN;
|
|
}*/
|
|
hashes->hLfn = crc32((uint8*)(UName.Buffer), UName.Length);
|
|
} else {
|
|
BrutePoint();
|
|
}
|
|
MyFreePool__(UName.Buffer);
|
|
}
|
|
|
|
if(Mask & HASH_DOS) {
|
|
UName.Buffer = (PWCHAR)(&ShortNameBuffer);
|
|
UName.MaximumLength = 13*sizeof(WCHAR);
|
|
UDFDOSName(Vcb, &UName, Name, (Mask & HASH_KEEP_NAME) ? TRUE : FALSE);
|
|
if(!RtlCompareUnicodeString(Name, &UName, TRUE)) {
|
|
RetFlags |= UDF_FI_FLAG_DOS;
|
|
}
|
|
hashes->hDos = crc32((uint8*)(UName.Buffer), UName.Length);
|
|
}
|
|
return RetFlags;
|
|
} // UDFBuildHashEntry()
|
|
|
|
#ifdef UDF_CHECK_UTIL
|
|
uint32
|
|
UDFFindNextFI(
|
|
IN int8* buff,
|
|
IN uint32 prevOffset,
|
|
IN uint32 Length
|
|
)
|
|
{
|
|
PFILE_IDENT_DESC FileId;
|
|
while(prevOffset+sizeof(FILE_IDENT_DESC) < Length) {
|
|
prevOffset++;
|
|
FileId = (PFILE_IDENT_DESC)(buff+prevOffset);
|
|
if(FileId->descTag.tagIdent != TID_FILE_IDENT_DESC)
|
|
continue;
|
|
if(FileId->descTag.descVersion != 2 && FileId->descTag.descVersion != 3)
|
|
continue;
|
|
if(FileId->fileVersionNum != 1)
|
|
continue;
|
|
if(FileId->fileCharacteristics & (~0x1f))
|
|
continue;
|
|
if(prevOffset + ((FileId->lengthFileIdent + FileId->lengthOfImpUse + sizeof(FILE_IDENT_DESC) + 3) & (~((uint32)3))) <= Length) {
|
|
UDFPrint(("UDFFindNextFI OK: %x\n", prevOffset));
|
|
return prevOffset;
|
|
}
|
|
}
|
|
return 0;
|
|
} // end UDFFindNextFI()
|
|
#else //UDF_CHECK_UTIL
|
|
#define UDFFindNextFI(a,b,c) 0
|
|
#endif //UDF_CHECK_UTIL
|
|
|
|
/*
|
|
This routine scans directory extent & builds index table for FileIdents
|
|
*/
|
|
OSSTATUS
|
|
UDFIndexDirectory(
|
|
IN PVCB Vcb,
|
|
IN OUT PUDF_FILE_INFO FileInfo
|
|
)
|
|
{
|
|
PDIR_INDEX_HDR hDirNdx;
|
|
PDIR_INDEX_ITEM DirNdx;
|
|
PFILE_IDENT_DESC FileId;
|
|
uint32 Offset = 0;
|
|
// uint32 prevOffset = 0;
|
|
uint_di Count = 0;
|
|
OSSTATUS status;
|
|
int8* buff;
|
|
PEXTENT_INFO ExtInfo; // Extent array for directory
|
|
uint16 PartNum;
|
|
SIZE_T ReadBytes;
|
|
uint16 valueCRC;
|
|
|
|
if(!FileInfo) return STATUS_INVALID_PARAMETER;
|
|
ValidateFileInfo(FileInfo);
|
|
|
|
ExtInfo = &(FileInfo->Dloc->DataLoc);
|
|
FileInfo->Dloc->DirIndex = NULL;
|
|
UDFPrint(("UDF: scaning directory\n"));
|
|
// allocate buffer for the whole directory
|
|
ASSERT((uint32)(ExtInfo->Length));
|
|
if(!ExtInfo->Length)
|
|
return STATUS_FILE_CORRUPT_ERROR;
|
|
buff = (int8*)DbgAllocatePool(PagedPool, (uint32)(ExtInfo->Length));
|
|
if(!buff)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
ExtInfo->Flags |= EXTENT_FLAG_ALLOC_SEQUENTIAL;
|
|
|
|
// read FileIdents
|
|
status = UDFReadExtent(Vcb, ExtInfo, 0, (uint32)(ExtInfo->Length), FALSE, buff, &ReadBytes);
|
|
if(!OS_SUCCESS(status)) {
|
|
DbgFreePool(buff);
|
|
return status;
|
|
}
|
|
// scan Dir to get entry counter
|
|
FileId = (PFILE_IDENT_DESC)buff;
|
|
DirPrint((" ExtInfo->Length %x\n", ExtInfo->Length));
|
|
// prevOffset = 0;
|
|
while(Offset<ExtInfo->Length) {
|
|
DirPrint((" Offset %x\n", Offset));
|
|
if(!FileId->descTag.tagIdent) {
|
|
DirPrint((" term item\n"));
|
|
break;
|
|
}
|
|
if(FileId->descTag.tagIdent != TID_FILE_IDENT_DESC) {
|
|
DirPrint((" Inv. tag %x\n", FileId->descTag.tagIdent));
|
|
Offset = UDFFindNextFI(buff, prevOffset, (ULONG)(ExtInfo->Length));
|
|
if(!Offset) {
|
|
DirPrint((" can't find next\n"));
|
|
break;
|
|
} else {
|
|
DirPrint((" found next offs %x\n", Offset));
|
|
FileId = (PFILE_IDENT_DESC)((buff)+Offset);
|
|
}
|
|
}
|
|
if(((ULONG)Offset & (Vcb->LBlockSize-1)) > (Vcb->LBlockSize-sizeof(FILE_IDENT_DESC))) {
|
|
DirPrint((" badly aligned\n", Offset));
|
|
if(Vcb->Modified) {
|
|
DirPrint((" queue repack request\n"));
|
|
FileInfo->Dloc->DirIndex->DelCount = Vcb->PackDirThreshold+1;
|
|
}
|
|
}
|
|
// prevOffset = Offset;
|
|
Offset += (FileId->lengthFileIdent + FileId->lengthOfImpUse + sizeof(FILE_IDENT_DESC) + 3) & (~((uint32)3));
|
|
FileId = (PFILE_IDENT_DESC)((buff)+Offset);
|
|
Count++;
|
|
if(Offset+sizeof(FILE_IDENT_DESC) > ExtInfo->Length) {
|
|
if(Offset != ExtInfo->Length) {
|
|
UDFPrint((" Trash at the end of Dir\n"));
|
|
}
|
|
// BrutePoint();
|
|
break;
|
|
}
|
|
}
|
|
DirPrint((" final Offset %x\n", Offset));
|
|
if(Offset > ExtInfo->Length) {
|
|
BrutePoint();
|
|
UDFPrint((" Unexpected end of Dir\n"));
|
|
DbgFreePool(buff);
|
|
return STATUS_FILE_CORRUPT_ERROR;
|
|
}
|
|
// allocate buffer for directory index & zero it
|
|
DirPrint((" Count %x\n", Count));
|
|
hDirNdx = UDFDirIndexAlloc(Count+1);
|
|
if(!hDirNdx) {
|
|
DbgFreePool(buff);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
Offset = Count = 0;
|
|
hDirNdx->DIFlags |= (ExtInfo->Offset ? UDF_DI_FLAG_INIT_IN_ICB : 0);
|
|
// add entry pointing to the directory itself
|
|
DirNdx = UDFDirIndex(hDirNdx,0);
|
|
ASSERT(FileInfo->Dloc->FELoc.Mapping[0].extLocation);
|
|
DirNdx->FileEntryLoc.partitionReferenceNum = PartNum =
|
|
(uint16)UDFGetPartNumByPhysLba(Vcb, FileInfo->Dloc->FELoc.Mapping[0].extLocation);
|
|
ASSERT(PartNum != -1);
|
|
DirNdx->FileEntryLoc.logicalBlockNum =
|
|
UDFPhysLbaToPart(Vcb, PartNum, FileInfo->Dloc->FELoc.Mapping[0].extLocation);
|
|
if(DirNdx->FileEntryLoc.logicalBlockNum == (ULONG)-1) {
|
|
DirPrint((" err: FileEntryLoc=-1\n"));
|
|
DbgFreePool(buff);
|
|
UDFDirIndexFree(hDirNdx);
|
|
return STATUS_FILE_CORRUPT_ERROR;
|
|
}
|
|
DirNdx->FileCharacteristics = (FileInfo->FileIdent) ?
|
|
FileInfo->FileIdent->fileCharacteristics :
|
|
FILE_DIRECTORY;
|
|
// DirNdx->Offset = 0;
|
|
// DirNdx->Length = 0;
|
|
RtlInitUnicodeString(&DirNdx->FName, L".");
|
|
DirNdx->FileInfo = FileInfo;
|
|
DirNdx->FI_Flags |= UDF_FI_FLAG_KEEP_NAME;
|
|
DirNdx->FI_Flags |= UDFBuildHashEntry(Vcb, &(DirNdx->FName), &(DirNdx->hashes),
|
|
HASH_ALL | HASH_KEEP_NAME);
|
|
Count++;
|
|
FileId = (PFILE_IDENT_DESC)buff;
|
|
status = STATUS_SUCCESS;
|
|
// prevOffset = 0;
|
|
while((Offset<ExtInfo->Length) && FileId->descTag.tagIdent) {
|
|
// add new entry to index list
|
|
if(FileId->descTag.tagIdent != TID_FILE_IDENT_DESC) {
|
|
UDFPrint((" Invalid tagIdent %x (expected %x) offst %x\n", FileId->descTag.tagIdent, TID_FILE_IDENT_DESC, Offset));
|
|
DirPrint((" FileId: filen %x, iulen %x, charact %x\n",
|
|
FileId->lengthFileIdent, FileId->lengthOfImpUse, FileId->fileCharacteristics));
|
|
DirPrint((" loc: @%x\n", UDFExtentOffsetToLba(Vcb, ExtInfo->Mapping, Offset, NULL, NULL, NULL, NULL)));
|
|
KdDump(FileId, sizeof(FileId->descTag));
|
|
Offset = UDFFindNextFI(buff, prevOffset, (ULONG)(ExtInfo->Length));
|
|
if(!Offset) {
|
|
DbgFreePool(buff);
|
|
UDFDirIndexFree(hDirNdx);
|
|
return STATUS_FILE_CORRUPT_ERROR;
|
|
} else {
|
|
DirPrint((" found next offs %x\n", Offset));
|
|
FileId = (PFILE_IDENT_DESC)((buff)+Offset);
|
|
}
|
|
}
|
|
DirNdx = UDFDirIndex(hDirNdx,Count);
|
|
// allocate buffer & fill it with decompressed unicode filename
|
|
if(FileId->fileCharacteristics & FILE_DELETED) {
|
|
DirPrint((" FILE_DELETED\n"));
|
|
hDirNdx->DelCount++;
|
|
}
|
|
DirPrint((" FileId: offs %x, filen %x, iulen %x\n", Offset, FileId->lengthFileIdent, FileId->lengthOfImpUse));
|
|
DirNdx->Length = (FileId->lengthFileIdent + FileId->lengthOfImpUse + sizeof(FILE_IDENT_DESC) + 3) & (~((uint32)3));
|
|
DirPrint((" DirNdx: Length %x, Charact %x\n", DirNdx->Length, FileId->fileCharacteristics));
|
|
if(FileId->fileCharacteristics & FILE_PARENT) {
|
|
DirPrint((" parent\n"));
|
|
// init 'parent' entry
|
|
// '..' points to Parent Object (if any),
|
|
// otherwise it points to the Dir itself
|
|
RtlInitUnicodeString(&DirNdx->FName, L"..");
|
|
DirNdx->FileInfo = (FileInfo->ParentFile) ?
|
|
FileInfo->ParentFile : FileInfo;
|
|
DirNdx->FI_Flags |= UDF_FI_FLAG_KEEP_NAME;
|
|
DirNdx->FI_Flags |= UDFBuildHashEntry(Vcb, &(DirNdx->FName), &(DirNdx->hashes), HASH_ALL | HASH_KEEP_NAME);
|
|
} else {
|
|
// init plain file/dir entry
|
|
ASSERT( (Offset+sizeof(FILE_IDENT_DESC)+FileId->lengthOfImpUse+FileId->lengthFileIdent) <=
|
|
ExtInfo->Length );
|
|
UDFDecompressUnicode(&(DirNdx->FName),
|
|
((uint8*)(FileId+1)) + (FileId->lengthOfImpUse),
|
|
FileId->lengthFileIdent,
|
|
&valueCRC);
|
|
UDFNormalizeFileName(&(DirNdx->FName), valueCRC);
|
|
DirNdx->FI_Flags |= UDFBuildHashEntry(Vcb, &(DirNdx->FName), &(DirNdx->hashes), HASH_ALL);
|
|
}
|
|
if((FileId->fileCharacteristics & FILE_METADATA)
|
|
||
|
|
!DirNdx->FName.Buffer
|
|
||
|
|
((DirNdx->FName.Length >= sizeof(UDF_RESERVED_NAME_HDR)-sizeof(WCHAR)) &&
|
|
(RtlCompareMemory(DirNdx->FName.Buffer, UDF_RESERVED_NAME_HDR, sizeof(UDF_RESERVED_NAME_HDR)-sizeof(WCHAR)) == sizeof(UDF_RESERVED_NAME_HDR)-sizeof(WCHAR)) )) {
|
|
DirPrint((" metadata\n"));
|
|
DirNdx->FI_Flags |= UDF_FI_FLAG_FI_INTERNAL;
|
|
}
|
|
#if 0
|
|
UDFPrint(("%ws\n", DirNdx->FName.Buffer));
|
|
#endif
|
|
DirPrint(("%ws\n", DirNdx->FName.Buffer));
|
|
// remember FileEntry location...
|
|
DirNdx->FileEntryLoc = FileId->icb.extLocation;
|
|
// ... and some file characteristics
|
|
DirNdx->FileCharacteristics = FileId->fileCharacteristics;
|
|
DirNdx->Offset = Offset;
|
|
#ifdef UDF_CHECK_DISK_ALLOCATION
|
|
if(!(FileId->fileCharacteristics & FILE_DELETED) &&
|
|
(UDFPartLbaToPhys(Vcb, &(DirNdx->FileEntryLoc)) != LBA_OUT_OF_EXTENT) &&
|
|
UDFGetFreeBit(((uint32*)(Vcb->FSBM_Bitmap)), UDFPartLbaToPhys(Vcb, &(DirNdx->FileEntryLoc)) )) {
|
|
|
|
AdPrint(("Ref to Discarded block %x\n",UDFPartLbaToPhys(Vcb, &(DirNdx->FileEntryLoc)) ));
|
|
BrutePoint();
|
|
FileId->fileCharacteristics |= FILE_DELETED;
|
|
} else
|
|
if(UDFPartLbaToPhys(Vcb, &(DirNdx->FileEntryLoc)) == LBA_OUT_OF_EXTENT) {
|
|
AdPrint(("Ref to Invalid block %x\n", UDFPartLbaToPhys(Vcb, &(DirNdx->FileEntryLoc)) ));
|
|
BrutePoint();
|
|
FileId->fileCharacteristics |= FILE_DELETED;
|
|
}
|
|
#endif // UDF_CHECK_DISK_ALLOCATION
|
|
// prevOffset = Offset;
|
|
Offset += DirNdx->Length;
|
|
FileId = (PFILE_IDENT_DESC)(((int8*)FileId)+DirNdx->Length);
|
|
Count++;
|
|
if(Offset+sizeof(FILE_IDENT_DESC) > ExtInfo->Length) {
|
|
if(Offset != ExtInfo->Length) {
|
|
UDFPrint((" Trash at the end of Dir (2)\n"));
|
|
}
|
|
// BrutePoint();
|
|
break;
|
|
}
|
|
} // while()
|
|
// we needn't writing terminator 'cause the buffer is already zero-filled
|
|
DbgFreePool(buff);
|
|
if(Count < 2) {
|
|
UDFDirIndexFree(hDirNdx);
|
|
UDFPrint((" Directory too short\n"));
|
|
return STATUS_FILE_CORRUPT_ERROR;
|
|
}
|
|
// store index
|
|
FileInfo->Dloc->DirIndex = hDirNdx;
|
|
return status;
|
|
} // end UDFIndexDirectory()
|
|
|
|
#ifndef UDF_READ_ONLY_BUILD
|
|
/*
|
|
This routine removes all DELETED entries from Dir & resizes it.
|
|
It must be called before closing, no files sould be opened.
|
|
*/
|
|
OSSTATUS
|
|
UDFPackDirectory__(
|
|
IN PVCB Vcb,
|
|
IN OUT PUDF_FILE_INFO FileInfo // source (opened)
|
|
)
|
|
{
|
|
#ifdef UDF_PACK_DIRS
|
|
uint32 d, LBS;
|
|
uint_di i, j;
|
|
uint32 IUl, FIl, l;
|
|
uint32 DataLocOffset;
|
|
uint32 Offset, curOffset;
|
|
int8* Buf;
|
|
OSSTATUS status;
|
|
SIZE_T ReadBytes;
|
|
int8* storedFI;
|
|
PUDF_FILE_INFO curFileInfo;
|
|
PDIR_INDEX_ITEM DirNdx, DirNdx2;
|
|
UDF_DIR_SCAN_CONTEXT ScanContext;
|
|
uint_di dc=0;
|
|
uint16 PartNum;
|
|
#endif //UDF_PACK_DIRS
|
|
|
|
ValidateFileInfo(FileInfo);
|
|
PDIR_INDEX_HDR hDirNdx = FileInfo->Dloc->DirIndex;
|
|
if(!hDirNdx) return STATUS_NOT_A_DIRECTORY;
|
|
#ifndef UDF_PACK_DIRS
|
|
return STATUS_SUCCESS;
|
|
#else // UDF_PACK_DIRS
|
|
|
|
// do not pack dirs on unchanged disks
|
|
if(!Vcb->Modified)
|
|
return STATUS_SUCCESS;
|
|
// start packing
|
|
LBS = Vcb->LBlockSize;
|
|
Buf = (int8*)DbgAllocatePool(PagedPool, LBS*2);
|
|
if(!Buf) return STATUS_INSUFFICIENT_RESOURCES;
|
|
// we shall never touch 1st entry 'cause it can't be deleted
|
|
Offset = UDFDirIndex(hDirNdx,2)->Offset;
|
|
DataLocOffset = FileInfo->Dloc->DataLoc.Offset;
|
|
|
|
i=j=2;
|
|
|
|
if(!UDFDirIndexInitScan(FileInfo, &ScanContext, i)) {
|
|
DbgFreePool(Buf);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
ASSERT(FileInfo->Dloc->FELoc.Mapping[0].extLocation);
|
|
PartNum = (uint16)UDFGetPartNumByPhysLba(Vcb, FileInfo->Dloc->FELoc.Mapping[0].extLocation);
|
|
ASSERT(PartNum != -1);
|
|
|
|
while((DirNdx = UDFDirIndexScan(&ScanContext, NULL))) {
|
|
|
|
if(UDFIsDeleted(DirNdx))
|
|
dc++;
|
|
|
|
if(!UDFIsDeleted(DirNdx) ||
|
|
DirNdx->FileInfo) {
|
|
// move down valid entry
|
|
status = UDFReadFile__(Vcb, FileInfo, curOffset = DirNdx->Offset,
|
|
l = DirNdx->Length, FALSE, Buf, &ReadBytes);
|
|
if(!OS_SUCCESS(status)) {
|
|
DbgFreePool(Buf);
|
|
return status;
|
|
}
|
|
// remove ImpUse field
|
|
IUl = ((PFILE_IDENT_DESC)Buf)->lengthOfImpUse;
|
|
curFileInfo = DirNdx->FileInfo;
|
|
// align next entry
|
|
if((d = LBS - ((curOffset + (l - IUl) + DataLocOffset) & (LBS-1)) ) < sizeof(FILE_IDENT_DESC)) {
|
|
|
|
// insufficient space at the end of last sector for
|
|
// next FileIdent's tag. fill it with ImpUse data
|
|
|
|
// generally, all data should be DWORD-aligned, but if it is not so
|
|
// this opearation will help us to avoid glitches
|
|
d = (d+3) & ~(3);
|
|
if(d != IUl) {
|
|
l = l + d - IUl;
|
|
FIl = ((PFILE_IDENT_DESC)Buf)->lengthFileIdent;
|
|
// copy filename to upper addr
|
|
RtlMoveMemory(Buf+sizeof(FILE_IDENT_DESC)+d,
|
|
Buf+sizeof(FILE_IDENT_DESC)+IUl, FIl);
|
|
RtlZeroMemory(Buf+sizeof(FILE_IDENT_DESC), d);
|
|
((PFILE_IDENT_DESC)Buf)->lengthOfImpUse = (uint16)d;
|
|
|
|
if(curFileInfo && curFileInfo->FileIdent) {
|
|
// update stored FI if any
|
|
if(!MyReallocPool__((int8*)(curFileInfo->FileIdent), l,
|
|
(int8**)&(curFileInfo->FileIdent), (l+IUl-d) )) {
|
|
DbgFreePool(Buf);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
storedFI = (int8*)(curFileInfo->FileIdent);
|
|
RtlMoveMemory(storedFI+sizeof(FILE_IDENT_DESC)+d,
|
|
storedFI+sizeof(FILE_IDENT_DESC)+IUl, FIl);
|
|
RtlZeroMemory(storedFI+sizeof(FILE_IDENT_DESC), d);
|
|
((PFILE_IDENT_DESC)storedFI)->lengthOfImpUse = (uint16)d;
|
|
FileInfo->Dloc->FELoc.Modified = TRUE;
|
|
FileInfo->Dloc->FE_Flags |= UDF_FE_FLAG_FE_MODIFIED;
|
|
}
|
|
}
|
|
} else {
|
|
d = 0;
|
|
}
|
|
// write modified to new addr
|
|
if((d != IUl) ||
|
|
(curOffset != Offset)) {
|
|
|
|
UDFSetUpTag(Vcb, (tag*)Buf, (uint16)l,
|
|
UDFPhysLbaToPart(Vcb, PartNum,
|
|
UDFExtentOffsetToLba(Vcb, FileInfo->Dloc->DataLoc.Mapping,
|
|
Offset, NULL, NULL, NULL, NULL)));
|
|
|
|
status = UDFWriteFile__(Vcb, FileInfo, Offset, l, FALSE, Buf, &ReadBytes);
|
|
if(!OS_SUCCESS(status)) {
|
|
DbgFreePool(Buf);
|
|
return status;
|
|
}
|
|
}
|
|
DirNdx2 = UDFDirIndex(hDirNdx, j);
|
|
*DirNdx2 = *DirNdx;
|
|
DirNdx2->Offset = Offset;
|
|
DirNdx2->Length = l;
|
|
if(curFileInfo) {
|
|
curFileInfo->Index = j;
|
|
DirNdx2->FI_Flags |= UDF_FI_FLAG_FI_MODIFIED;
|
|
}
|
|
Offset += l;
|
|
j++;
|
|
}
|
|
}
|
|
// resize DirIndex
|
|
DbgFreePool(Buf);
|
|
if(dc) {
|
|
if(!OS_SUCCESS(status = UDFDirIndexTrunc(&(FileInfo->Dloc->DirIndex), dc))) {
|
|
return status;
|
|
}
|
|
}
|
|
// terminator is set by UDFDirIndexTrunc()
|
|
FileInfo->Dloc->DirIndex->DelCount = 0;
|
|
ASSERT(FileInfo->Dloc->FELoc.Mapping[0].extLocation);
|
|
|
|
// now Offset points to EOF. Let's truncate directory
|
|
return UDFResizeFile__(Vcb, FileInfo, Offset);
|
|
#endif // UDF_PACK_DIRS
|
|
} // end UDFPackDirectory__()
|
|
|
|
/*
|
|
This routine rebuilds tags for all entries from Dir.
|
|
*/
|
|
OSSTATUS
|
|
UDFReTagDirectory(
|
|
IN PVCB Vcb,
|
|
IN OUT PUDF_FILE_INFO FileInfo // source (opened)
|
|
)
|
|
{
|
|
uint32 l;
|
|
uint32 Offset;
|
|
int8* Buf;
|
|
OSSTATUS status;
|
|
SIZE_T ReadBytes;
|
|
PUDF_FILE_INFO curFileInfo;
|
|
PDIR_INDEX_ITEM DirNdx;
|
|
UDF_DIR_SCAN_CONTEXT ScanContext;
|
|
uint16 PartNum;
|
|
|
|
ValidateFileInfo(FileInfo);
|
|
PDIR_INDEX_HDR hDirNdx = FileInfo->Dloc->DirIndex;
|
|
if(!hDirNdx) return STATUS_NOT_A_DIRECTORY;
|
|
|
|
// do not pack dirs on unchanged disks
|
|
if(!Vcb->Modified)
|
|
return STATUS_SUCCESS;
|
|
|
|
if( ((hDirNdx->DIFlags & UDF_DI_FLAG_INIT_IN_ICB) ? TRUE : FALSE) ==
|
|
((FileInfo->Dloc->DataLoc.Offset) ? TRUE : FALSE) ) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// start packing
|
|
Buf = (int8*)DbgAllocatePool(PagedPool, Vcb->LBlockSize*2);
|
|
if(!Buf) return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
Offset = UDFDirIndex(hDirNdx,1)->Offset;
|
|
|
|
if(!UDFDirIndexInitScan(FileInfo, &ScanContext, 1)) {
|
|
DbgFreePool(Buf);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
ASSERT(FileInfo->Dloc->FELoc.Mapping[0].extLocation);
|
|
PartNum = (uint16)UDFGetPartNumByPhysLba(Vcb, FileInfo->Dloc->FELoc.Mapping[0].extLocation);
|
|
ASSERT(PartNum != -1);
|
|
|
|
while((DirNdx = UDFDirIndexScan(&ScanContext, NULL))) {
|
|
|
|
status = UDFReadFile__(Vcb, FileInfo, Offset = DirNdx->Offset,
|
|
l = DirNdx->Length, FALSE, Buf, &ReadBytes);
|
|
if(!OS_SUCCESS(status)) {
|
|
DbgFreePool(Buf);
|
|
return status;
|
|
}
|
|
curFileInfo = DirNdx->FileInfo;
|
|
// write modified
|
|
UDFSetUpTag(Vcb, (tag*)Buf, (uint16)l,
|
|
UDFPhysLbaToPart(Vcb, PartNum,
|
|
UDFExtentOffsetToLba(Vcb, FileInfo->Dloc->DataLoc.Mapping,
|
|
Offset, NULL, NULL, NULL, NULL)));
|
|
|
|
if(curFileInfo && curFileInfo->FileIdent) {
|
|
FileInfo->Dloc->FELoc.Modified = TRUE;
|
|
FileInfo->Dloc->FE_Flags |= UDF_FE_FLAG_FE_MODIFIED;
|
|
}
|
|
|
|
status = UDFWriteFile__(Vcb, FileInfo, Offset, l, FALSE, Buf, &ReadBytes);
|
|
if(!OS_SUCCESS(status)) {
|
|
DbgFreePool(Buf);
|
|
return status;
|
|
}
|
|
if(curFileInfo) {
|
|
DirNdx->FI_Flags |= UDF_FI_FLAG_FI_MODIFIED;
|
|
}
|
|
}
|
|
// resize DirIndex
|
|
DbgFreePool(Buf);
|
|
|
|
hDirNdx->DIFlags &= ~UDF_DI_FLAG_INIT_IN_ICB;
|
|
hDirNdx->DIFlags |= (FileInfo->Dloc->DataLoc.Offset ? UDF_DI_FLAG_INIT_IN_ICB : 0);
|
|
return status;
|
|
|
|
} // end UDFReTagDirectory()
|
|
#endif //UDF_READ_ONLY_BUILD
|
|
|
|
/*
|
|
This routine performs search for specified file in specified directory &
|
|
returns corresponding offset in extent if found.
|
|
*/
|
|
OSSTATUS
|
|
UDFFindFile(
|
|
IN PVCB Vcb,
|
|
IN BOOLEAN IgnoreCase,
|
|
IN BOOLEAN NotDeleted,
|
|
IN PUNICODE_STRING Name,
|
|
IN PUDF_FILE_INFO DirInfo,
|
|
IN OUT uint_di* Index // IN:start index OUT:found file index
|
|
)
|
|
{
|
|
// PDIR_INDEX_HDR hDirIndex = DirInfo->Dloc->DirIndex;
|
|
UNICODE_STRING ShortName;
|
|
WCHAR ShortNameBuffer[13];
|
|
PDIR_INDEX_ITEM DirNdx;
|
|
UDF_DIR_SCAN_CONTEXT ScanContext;
|
|
uint_di j=(-1), k=(-1);
|
|
HASH_ENTRY hashes;
|
|
BOOLEAN CanBe8d3;
|
|
|
|
UDFBuildHashEntry(Vcb, Name, &hashes, HASH_POSIX | HASH_ULFN);
|
|
|
|
if((CanBe8d3 = UDFCanNameBeA8dot3(Name))) {
|
|
ShortName.MaximumLength = 13 * sizeof(WCHAR);
|
|
ShortName.Buffer = (PWCHAR)&ShortNameBuffer;
|
|
}
|
|
|
|
if(!UDFDirIndexInitScan(DirInfo, &ScanContext, (*Index)))
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
if(!IgnoreCase && !CanBe8d3) {
|
|
// perform case sensetive sequential directory scan
|
|
|
|
while((DirNdx = UDFDirIndexScan(&ScanContext, NULL))) {
|
|
if( (DirNdx->hashes.hPosix == hashes.hPosix) &&
|
|
DirNdx->FName.Buffer &&
|
|
(!RtlCompareUnicodeString(&(DirNdx->FName), Name, FALSE)) &&
|
|
( (!UDFIsDeleted(DirNdx)) || (!NotDeleted) ) ) {
|
|
(*Index) = ScanContext.i;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
|
|
if(hashes.hPosix == hashes.hLfn) {
|
|
|
|
while((DirNdx = UDFDirIndexScan(&ScanContext, NULL))) {
|
|
if(!DirNdx->FName.Buffer ||
|
|
(NotDeleted && UDFIsDeleted(DirNdx)) )
|
|
continue;
|
|
if( (DirNdx->hashes.hLfn == hashes.hLfn) &&
|
|
(!RtlCompareUnicodeString(&(DirNdx->FName), Name, IgnoreCase)) ) {
|
|
(*Index) = ScanContext.i;
|
|
return STATUS_SUCCESS;
|
|
} else
|
|
if( CanBe8d3 &&
|
|
!(DirNdx->FI_Flags & UDF_FI_FLAG_DOS) &&
|
|
(DirNdx->hashes.hDos == hashes.hLfn) &&
|
|
(k == (uint_di)(-1))) {
|
|
UDFDOSName(Vcb, &ShortName, &(DirNdx->FName), ScanContext.i < 2) ;
|
|
if(!RtlCompareUnicodeString(&ShortName, Name, IgnoreCase))
|
|
k = ScanContext.i;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
while((DirNdx = UDFDirIndexScan(&ScanContext, NULL))) {
|
|
// perform sequential directory scan
|
|
if(!DirNdx->FName.Buffer ||
|
|
(NotDeleted && UDFIsDeleted(DirNdx)) )
|
|
continue;
|
|
if( (DirNdx->hashes.hPosix == hashes.hPosix) &&
|
|
(!RtlCompareUnicodeString(&(DirNdx->FName), Name, FALSE)) ) {
|
|
(*Index) = ScanContext.i;
|
|
return STATUS_SUCCESS;
|
|
} else
|
|
if( (DirNdx->hashes.hLfn == hashes.hLfn) &&
|
|
(j == (uint_di)(-1)) &&
|
|
(!RtlCompareUnicodeString(&(DirNdx->FName), Name, IgnoreCase)) ) {
|
|
j = ScanContext.i;
|
|
} else
|
|
if( CanBe8d3 &&
|
|
!(DirNdx->FI_Flags & UDF_FI_FLAG_DOS) &&
|
|
(DirNdx->hashes.hDos == hashes.hLfn) &&
|
|
(k == (uint_di)(-1))) {
|
|
UDFDOSName(Vcb, &ShortName, &(DirNdx->FName), ScanContext.i < 2 );
|
|
if(!RtlCompareUnicodeString(&ShortName, Name, IgnoreCase)) {
|
|
k = ScanContext.i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(j != (uint_di)(-1)) {
|
|
(*Index) = j;
|
|
return STATUS_SUCCESS;
|
|
} else
|
|
if(k != (uint_di)(-1)) {
|
|
(*Index) = k;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
return STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
} // end UDFFindFile()
|
|
|
|
/*
|
|
This routine returns pointer to parent DirIndex
|
|
*/
|
|
PDIR_INDEX_HDR
|
|
UDFGetDirIndexByFileInfo(
|
|
IN PUDF_FILE_INFO FileInfo
|
|
)
|
|
{
|
|
ValidateFileInfo(FileInfo);
|
|
|
|
if(!FileInfo) {
|
|
BrutePoint();
|
|
return NULL;
|
|
}
|
|
if (FileInfo->ParentFile) {
|
|
ValidateFileInfo(FileInfo->ParentFile);
|
|
|
|
if(UDFIsAStreamDir(FileInfo))
|
|
return NULL;
|
|
if(FileInfo->ParentFile->Dloc)
|
|
return FileInfo->ParentFile->Dloc->DirIndex;
|
|
return NULL;
|
|
}
|
|
if(FileInfo->Dloc)
|
|
return FileInfo->Dloc->DirIndex;
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
File Data Location support routines (UDFXxxDloc)
|
|
This group is responsible for caching FE locations
|
|
If requested FE referenced by another FI the file is assumed to be linked
|
|
All linked files reference to common Data Location (& attr) structure
|
|
*/
|
|
|
|
/*
|
|
Check if given FE is already in use
|
|
*/
|
|
LONG
|
|
UDFFindDloc(
|
|
IN PVCB Vcb,
|
|
IN uint32 Lba
|
|
)
|
|
{
|
|
PUDF_DATALOC_INDEX DlocList;
|
|
uint32 l;
|
|
|
|
if(!(DlocList = Vcb->DlocList) || !Lba) return (-1);
|
|
// scan FE location cache
|
|
l = Vcb->DlocCount;
|
|
for(uint32 i=0; i<l; i++, DlocList++) {
|
|
if(DlocList->Lba == Lba)
|
|
return i;
|
|
}
|
|
return (-1);
|
|
} // end UDFFindDloc()
|
|
|
|
/*
|
|
Check if given FE is already stored in memory
|
|
*/
|
|
LONG
|
|
UDFFindDlocInMem(
|
|
IN PVCB Vcb,
|
|
IN PUDF_DATALOC_INFO Dloc
|
|
)
|
|
{
|
|
PUDF_DATALOC_INDEX DlocList;
|
|
uint32 l;
|
|
|
|
if(!(DlocList = Vcb->DlocList) || !Dloc) return (-1);
|
|
// scan FE location cache
|
|
l = Vcb->DlocCount;
|
|
for(uint32 i=0; i<l; i++, DlocList++) {
|
|
if(DlocList->Dloc == Dloc)
|
|
return i;
|
|
}
|
|
return (-1);
|
|
} // end UDFFindDlocInMem()
|
|
|
|
/*
|
|
Find free cache entry
|
|
*/
|
|
LONG
|
|
UDFFindFreeDloc(
|
|
IN PVCB Vcb,
|
|
IN uint32 Lba
|
|
)
|
|
{
|
|
PUDF_DATALOC_INDEX DlocList;
|
|
uint32 l;
|
|
|
|
if(!Vcb->DlocList) {
|
|
// init FE location cache
|
|
if(!(Vcb->DlocList = (PUDF_DATALOC_INDEX)MyAllocatePoolTag__(NonPagedPool, sizeof(UDF_DATALOC_INDEX)*DLOC_LIST_GRANULARITY, MEM_DLOC_NDX_TAG)))
|
|
return (-1);
|
|
RtlZeroMemory(Vcb->DlocList, DLOC_LIST_GRANULARITY*sizeof(UDF_DATALOC_INDEX));
|
|
Vcb->DlocCount = DLOC_LIST_GRANULARITY;
|
|
}
|
|
// scan for free entry
|
|
DlocList = Vcb->DlocList;
|
|
l = Vcb->DlocCount;
|
|
for(uint32 i=0; i<l; i++, DlocList++) {
|
|
if(!DlocList->Dloc)
|
|
return i;
|
|
}
|
|
// alloc some free entries
|
|
if(!MyReallocPool__((int8*)(Vcb->DlocList), Vcb->DlocCount*sizeof(UDF_DATALOC_INDEX),
|
|
(int8**)&(Vcb->DlocList), (Vcb->DlocCount+DLOC_LIST_GRANULARITY)*sizeof(UDF_DATALOC_INDEX))) {
|
|
return (-1);
|
|
}
|
|
RtlZeroMemory(&(Vcb->DlocList[Vcb->DlocCount]), DLOC_LIST_GRANULARITY*sizeof(UDF_DATALOC_INDEX));
|
|
Vcb->DlocCount += DLOC_LIST_GRANULARITY;
|
|
return (Vcb->DlocCount - DLOC_LIST_GRANULARITY);
|
|
} // end UDFFindFreeDloc()
|
|
|
|
/*
|
|
*/
|
|
OSSTATUS
|
|
UDFAcquireDloc(
|
|
IN PVCB Vcb,
|
|
IN PUDF_DATALOC_INFO Dloc
|
|
)
|
|
{
|
|
UDFAcquireResourceExclusive(&(Vcb->DlocResource2),TRUE);
|
|
if(Dloc->FE_Flags & UDF_FE_FLAG_UNDER_INIT) {
|
|
UDFReleaseResource(&(Vcb->DlocResource2));
|
|
return STATUS_SHARING_PAUSED;
|
|
}
|
|
Dloc->FE_Flags |= UDF_FE_FLAG_UNDER_INIT;
|
|
UDFReleaseResource(&(Vcb->DlocResource2));
|
|
return STATUS_SUCCESS;
|
|
} // end UDFAcquireDloc()
|
|
|
|
/*
|
|
*/
|
|
OSSTATUS
|
|
UDFReleaseDloc(
|
|
IN PVCB Vcb,
|
|
IN PUDF_DATALOC_INFO Dloc
|
|
)
|
|
{
|
|
UDFAcquireResourceExclusive(&(Vcb->DlocResource2),TRUE);
|
|
Dloc->FE_Flags &= ~UDF_FE_FLAG_UNDER_INIT;
|
|
UDFReleaseResource(&(Vcb->DlocResource2));
|
|
return STATUS_SUCCESS;
|
|
} // end UDFReleaseDloc()
|
|
|
|
/*
|
|
Try to store FE location in cache
|
|
If it is already in use, caller will be informed about it
|
|
*/
|
|
OSSTATUS
|
|
UDFStoreDloc(
|
|
IN PVCB Vcb,
|
|
IN PUDF_FILE_INFO fi,
|
|
IN uint32 Lba
|
|
)
|
|
{
|
|
LONG i;
|
|
PUDF_DATALOC_INFO Dloc;
|
|
|
|
if(!Lba) return STATUS_INVALID_PARAMETER;
|
|
if(Lba == (ULONG)-1) return STATUS_INVALID_PARAMETER;
|
|
|
|
UDFAcquireResourceExclusive(&(Vcb->DlocResource),TRUE);
|
|
|
|
// check if FE specified is already in use
|
|
if((i = UDFFindDloc(Vcb, Lba)) == (-1)) {
|
|
// not used
|
|
if((i = UDFFindFreeDloc(Vcb, Lba)) == (-1)) {
|
|
UDFReleaseResource(&(Vcb->DlocResource));
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
} else {
|
|
if(!OS_SUCCESS(UDFAcquireDloc(Vcb, Dloc = Vcb->DlocList[i].Dloc))) {
|
|
UDFReleaseResource(&(Vcb->DlocResource));
|
|
return STATUS_SHARING_PAUSED;
|
|
}
|
|
// update caller's structures & exit
|
|
fi->Dloc = Dloc;
|
|
UDFReleaseDloc(Vcb, Dloc);
|
|
#if defined UDF_DBG && !defined _CONSOLE
|
|
if(fi->Dloc->CommonFcb) {
|
|
ASSERT((uint32)(fi->Dloc->CommonFcb) != 0xDEADDA7A);
|
|
ASSERT(fi->Dloc->CommonFcb->CommonFCBHeader.NodeTypeCode == UDF_NODE_TYPE_NT_REQ_FCB);
|
|
}
|
|
#endif // UDF_DBG
|
|
UDFReleaseResource(&(Vcb->DlocResource));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
// allocate common DataLocation (Dloc) descriptor
|
|
Dloc = fi->Dloc = (PUDF_DATALOC_INFO)MyAllocatePoolTag__(UDF_DATALOC_INFO_MT, sizeof(UDF_DATALOC_INFO), MEM_DLOC_INF_TAG);
|
|
if(!Dloc) {
|
|
UDFReleaseResource(&(Vcb->DlocResource));
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
Vcb->DlocList[i].Lba = Lba;
|
|
Vcb->DlocList[i].Dloc = Dloc;
|
|
RtlZeroMemory(Dloc, sizeof(UDF_DATALOC_INFO));
|
|
Dloc->LinkedFileInfo = fi;
|
|
UDFAcquireDloc(Vcb, Dloc);
|
|
UDFReleaseResource(&(Vcb->DlocResource));
|
|
return STATUS_SUCCESS;
|
|
} // end UDFStoreDloc()
|
|
|
|
/*
|
|
Remove unreferenced FE location from cache & free allocated memory
|
|
This routine must be invoked when there are no more opened files
|
|
associated with given FE
|
|
*/
|
|
OSSTATUS
|
|
UDFRemoveDloc(
|
|
IN PVCB Vcb,
|
|
IN PUDF_DATALOC_INFO Dloc
|
|
)
|
|
{
|
|
LONG i;
|
|
|
|
UDFAcquireResourceExclusive(&(Vcb->DlocResource),TRUE);
|
|
|
|
if((i = UDFFindDlocInMem(Vcb, Dloc)) == (-1)) {
|
|
// FE specified is not in cache. exit
|
|
UDFReleaseResource(&(Vcb->DlocResource));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
// remove from cache
|
|
ASSERT(Vcb->DlocList);
|
|
RtlZeroMemory(&(Vcb->DlocList[i]), sizeof(UDF_DATALOC_INDEX));
|
|
UDFReleaseResource(&(Vcb->DlocResource));
|
|
MyFreePool__(Dloc);
|
|
return STATUS_SUCCESS;
|
|
} // end UDFRemoveDloc()
|
|
|
|
/*
|
|
Remove unlinked FE location from cache & keep allocated memory
|
|
This routine must be invoked when there are no more opened files
|
|
associated with given FE
|
|
*/
|
|
OSSTATUS
|
|
UDFUnlinkDloc(
|
|
IN PVCB Vcb,
|
|
IN PUDF_DATALOC_INFO Dloc
|
|
)
|
|
{
|
|
LONG i;
|
|
|
|
UDFAcquireResourceExclusive(&(Vcb->DlocResource),TRUE);
|
|
|
|
if((i = UDFFindDlocInMem(Vcb, Dloc)) == (-1)) {
|
|
// FE specified is not in cache. exit
|
|
UDFReleaseResource(&(Vcb->DlocResource));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
// remove from cache
|
|
ASSERT(Vcb->DlocList);
|
|
RtlZeroMemory(&(Vcb->DlocList[i]), sizeof(UDF_DATALOC_INDEX));
|
|
UDFReleaseResource(&(Vcb->DlocResource));
|
|
return STATUS_SUCCESS;
|
|
} // end UDFUnlinkDloc()
|
|
|
|
/*
|
|
This routine releases memory allocated for Dloc & removes it from
|
|
cache (if it is still there)
|
|
*/
|
|
void
|
|
UDFFreeDloc(
|
|
IN PVCB Vcb,
|
|
IN PUDF_DATALOC_INFO Dloc
|
|
)
|
|
{
|
|
LONG i;
|
|
|
|
UDFAcquireResourceExclusive(&(Vcb->DlocResource),TRUE);
|
|
|
|
if((i = UDFFindDlocInMem(Vcb, Dloc)) != (-1)) {
|
|
ASSERT(Vcb->DlocList);
|
|
RtlZeroMemory(&(Vcb->DlocList[i]), sizeof(UDF_DATALOC_INDEX));
|
|
}
|
|
UDFReleaseResource(&(Vcb->DlocResource));
|
|
MyFreePool__(Dloc);
|
|
} // end UDFFreeDloc()
|
|
|
|
/*
|
|
This routine updates Dloc LBA after relocation
|
|
*/
|
|
void
|
|
UDFRelocateDloc(
|
|
IN PVCB Vcb,
|
|
IN PUDF_DATALOC_INFO Dloc,
|
|
IN uint32 NewLba
|
|
)
|
|
{
|
|
LONG i;
|
|
|
|
UDFAcquireResourceExclusive(&(Vcb->DlocResource),TRUE);
|
|
|
|
if((i = UDFFindDlocInMem(Vcb, Dloc)) != (-1)) {
|
|
ASSERT(Vcb->DlocList);
|
|
Vcb->DlocList[i].Lba = NewLba;
|
|
}
|
|
UDFReleaseResource(&(Vcb->DlocResource));
|
|
|
|
} // end UDFRelocateDloc()
|
|
|
|
/*
|
|
Release FE cache
|
|
*/
|
|
void
|
|
UDFReleaseDlocList(
|
|
IN PVCB Vcb
|
|
)
|
|
{
|
|
if(!Vcb->DlocList) return;
|
|
UDFAcquireResourceExclusive(&(Vcb->DlocResource),TRUE);
|
|
for(uint32 i=0; i<Vcb->DlocCount; i++) {
|
|
if(Vcb->DlocList[i].Dloc)
|
|
MyFreePool__(Vcb->DlocList[i].Dloc);
|
|
}
|
|
MyFreePool__(Vcb->DlocList);
|
|
Vcb->DlocList = NULL;
|
|
Vcb->DlocCount = 0;
|
|
UDFReleaseResource(&(Vcb->DlocResource));
|
|
} // end UDFReleaseDlocList()
|
|
|
|
/*
|
|
This routine walks through Linked/Parallel FI chain and looks for
|
|
FE with same Index & Parent File
|
|
*/
|
|
PUDF_FILE_INFO
|
|
UDFLocateParallelFI(
|
|
PUDF_FILE_INFO di, // parent FileInfo
|
|
uint_di i, // Index
|
|
PUDF_FILE_INFO fi // FileInfo to start search from
|
|
)
|
|
{
|
|
PUDF_FILE_INFO ParFileInfo = fi->NextLinkedFile;
|
|
// PUDF_DATALOC_INFO Dloc = di->Dloc;
|
|
while((ParFileInfo != fi) &&
|
|
((ParFileInfo->ParentFile != di) ||
|
|
(ParFileInfo->Index != i)) ) {
|
|
ParFileInfo = ParFileInfo->NextLinkedFile;
|
|
}
|
|
return ParFileInfo;
|
|
// BrutePoint();
|
|
} // end UDFLocateParallelFI()
|
|
|
|
/*
|
|
This routine walks through Linked/Parallel FI chain and looks for
|
|
FE with same Index & Parent Dloc
|
|
*/
|
|
PUDF_FILE_INFO
|
|
UDFLocateAnyParallelFI(
|
|
PUDF_FILE_INFO fi // FileInfo to start search from
|
|
)
|
|
{
|
|
if(!fi->ParentFile) {
|
|
if(fi->NextLinkedFile == fi)
|
|
return NULL;
|
|
return fi->NextLinkedFile;
|
|
}
|
|
PUDF_FILE_INFO ParFileInfo = fi->NextLinkedFile;
|
|
PUDF_DATALOC_INFO Dloc = fi->ParentFile->Dloc;
|
|
uint_di i = fi->Index;
|
|
BOOLEAN NotFound = TRUE;
|
|
while((ParFileInfo != fi) &&
|
|
(NotFound =
|
|
((ParFileInfo->Index != i) ||
|
|
(ParFileInfo->ParentFile->Dloc != Dloc))) ) {
|
|
ParFileInfo = ParFileInfo->NextLinkedFile;
|
|
}
|
|
/* if(NotFound) {
|
|
if((ParFileInfo->Index == i) &&
|
|
(ParFileInfo->ParentFile->Dloc == Dloc))
|
|
return ParFileInfo;
|
|
return NULL;
|
|
}
|
|
return ParFileInfo;*/
|
|
return NotFound ? NULL : ParFileInfo;
|
|
// BrutePoint();
|
|
} // end UDFLocateAnyParallelFI()
|
|
|
|
void
|
|
UDFInsertLinkedFile(
|
|
PUDF_FILE_INFO fi, // FileInfo to be added to chain
|
|
PUDF_FILE_INFO fi2 // any FileInfo fro the chain
|
|
)
|
|
{
|
|
fi->NextLinkedFile = fi2->NextLinkedFile;
|
|
fi->PrevLinkedFile = fi2;
|
|
fi->NextLinkedFile->PrevLinkedFile =
|
|
fi->PrevLinkedFile->NextLinkedFile = fi;
|
|
return;
|
|
} // end UDFInsertLinkedFile()
|
|
|