Files
slimbootloader/BootloaderCommonPkg/Library/Ext23Lib/Ext2Fs.c
T
James Gutbub 2a28a0d3df Clean up EXT library
The EXT library has some unused code that
we can remove to help reduce the size and
to clean things up some more. Also add a
routine for dumping the group descriptor
table which can be helpful for debugging
issues.

Signed-off-by: James Gutbub <james.gutbub@intel.com>
2019-10-09 11:56:09 -07:00

1333 lines
35 KiB
C

/** @file
Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
Copyright (c) 1997 Manuel Bouyer.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
Copyright (c) 1993
The Regents of the University of California. All rights reserved.
This code is derived from software contributed to Berkeley by
The Mach Operating System project at Carnegie-Mellon University.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the Name of the University nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
Copyright (c) 1990, 1991 Carnegie Mellon University
All Rights Reserved.
Author: David Golub
Permission to use, copy, modify and distribute this software and its
documentation is hereby granted, provided that both the copyright
notice and this permission notice appear in all copies of the
software, derivative works or modified versions, and any portions
thereof, and that both notices appear in supporting documentation.
CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
Carnegie Mellon requests users of this software to return to
Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
School of Computer Science
Carnegie Mellon University
Pittsburgh PA 15213-3890
any improvements or extensions that they make and grant Carnegie the
rights to redistribute these changes.
Stand-alone FILE reading package for Ext2 FILE system.
**/
#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/MediaAccessLib.h>
#include "Ext2Fs.h"
#include "LibsaFsStand.h"
#define HOWMANY(x, y) (((x)+((y)-1))/(y))
#if defined(LIBSA_FS_SINGLECOMPONENT) && !defined(LIBSA_NO_FS_SYMLINK)
#define LIBSA_NO_FS_SYMLINK
#endif
/**
Read a new inode into a FILE structure.
@param [in] INumber inode number
@param [in] File pointer to open file struct.
@retval
**/
STATIC
INT32
ReadInode (
IN INODE32 INumber,
IN OPEN_FILE *File
);
/**
Given an offset in a FILE, find the disk block number that
contains that block.
@param File pointer to an Open file.
@param FileBlock Block to find the file.
@param DiskBlockPtr Pointer to the disk which contains block.
@retval 0 if success
@retval other if error.
**/
STATIC
INT32
BlockMap (
OPEN_FILE *File,
INDPTR FileBlock,
INDPTR *DiskBlockPtr
);
/**
Read a portion of a FILE into an internal buffer.
Return the location in the buffer and the amount in the buffer.
@param File Pointer to the open file.
@param BufferPtr buffer corresponding to offset
@param SizePtr Size of remainder of buffer.
**/
STATIC
INT32
BufReadFile (
OPEN_FILE *File,
CHAR8 **BufferPtr,
UINT32 *SizePtr
);
/**
Search a directory for a Name and return its
inode number.
@param Name Name to compare with
@param Length Length of the dir name
@param File Pointer to file private data
@param INumPtr pointer to Inode number.
**/
STATIC
INT32
SearchDirectory (
CHAR8 *Name,
INT32 Length,
OPEN_FILE *File,
INODE32 *INumPtr
);
/**
Gives the info of device block config.
@param DevData Device privete data.
@param ReadWrite Read write
@param BlockNum Block number to start
@param Size Size to read block.
@param Buf Buffer to read the block data.
@param RSize
@retval EFI_SUCCESS The function completed successfully.
@retval !EFI_SUCCESS Something error while read FILE.
**/
INT32
BDevStrategy (
VOID *DevData,
INT32 ReadWrite,
DADDRESS BlockNum,
UINT32 Size,
VOID *Buf,
UINT32 *RSize
)
{
INT32 Res;
PEI_EXT_PRIVATE_DATA *PrivateData;
UINT64 Startblockno;
PrivateData = DevData;
if (ReadWrite != F_READ && ReadWrite != F_WRITE) {
return EFI_INVALID_PARAMETER;
}
if ((Size % PrivateData->BlockSize) != 0) {
return EFI_INVALID_PARAMETER;
}
Startblockno = BlockNum + PrivateData->StartBlock;
if (ReadWrite == F_READ) {
Res = MediaReadBlocks (PrivateData->PhysicalDevNo, (UINT32)Startblockno, Size, Buf);
if (Res != 0) {
return Res;
}
}
*RSize = Size;
return 0;
}
/**
Read a new inode into a FILE structure.
@param [in] INumber inode number
@param [in] File pointer to open file struct.
@retval
**/
STATIC
INT32
ReadInode (
IN INODE32 INumber,
IN OPEN_FILE *File
)
{
FILE *Fp;
M_EXT2FS *FileSystem;
CHAR8 *Buf;
UINT32 RSize;
INT32 Rc;
DADDRESS InodeSector;
EXT2GD *Ext2FsGrpDes;
EXTFS_DINODE *DInodePtr;
Fp = (FILE *)File->FileSystemSpecificData;
FileSystem = Fp->SuperBlockPtr;
Ext2FsGrpDes = FileSystem->Ext2FsGrpDes;
Ext2FsGrpDes = (EXT2GD*)((UINT32)Ext2FsGrpDes + (INOTOCG(FileSystem, INumber) * FileSystem->Ext2FsGDSize));
InodeSector = (DADDRESS) (Ext2FsGrpDes->Ext2BGDInodeTables + DivU64x32 (ModU64x32 ((INumber - 1), FileSystem->Ext2Fs.Ext2FsINodesPerGroup), FileSystem->Ext2FsInodesPerBlock));
if (FileSystem->Ext2FsGDSize > 32) {
if (Ext2FsGrpDes->Ext2BGDInodeTablesHi !=0) {
InodeSector |= LShiftU64 ((UINT64) (Ext2FsGrpDes->Ext2BGDInodeTablesHi), 32);
}
}
InodeSector = FSBTODB (FileSystem, InodeSector);
//
// Read inode and save it.
//
Buf = Fp->Buffer;
Rc = DEV_STRATEGY (File->DevPtr) (File->FileDevData, F_READ,
InodeSector, FileSystem->Ext2FsBlockSize, Buf, &RSize);
if (Rc != 0) {
return Rc;
}
if (RSize != (UINT32)FileSystem->Ext2FsBlockSize) {
return EFI_DEVICE_ERROR;
}
DInodePtr = (EXTFS_DINODE *) (Buf +
EXT2_DINODE_SIZE (FileSystem) * INODETOFSBO (FileSystem, INumber));
E2FSILOAD (DInodePtr, &Fp->DiskInode);
//
// Clear out the Old buffers
//
Fp->InodeCacheBlock = ~0;
Fp->BufferBlockNum = -1;
return Rc;
}
/**
Given an offset in a FILE, find the disk block number that
contains that block.
@param File pointer to an Open file.
@param FileBlock Block to find the file.
@param DiskBlockPtr Pointer to the disk which contains block.
@retval 0 if success
@retval other if error.
**/
STATIC
INT32
BlockMap (
OPEN_FILE *File,
INDPTR FileBlock,
INDPTR *DiskBlockPtr
)
{
FILE *Fp;
M_EXT2FS *FileSystem;
UINT32 Level;
INDPTR IndCache;
INDPTR IndBlockNum;
UINT32 RSize;
INT32 Rc;
INDPTR *Buf;
UINT32 Index;
UINT64 NextLevelNode;
EXT4_EXTENT_TABLE *Etable;
EXT4_EXTENT_INDEX *ExtIndex;
EXT4_EXTENT *Extent;
Fp = (FILE *)File->FileSystemSpecificData;
FileSystem = Fp->SuperBlockPtr;
Buf = (VOID *)Fp->Buffer;
if ((Fp->DiskInode.Ext2DInodeStatusFlags & EXT4_EXTENTS) != 0) {
Etable = (EXT4_EXTENT_TABLE*) &(Fp->DiskInode.Ext2DInodeBlocks);
if (Etable->Eheader.EhMagic != EXT4_EXTENT_HEADER_MAGIC) {
DEBUG ((DEBUG_ERROR, "EXT4 extent header magic mismatch 0x%X!\n", Etable->Eheader.EhMagic));
return EFI_DEVICE_ERROR;
}
while (Etable->Eheader.EhDepth > 0) {
ExtIndex = NULL;
for (Index=1; Index < Etable->Eheader.EhEntries; Index++) {
ExtIndex = &(Etable->Enodes.Eindex[Index]);
if (((UINT32) FileBlock) < ExtIndex->EiBlk) {
ExtIndex = &(Etable->Enodes.Eindex[Index-1]);
break;
}
ExtIndex = NULL;
}
if (ExtIndex != NULL) {
//
// TODO: Need to support 48-bit block device addressing.
// Throw an ASSERT if upper 16-bits are non-zero.
//
ASSERT (ExtIndex->EiLeafHi == 0);
NextLevelNode = ExtIndex->EiLeafLo; //LShiftU64((UINT64)ExtIndex->EiLeafHi, 32) | ExtIndex->EiLeafLo;
//
// We need to read the next level node of the extent tree since the data was not in the current level.
//
Rc = DEV_STRATEGY (File->DevPtr) (File->FileDevData, F_READ,
FSBTODB (Fp->SuperBlockPtr, (DADDRESS) NextLevelNode), FileSystem->Ext2FsBlockSize,
Buf, &RSize);
if (Rc != 0) {
return Rc;
}
if (RSize != (UINT32)FileSystem->Ext2FsBlockSize) {
return EFI_DEVICE_ERROR;
}
Etable = (EXT4_EXTENT_TABLE*) Buf;
if (Etable->Eheader.EhMagic != EXT4_EXTENT_HEADER_MAGIC) {
DEBUG ((DEBUG_ERROR, "EXT4 extent header magic mismatch 0x%X!\n", Etable->Eheader.EhMagic));
return EFI_DEVICE_ERROR;
}
} else {
DEBUG ((DEBUG_ERROR, "Could not find FileBlock #%d in the index extent data!\n", FileBlock));
return EFI_NO_MAPPING;
}
}
Extent = NULL;
for (Index=0; Index < Etable->Eheader.EhEntries; Index++) {
Extent = &(Etable->Enodes.Extent[Index]);
if ((((UINT32) FileBlock) >= Extent->Eblk) && (((UINT32) FileBlock) < (Extent->Eblk + Extent->Elen))) {
break;
}
Extent = NULL;
}
if (Extent != NULL) {
//
// TODO: Need to support 48-bit block device addressing
// Throw an ASSERT if upper 16-bits are non-zero.
//
ASSERT (Extent->EstartHi == 0);
*DiskBlockPtr = Extent->EstartLo + (FileBlock - Extent->Eblk); // (LShiftU64((UINT64)Extent->EiLeafHi, 32) | Extent->EstartLo) + (FileBlock - Extent->Eblk);
} else {
*DiskBlockPtr = 0;
}
} else {
if (FileBlock < NDADDR) {
//
// Direct block.
//
*DiskBlockPtr = Fp->DiskInode.Ext2DInodeBlocks[FileBlock];
return 0;
}
FileBlock -= NDADDR;
IndCache = FileBlock >> LN2_IND_CACHE_SZ;
if (IndCache == Fp->InodeCacheBlock) {
*DiskBlockPtr =
Fp->InodeCache[FileBlock & IND_CACHE_MASK];
return 0;
}
for (Level = 0;;) {
Level += Fp->NiShift;
if (FileBlock < (INDPTR)1 << Level) {
break;
}
if (Level > NIADDR * Fp->NiShift)
//
// Block number too high
//
{
return EFI_OUT_OF_RESOURCES;
}
FileBlock -= (INDPTR)1 << Level;
}
IndBlockNum =
Fp->DiskInode.Ext2DInodeBlocks[NDADDR + (Level / Fp->NiShift - 1)];
while (1) {
Level -= Fp->NiShift;
if (IndBlockNum == 0) {
*DiskBlockPtr = 0; // missing
return 0;
}
//
// If we were feeling brave, we could work out the number
// of the disk sector and read a single disk sector instead
// of a filesystem block.
// However we don't do this very often anyway...
//
Rc = DEV_STRATEGY (File->DevPtr) (File->FileDevData, F_READ,
FSBTODB (Fp->SuperBlockPtr, IndBlockNum), FileSystem->Ext2FsBlockSize,
Buf, &RSize);
if (Rc != 0) {
return Rc;
}
if (RSize != (UINT32)FileSystem->Ext2FsBlockSize) {
return EFI_DEVICE_ERROR;
}
IndBlockNum = Buf[FileBlock >> Level];
if (Level == 0) {
break;
}
FileBlock &= (1 << Level) - 1;
}
//
// Save the part of the block that contains this sector
//
CopyMem (Fp->InodeCache, &Buf[FileBlock & ~IND_CACHE_MASK],
IND_CACHE_SZ * sizeof Fp->InodeCache[0]);
Fp->InodeCacheBlock = IndCache;
*DiskBlockPtr = IndBlockNum;
}
return 0;
}
/**
Read a portion of a FILE into an internal buffer.
Return the location in the buffer and the amount in the buffer.
@param File Pointer to the open file.
@param BufferPtr buffer corresponding to offset
@param SizePtr Size of remainder of buffer.
**/
STATIC
INT32
BufReadFile (
OPEN_FILE *File,
CHAR8 **BufferPtr,
UINT32 *SizePtr
)
{
FILE *Fp;
M_EXT2FS *FileSystem;
INT32 Off;
INDPTR FileBlock;
INDPTR DiskBlock;
UINT32 BlockSize;
INT32 Rc;
Fp = (FILE *)File->FileSystemSpecificData;
FileSystem = Fp->SuperBlockPtr;
DiskBlock = 0;
Off = BLOCKOFFSET (FileSystem, Fp->SeekPtr);
FileBlock = LBLKNO (FileSystem, Fp->SeekPtr);
BlockSize = FileSystem->Ext2FsBlockSize; // no fragment
if (FileBlock != Fp->BufferBlockNum) {
Rc = BlockMap (File, FileBlock, &DiskBlock);
if (Rc != 0) {
return Rc;
}
if (DiskBlock == 0) {
SetMem32 (Fp->Buffer, BlockSize, 0);
Fp->BufferSize = BlockSize;
} else {
Rc = DEV_STRATEGY (File->DevPtr) (File->FileDevData, F_READ,
FSBTODB (FileSystem, DiskBlock),
BlockSize, Fp->Buffer, &Fp->BufferSize);
if (Rc != 0) {
return Rc;
}
}
Fp->BufferBlockNum = FileBlock;
}
//
// Return address of byte in buffer corresponding to
// offset, and Size of remainder of buffer after that
// byte.
//
*BufferPtr = Fp->Buffer + Off;
*SizePtr = BlockSize - Off;
//
// But truncate buffer at end of FILE.
//
// XXX should handle LARGEFILE
//
if (*SizePtr > Fp->DiskInode.Ext2DInodeSize - Fp->SeekPtr) {
*SizePtr = Fp->DiskInode.Ext2DInodeSize - Fp->SeekPtr;
}
return 0;
}
/**
Search a directory for a Name and return its
inode number.
@param Name Name to compare with
@param Length Length of the dir name
@param File Pointer to file private data
@param INumPtr pointer to Inode number.
**/
STATIC
INT32
SearchDirectory (
CHAR8 *Name,
INT32 Length,
OPEN_FILE *File,
INODE32 *INumPtr
)
{
FILE *Fp;
EXT2FS_DIRECT *Dp;
EXT2FS_DIRECT *EdPtr;
CHAR8 *Buf;
UINT32 BufSize;
INT32 NameLen;
INT32 Rc;
Fp = (FILE *)File->FileSystemSpecificData;
Fp->SeekPtr = 0;
//
// XXX should handle LARGEFILE
//
while (Fp->SeekPtr < (OFFSET)Fp->DiskInode.Ext2DInodeSize) {
Rc = BufReadFile (File, &Buf, &BufSize);
if (Rc != 0) {
return Rc;
}
Dp = (EXT2FS_DIRECT *)Buf;
EdPtr = (EXT2FS_DIRECT *) (Buf + BufSize);
for (; Dp < EdPtr;
Dp = (VOID *) ((CHAR8 *)Dp + Dp->Ext2DirectRecLen)) {
if (Dp->Ext2DirectRecLen <= 0) {
break;
}
if (Dp->Ext2DirectInodeNumber == (INODE32)0) {
continue;
}
NameLen = Dp->Ext2DirectNameLen;
if (NameLen == Length &&
!CompareMem (Name, Dp->Ext2DirectName, Length)) {
//
// found entry
//
*INumPtr = Dp->Ext2DirectInodeNumber;
return 0;
}
}
Fp->SeekPtr += BufSize;
}
return EFI_UNSUPPORTED;
}
/**
Read Superblock of the file.
@param File File for which super block needs to be read.
@param FileSystem Fs on which super block is computed.
@retval 0 if superblock compute is success
@retval other if error.
**/
INT32
ReadSBlock (
OPEN_FILE *File,
M_EXT2FS *FileSystem
)
{
PEI_EXT_PRIVATE_DATA *PrivateData;
UINT8 *Buffer;
EXT2FS Ext2Fs;
UINT32 BufSize;
INT32 Rc;
UINT32 SbOffset;
Rc = 0;
Buffer = NULL;
PrivateData = (PEI_EXT_PRIVATE_DATA*) File->FileDevData;
Buffer = AllocatePool ((PrivateData->BlockSize > SBSIZE) ? PrivateData->BlockSize : SBSIZE);
if (Buffer == NULL) {
Rc = EFI_OUT_OF_RESOURCES;
goto Exit;
}
Rc = DEV_STRATEGY (File->DevPtr) (File->FileDevData, F_READ,
SBOFF / PrivateData->BlockSize, PrivateData->BlockSize, Buffer, &BufSize);
if (Rc != 0) {
goto Exit;
}
SbOffset = (SBOFF < PrivateData->BlockSize) ? SBOFF : 0;
E2FS_SBLOAD ((VOID *)(&Buffer[SbOffset]), &Ext2Fs);
if (Ext2Fs.Ext2FsMagic != E2FS_MAGIC) {
Rc = EFI_INVALID_PARAMETER;
goto Exit;
}
if (Ext2Fs.Ext2FsRev > E2FS_REV1 ||
(Ext2Fs.Ext2FsRev == E2FS_REV1 &&
(Ext2Fs.Ext2FsFirstInode != EXT2_FIRSTINO ||
(Ext2Fs.Ext2FsInodeSize != 128 && Ext2Fs.Ext2FsInodeSize != 256) ||
Ext2Fs.Ext2FsFeaturesIncompat & ~EXT2F_INCOMPAT_SUPP))) {
Rc = EFI_UNSUPPORTED;
goto Exit;
}
E2FS_SBLOAD ((VOID *)&Ext2Fs, &FileSystem->Ext2Fs);
//
// compute in-memory m_ext2fs values
//
FileSystem->Ext2FsNumCylinder =
HOWMANY (FileSystem->Ext2Fs.Ext2FsBlockCount - FileSystem->Ext2Fs.Ext2FsFirstDataBlock,
FileSystem->Ext2Fs.Ext2FsBlocksPerGroup);
FileSystem->Ext2FsFsbtobd = (FileSystem->Ext2Fs.Ext2FsLogBlockSize + 10) - HighBitSet32 (PrivateData->BlockSize);
FileSystem->Ext2FsBlockSize = MINBSIZE << FileSystem->Ext2Fs.Ext2FsLogBlockSize;
FileSystem->Ext2FsLogicalBlock = LOG_MINBSIZE + FileSystem->Ext2Fs.Ext2FsLogBlockSize;
FileSystem->Ext2FsQuadBlockOffset = FileSystem->Ext2FsBlockSize - 1;
FileSystem->Ext2FsBlockOffset = (UINT32)~FileSystem->Ext2FsQuadBlockOffset;
FileSystem->Ext2FsGDSize = 32;
if (Ext2Fs.Ext2FsFeaturesIncompat & EXT2F_INCOMPAT_64BIT) {
FileSystem->Ext2FsGDSize = FileSystem->Ext2Fs.Ext2FsGDSize;
}
FileSystem->Ext2FsNumGrpDesBlock =
HOWMANY (FileSystem->Ext2FsNumCylinder, FileSystem->Ext2FsBlockSize / FileSystem->Ext2FsGDSize);
FileSystem->Ext2FsInodesPerBlock = FileSystem->Ext2FsBlockSize / Ext2Fs.Ext2FsInodeSize;
FileSystem->Ext2FsInodesTablePerGrp = FileSystem->Ext2Fs.Ext2FsINodesPerGroup / FileSystem->Ext2FsInodesPerBlock;
Exit:
if (Buffer != NULL) {
FreePool (Buffer);
}
return Rc;
}
/**
Read group descriptor of the file.
@param File File for which group descriptor needs to be read.
@param FileSystem Fs on which super block is computed.
@retval 0 if Group descriptor read is success
@retval other if error.
**/
INT32
ReadGDBlock (
OPEN_FILE *File,
M_EXT2FS *FileSystem
)
{
FILE *Fp;
UINT32 RSize;
UINT32 gdpb;
INT32 Index, Rc;
Fp = (FILE *)File->FileSystemSpecificData;
gdpb = FileSystem->Ext2FsBlockSize / FileSystem->Ext2FsGDSize;
for (Index = 0; Index < FileSystem->Ext2FsNumGrpDesBlock; Index++) {
Rc = DEV_STRATEGY (File->DevPtr) (File->FileDevData, F_READ,
FSBTODB (FileSystem, FileSystem->Ext2Fs.Ext2FsFirstDataBlock +
1 /* superblock */ + Index),
FileSystem->Ext2FsBlockSize, Fp->Buffer, &RSize);
if (Rc != 0) {
return Rc;
}
if (RSize != (UINT32)FileSystem->Ext2FsBlockSize) {
return EFI_DEVICE_ERROR;
}
E2FS_CGLOAD ((EXT2GD *)Fp->Buffer,
&FileSystem->Ext2FsGrpDes[Index * gdpb],
(Index == (FileSystem->Ext2FsNumGrpDesBlock - 1)) ?
(FileSystem->Ext2FsNumCylinder - gdpb * Index) * FileSystem->Ext2FsGDSize :
FileSystem->Ext2FsBlockSize);
}
return 0;
}
/**
Open struct file.
@param Path path to locate the file
@param File The struct having the device and file info
@retval 0 if Group descriptor read is success
@retval other if error.
**/
INT32
Ext2fsOpen (
CHAR8 *Path,
OPEN_FILE *File
)
{
#ifndef LIBSA_FS_SINGLECOMPONENT
CHAR8 *Cp;
CHAR8 *Ncp;
INT32 Component;
#endif
INODE32 INumber;
FILE *Fp;
M_EXT2FS *FileSystem;
INT32 Rc;
#ifndef LIBSA_NO_FS_SYMLINK
INODE32 ParentInumber;
INT32 Nlinks;
CHAR8 NameBuf[MAXPATHLEN + 1];
CHAR8 *Buf;
Nlinks = 0;
#endif
INDPTR mult;
INT32 Length2;
//
// allocate struct file system specific data structure
//
Fp = AllocatePool (sizeof (FILE));
SetMem32 (Fp, sizeof (FILE), 0 );
File->FileSystemSpecificData = (VOID *)Fp;
//
// allocate space and read super block
//
FileSystem = AllocatePool (sizeof (*FileSystem));
SetMem32 (FileSystem, sizeof (*FileSystem), 0);
Fp->SuperBlockPtr = FileSystem;
Rc = ReadSBlock (File, FileSystem);
if (Rc != 0) {
goto out;
}
#ifdef EXT2FS_DEBUG
DumpSBlock (FileSystem);
#endif
//
// alloc a block sized buffer used for all FileSystem transfers
//
Fp->Buffer = AllocatePool (FileSystem->Ext2FsBlockSize);
//
// read group descriptor blocks
//
FileSystem->Ext2FsGrpDes = AllocatePool (FileSystem->Ext2FsGDSize * FileSystem->Ext2FsNumCylinder);
Rc = ReadGDBlock (File, FileSystem);
if (Rc != 0) {
goto out;
}
#ifdef EXT2FS_DEBUG
DumpGroupDesBlock (FileSystem);
#endif
//
// Calculate indirect block levels.
//
//
// We note that the number of indirect blocks is always
// a power of 2. This lets us use shifts and masks instead
// of divide and remainder and avoinds pulling in the
// 64bit division routine into the boot code.
//
mult = NINDIR (FileSystem);
for (Length2 = 0; mult != 1; Length2++) {
mult >>= 1;
}
Fp->NiShift = Length2;
INumber = EXT2_ROOTINO;
if ((Rc = ReadInode (INumber, File)) != 0) {
goto out;
}
#ifndef LIBSA_FS_SINGLECOMPONENT
Cp = Path;
while (*Cp != '\0') {
//
// Remove extra separators
//
while (*Cp == '/') {
Cp++;
}
if (*Cp == '\0') {
break;
}
//
// Check that current node is a directory.
//
if ((Fp->DiskInode.Ext2DInodeMode & EXT2_IFMT) != EXT2_IFDIR) {
Rc = EFI_LOAD_ERROR;
goto out;
}
//
// Get next component of Path Name.
//
Ncp = Cp;
while ((Component = *Cp) != '\0' && Component != '/') {
Cp++;
}
//
// Look up component in current directory.
// Save directory INumber in case we find a
// symbolic link.
//
#ifndef LIBSA_NO_FS_SYMLINK
ParentInumber = INumber;
#endif
Rc = SearchDirectory (Ncp, Cp - Ncp, File, &INumber);
if (Rc != 0) {
goto out;
}
//
// Open next component.
//
if ((Rc = ReadInode (INumber, File)) != 0) {
goto out;
}
#ifndef LIBSA_NO_FS_SYMLINK
//
// Check for symbolic link.
//
if ((Fp->DiskInode.Ext2DInodeMode & EXT2_IFMT) == EXT2_IFLNK) {
//
// XXX should handle LARGEFILE
//
INT32 LinkLength;
INT32 Len;
LinkLength = Fp->DiskInode.Ext2DInodeSize;
Len = AsciiStrLen (Cp);
if (LinkLength + Len > MAXPATHLEN ||
++Nlinks > MAXSYMLINKS) {
Rc = ENOENT;
goto out;
}
memmove (&NameBuf[LinkLength], Cp, Len + 1);
if (LinkLength < EXT2_MAXSYMLINKLEN) {
CopyMem (NameBuf, Fp->DiskInode.Ext2DInodeBlocks, LinkLength);
} else {
//
// Read FILE for symbolic link
//
UINT32 BufSize;
INDPTR DiskBlock;
Buf = Fp->Buffer;
Rc = BlockMap (File, (INDPTR)0, &DiskBlock);
if (Rc != 0) {
goto out;
}
Rc = DEV_STRATEGY (File->DevPtr) (File->FileDevData,
F_READ, FSBTODB (FileSystem, DiskBlock),
FileSystem->Ext2FsBlockSize, Buf, &BufSize);
if (Rc != 0) {
goto out;
}
CopyMem (NameBuf, Buf, LinkLength);
}
//
// If relative pathname, restart at parent directory.
// If absolute pathname, restart at root.
//
Cp = NameBuf;
if (*Cp != '/') {
INumber = ParentInumber;
} else {
INumber = (INODE32)EXT2_ROOTINO;
}
if ((Rc = ReadInode (INumber, File)) != 0) {
goto out;
}
}
#endif // !LIBSA_NO_FS_SYMLINK
}
//
// Found terminal component.
//
Rc = 0;
#else // !LIBSA_FS_SINGLECOMPONENT
//
// look up component in the current (root) directory
//
Rc = SearchDirectory (Path, AsciiStrLen (Path), File, &INumber);
if (Rc != 0) {
goto out;
}
//
// open it
//
Rc = ReadInode (INumber, File);
#endif // !LIBSA_FS_SINGLECOMPONENT
Fp->SeekPtr = 0; // reset seek pointer
out:
if (Rc != 0) {
Ext2fsClose (File);
}
#if 0
else {
fsmod = "Ext2Fs";
}
#endif
return Rc;
}
/**
Close the file.
@param File File to be closed.
@retval 0 if Group descriptor read is success
**/
INT32
Ext2fsClose (
OPEN_FILE *File
)
{
FILE *Fp;
Fp = (FILE *)File->FileSystemSpecificData;
File->FileSystemSpecificData = NULL;
if (Fp == NULL) {
return 0;
}
if (Fp->SuperBlockPtr->Ext2FsGrpDes) {
FreePool (Fp->SuperBlockPtr->Ext2FsGrpDes);
}
if (Fp->Buffer) {
FreePool (Fp->Buffer);
}
FreePool (Fp->SuperBlockPtr);
FreePool (Fp);
return 0;
}
/**
Gets the size of the file from descriptor.
@param File File to be closed.
@retval size of the file from descriptor.
**/
UINT32
Ext2fsFileSize (
OPEN_FILE *File
)
{
FILE *Fp;
Fp = (FILE *)File->FileSystemSpecificData;
return (UINT32)Fp->DiskInode.Ext2DInodeSize;
}
/**
Copy a portion of a FILE into kernel memory.
Cross block boundaries when necessary
@param File
@param Start
@param Size
@param ResId
**/
INT32
Ext2fsRead (
OPEN_FILE *File,
VOID *Start,
UINT32 Size,
UINT32 *ResId
)
{
FILE *Fp;
UINT32 Csize;
CHAR8 *Buf;
UINT32 BufSize;
INT32 Rc;
CHAR8 *Address;
Fp = (FILE *)File->FileSystemSpecificData;
Rc = 0;
Address = Start;
while (Size != 0) {
//
// XXX should handle LARGEFILE
//
if (Fp->SeekPtr >= (OFFSET)Fp->DiskInode.Ext2DInodeSize) {
break;
}
Rc = BufReadFile (File, &Buf, &BufSize);
if (Rc != 0) {
break;
}
Csize = Size;
if (Csize > BufSize) {
Csize = BufSize;
}
CopyMem (Address, Buf, Csize);
Fp->SeekPtr += Csize;
Address += Csize;
Size -= Csize;
}
if (ResId != NULL) {
*ResId = Size;
}
return Rc;
}
/**
Updates the seek ptr of file based on seek point.
@param File pointer to an Open file.
@param Offset Offset to update the seekptr.
@param Where Seek point where it needs to be update.
**/
OFFSET
Ext2fsSeek (
OPEN_FILE *File,
OFFSET Offset,
INT32 Where
)
{
FILE *Fp;
Fp = (FILE *)File->FileSystemSpecificData;
switch (Where) {
case SEEK_SET:
Fp->SeekPtr = Offset;
break;
case SEEK_CUR:
Fp->SeekPtr += Offset;
break;
case SEEK_END:
//
// XXX should handle LARGEFILE
//
Fp->SeekPtr = Fp->DiskInode.Ext2DInodeSize - Offset;
break;
default:
return -1;
}
return Fp->SeekPtr;
}
/**
Update the mode and size from descriptor to stat Block.
contains that block.
@param File pointer to an file private data.
@param StatBlock pointer to File information (stats).
@retval 0 if success
**/
INT32
Ext2fsStat (
OPEN_FILE *File,
STAT *StatBlock
)
{
FILE *Fp;
Fp = (FILE *)File->FileSystemSpecificData;
//
// only important stuff
//
SetMem32 (StatBlock, sizeof * StatBlock, 0);
StatBlock->StatMode = Fp->DiskInode.Ext2DInodeMode;
//
// XXX should handle LARGEFILE
//
StatBlock->StatSize = Fp->DiskInode.Ext2DInodeSize;
return 0;
}
#ifdef EXT2FS_DEBUG
/**
Dump the file system super block info.
@param FileSystem pointer to filesystem.
**/
VOID
DumpSBlock (
M_EXT2FS *FileSystem
)
{
DEBUG ((DEBUG_INFO, "FileSystem->Ext2Fs.Ext2FsBlockCount = %u\n", FileSystem->Ext2Fs.Ext2FsBlockCount));
DEBUG ((DEBUG_INFO, "FileSystem->Ext2Fs.Ext2FsFirstDataBlock = %u\n", FileSystem->Ext2Fs.Ext2FsFirstDataBlock));
DEBUG ((DEBUG_INFO, "FileSystem->Ext2Fs.Ext2FsLogBlockSize = %u\n", FileSystem->Ext2Fs.Ext2FsLogBlockSize));
DEBUG ((DEBUG_INFO, "FileSystem->Ext2Fs.Ext2FsBlocksPerGroup = %u\n", FileSystem->Ext2Fs.Ext2FsBlocksPerGroup));
DEBUG ((DEBUG_INFO, "FileSystem->Ext2Fs.Ext2FsINodesPerGroup = %u\n", FileSystem->Ext2Fs.Ext2FsINodesPerGroup));
DEBUG ((DEBUG_INFO, "FileSystem->Ext2Fs.Ext2FsMagic = 0x%x\n", FileSystem->Ext2Fs.Ext2FsMagic));
DEBUG ((DEBUG_INFO, "FileSystem->Ext2Fs.Ext2FsRev = %u\n", FileSystem->Ext2Fs.Ext2FsRev));
if (FileSystem->Ext2Fs.Ext2FsRev == E2FS_REV1) {
DEBUG ((DEBUG_INFO, "FileSystem->Ext2Fs.Ext2FsFirstInode = %u\n",
FileSystem->Ext2Fs.Ext2FsFirstInode));
DEBUG ((DEBUG_INFO, "FileSystem->Ext2Fs.Ext2FsInodeSize = %u\n",
FileSystem->Ext2Fs.Ext2FsInodeSize));
DEBUG ((DEBUG_INFO, "FileSystem->Ext2Fs.Ext2FsFeaturesCompat = %u\n",
FileSystem->Ext2Fs.Ext2FsFeaturesCompat));
DEBUG ((DEBUG_INFO, "FileSystem->Ext2Fs.Ext2FsFeaturesIncompat = %u\n",
FileSystem->Ext2Fs.Ext2FsFeaturesIncompat));
DEBUG ((DEBUG_INFO, "FileSystem->Ext2Fs.Ext2FsFeaturesROCompat = %u\n",
FileSystem->Ext2Fs.Ext2FsFeaturesROCompat));
DEBUG ((DEBUG_INFO, "FileSystem->Ext2Fs.Ext2FsRsvdGDBlock = %u\n",
FileSystem->Ext2Fs.Ext2FsRsvdGDBlock));
}
DEBUG ((DEBUG_INFO, "FileSystem->Ext2FsGDSize = %u\n", FileSystem->Ext2FsGDSize));
DEBUG ((DEBUG_INFO, "FileSystem->Ext2FsBlockSize = %u\n", FileSystem->Ext2FsBlockSize));
DEBUG ((DEBUG_INFO, "FileSystem->Ext2FsFsbtobd = %u\n", FileSystem->Ext2FsFsbtobd));
DEBUG ((DEBUG_INFO, "FileSystem->Ext2FsNumCylinder = %u\n", FileSystem->Ext2FsNumCylinder));
DEBUG ((DEBUG_INFO, "FileSystem->Ext2FsNumGrpDesBlock = %u\n", FileSystem->Ext2FsNumGrpDesBlock));
DEBUG ((DEBUG_INFO, "FileSystem->Ext2FsInodesPerBlock = %u\n", FileSystem->Ext2FsInodesPerBlock));
DEBUG ((DEBUG_INFO, "FileSystem->Ext2FsInodesTablePerGrp = %u\n", FileSystem->Ext2FsInodesTablePerGrp));
}
/**
Dump the file group descriptor block info.
@param FileSystem pointer to filesystem.
**/
VOID
DumpGroupDesBlock (
M_EXT2FS *FileSystem
)
{
INT32 Index;
EXT2GD *Ext2FsGrpDesEntry;
for (Index=0; Index < FileSystem->Ext2FsNumCylinder; Index++) {
Ext2FsGrpDesEntry = (EXT2GD*) ((UINT32) FileSystem->Ext2FsGrpDes + (Index * FileSystem->Ext2FsGDSize));
DEBUG ((DEBUG_INFO, "Ext2FsGrpDes[Index=%u]\n", Index));
DEBUG ((DEBUG_INFO, " Ext2BGDBlockBitmap %u\n", Ext2FsGrpDesEntry->Ext2BGDBlockBitmap));
DEBUG ((DEBUG_INFO, " Ext2BGDInodeBitmap %u\n", Ext2FsGrpDesEntry->Ext2BGDInodeBitmap));
DEBUG ((DEBUG_INFO, " Ext2BGDInodeTables %u\n", Ext2FsGrpDesEntry->Ext2BGDInodeTables));
DEBUG ((DEBUG_INFO, " Ext2BGDFreeBlocks %u\n", Ext2FsGrpDesEntry->Ext2BGDFreeBlocks));
DEBUG ((DEBUG_INFO, " Ext2BGDFreeInodes %u\n", Ext2FsGrpDesEntry->Ext2BGDFreeInodes));
DEBUG ((DEBUG_INFO, " Ext2BGDNumDir %u\n", Ext2FsGrpDesEntry->Ext2BGDNumDir));
if (FileSystem->Ext2FsGDSize > 32) {
DEBUG ((DEBUG_INFO, " Ext2BGDInodeTablesHi %u\n", Ext2FsGrpDesEntry->Ext2BGDInodeTablesHi));
}
}
}
#endif
/**
Determine the disk block(s) that contains data for FILE <File>,
starting at Offset <FilePosition> and extending for up to <Len> bytes.
@param File Pointer to an file private data.
@param FilePosition offset address of the file position.
@param Len Number of bytes
@param NBlocks Number of consecutive blocks
@retval First disk block, number of consecutive blocks thru *<nblock>.
**/
INT32
Ext2fsDiskBlocks (
OPEN_FILE *File,
UINT32 FilePosition,
UINT32 Len,
UINT32 *NBlocks
)
{
FILE *Fp;
M_EXT2FS *FileSystem;
UINT32 BlockSize;
UINT32 First;
UINT32 Num;
INDPTR FileBlock;
INDPTR DiskBlock;
INT32 Rc;
First = 0;
Num = 0;
Fp = (FILE *) File->FileSystemSpecificData;
FileSystem = Fp->SuperBlockPtr;
BlockSize = FileSystem->Ext2FsBlockSize;
*NBlocks = 0;
while (Len >= BlockSize) {
FileBlock = LBLKNO (FileSystem, FilePosition);
DiskBlock = 0;
Rc = BlockMap (File, FileBlock, &DiskBlock);
if (Rc != 0) {
if (Num == 0) {
return 0;
} else {
break;
}
}
if (Num == 0) {
First = DiskBlock;
Num += 1;
} else if (First + Num == (UINT32)DiskBlock) {
Num += 1;
} else {
break;
}
FilePosition += BlockSize;
Len -= BlockSize;
}
*NBlocks = Num;
return FSBTODB (FileSystem, First);
}
/**
Given an offset in a FILE, find the disk block number that
contains that block.
@param OpenFile pointer to an Open file.
@param Stat Block to find the file.
@param Name Pointer to the disk which contains block.
@param NamLen
@retval 0 if success
@retval other if error.
**/
INT32
Ext2fsLookUpFile (
OPEN_FILE *OpenFile,
STAT *Stat,
CHAR8 *Name,
UINT32 NamLen
)
{
FILE *Fp;
EXT2FS_DIRECT *Dp;
CHAR8 *Buf;
UINT32 BufSize;
UINT32 Ext2DirectInodeNumber;
INT32 Ext2DirectRecLen;
UINT32 Ext2DirectNameLen;
INT32 Rc;
Fp = OpenFile->FileSystemSpecificData;
do {
if ((UINT32)Fp->SeekPtr >= Fp->DiskInode.Ext2DInodeSize) {
return 0;
}
if ((Rc = BufReadFile (OpenFile, &Buf, &BufSize)) != 0) {
return (Rc < 0) ? Rc : -Rc;
}
if (BufSize < sizeof (EXT2FS_DIRECT) - EXT2FS_MAXNAMLEN + 1) {
return -1; // XXX: corrupt entry.
}
Dp = (EXT2FS_DIRECT *) Buf;
Ext2DirectInodeNumber = Dp->Ext2DirectInodeNumber;
Ext2DirectRecLen = Dp->Ext2DirectRecLen;
Ext2DirectNameLen = Dp->Ext2DirectNameLen;
Dp->Ext2DirectName[Ext2DirectNameLen] = '\0';
if (Ext2DirectRecLen <= 0) {
return 0;
}
Fp->SeekPtr += Ext2DirectRecLen;
} while (Ext2DirectInodeNumber == 0 || Ext2DirectNameLen == 0);
CopyMem (Name, Dp->Ext2DirectName, MIN (NamLen, Ext2DirectNameLen));
_FILE Tf;
SetMem32 (&Tf, sizeof (Tf), 0);
if ((Rc = Ext2fsOpen (Dp->Ext2DirectName, &Tf.Openfile)) != 0) {
return (Rc < 0) ? Rc : -Rc;
}
Ext2fsStat (&Tf.Openfile, Stat);
Ext2fsClose (&Tf.Openfile);
return Ext2DirectNameLen;
}