1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
|
/*
* fs_ext2/inode.c
* Shared functions for inode handling
*
* Copyright (c) 2006 Christoph Pfisterer
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* See LICENSE.txt for details about the copyright status of
* the ext2 file system driver.
*/
#include "fs_ext2.h"
#define DEBUG_LEVEL 0
//
// time conversion
//
// Adopted from public domain code in FreeBSD libc.
//
#define SECSPERMIN 60
#define MINSPERHOUR 60
#define HOURSPERDAY 24
#define DAYSPERWEEK 7
#define DAYSPERNYEAR 365
#define DAYSPERLYEAR 366
#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY)
#define MONSPERYEAR 12
#define EPOCH_YEAR 1970
#define EPOCH_WDAY TM_THURSDAY
#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400)
static const int mon_lengths[2][MONSPERYEAR] = {
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
};
static const int year_lengths[2] = {
DAYSPERNYEAR, DAYSPERLYEAR
};
VOID Ext2DecodeTime(OUT EFI_TIME *EfiTime, IN UINT32 UnixTime)
{
long days, rem;
int y, newy, yleap;
const int *ip;
ZeroMem(EfiTime, sizeof(EFI_TIME));
days = UnixTime / SECSPERDAY;
rem = UnixTime % SECSPERDAY;
EfiTime->Hour = (int) (rem / SECSPERHOUR);
rem = rem % SECSPERHOUR;
EfiTime->Minute = (int) (rem / SECSPERMIN);
EfiTime->Second = (int) (rem % SECSPERMIN);
y = EPOCH_YEAR;
while (days < 0 || days >= (long) year_lengths[yleap = isleap(y)]) {
newy = y + days / DAYSPERNYEAR;
if (days < 0)
--newy;
days -= (newy - y) * DAYSPERNYEAR +
LEAPS_THRU_END_OF(newy - 1) -
LEAPS_THRU_END_OF(y - 1);
y = newy;
}
EfiTime->Year = y;
ip = mon_lengths[yleap];
for (EfiTime->Month = 0; days >= (long) ip[EfiTime->Month]; ++(EfiTime->Month))
days = days - (long) ip[EfiTime->Month];
EfiTime->Month++; // adjust range to EFI conventions
EfiTime->Day = (int) (days + 1);
}
//
// base inode functions
//
EFI_STATUS Ext2InodeOpen(IN EXT2_VOLUME_DATA *Volume,
IN UINT32 InodeNo,
IN EXT2_INODE *ParentDirInode OPTIONAL,
IN struct ext2_dir_entry *DirEntry OPTIONAL,
OUT EXT2_INODE **NewInode)
{
EFI_STATUS Status;
EXT2_INODE *Inode;
UINT32 GroupNo, GroupDescBlockNo, GroupDescIndex;
struct ext2_group_desc *GroupDesc;
UINT32 InodeNoInGroup, InodeBlockNo, InodeIndex;
UINTN i;
#if DEBUG_LEVEL
Print(L"Ext2InodeOpen: %d\n", InodeNo);
#endif
// first check the volume's list of open directory inodes
for (Inode = Volume->DirInodeList; Inode != NULL; Inode = Inode->Next) {
if (Inode->InodeNo == InodeNo) {
Inode->RefCount++;
*NewInode = Inode;
#if DEBUG_LEVEL
Print(L"...found dir %d '%s', now %d refs\n", Inode->InodeNo, Inode->Name, Inode->RefCount);
#endif
return EFI_SUCCESS;
}
}
// read the group descripter for the block group the inode belongs to
GroupNo = (InodeNo - 1) / Volume->SuperBlock->s_inodes_per_group;
GroupDescBlockNo = (Volume->SuperBlock->s_first_data_block + 1) +
GroupNo / (Volume->BlockSize / sizeof(struct ext2_group_desc));
GroupDescIndex = GroupNo % (Volume->BlockSize / sizeof(struct ext2_group_desc));
Status = Ext2ReadBlock(Volume, GroupDescBlockNo);
if (EFI_ERROR(Status))
return Status;
GroupDesc = ((struct ext2_group_desc *)(Volume->BlockBuffer)) + GroupDescIndex;
// TODO: in the future, read and keep the bg_inode_table field of all block
// groups when mounting the file system (optimization)
// read the inode block for the requested inode
InodeNoInGroup = (InodeNo - 1) % Volume->SuperBlock->s_inodes_per_group;
InodeBlockNo = GroupDesc->bg_inode_table +
InodeNoInGroup / (Volume->BlockSize / Volume->InodeSize);
InodeIndex = InodeNoInGroup % (Volume->BlockSize / Volume->InodeSize);
Status = Ext2ReadBlock(Volume, InodeBlockNo);
if (EFI_ERROR(Status))
return Status;
// set up the inode structure
Inode = AllocateZeroPool(sizeof(EXT2_INODE));
Inode->Volume = Volume;
Inode->InodeNo = InodeNo;
Inode->RefCount = 1;
if (ParentDirInode != NULL) {
ParentDirInode->RefCount++;
Inode->ParentDirInode = ParentDirInode;
#if DEBUG_LEVEL
Print(L"...parent inode %d '%s', %d refs\n", ParentDirInode->InodeNo,
ParentDirInode->Name, ParentDirInode->RefCount);
#endif
}
// convert file name
if (DirEntry != NULL) {
Inode->Name = AllocatePool((DirEntry->name_len + 1) * sizeof(CHAR16));
for (i = 0; i < DirEntry->name_len; i++)
Inode->Name[i] = DirEntry->name[i];
Inode->Name[i] = 0;
} else
Inode->Name = StrDuplicate(L"");
// keep the raw inode structure around
Inode->RawInode = AllocatePool(Volume->InodeSize);
CopyMem(Inode->RawInode, Volume->BlockBuffer + InodeIndex * Volume->InodeSize, Volume->InodeSize);
Inode->FileSize = Inode->RawInode->i_size;
// TODO: check docs for 64-bit sized files
if (S_ISDIR(Inode->RawInode->i_mode)) {
// add to the volume's list of open directories
if (Volume->DirInodeList != NULL) {
Volume->DirInodeList->Prev = Inode;
Inode->Next = Volume->DirInodeList;
}
Volume->DirInodeList = Inode;
}
*NewInode = Inode;
#if DEBUG_LEVEL
Print(L"...created inode %d '%s', %d refs\n", Inode->InodeNo, Inode->Name, Inode->RefCount);
#endif
return EFI_SUCCESS;
}
EFI_STATUS Ext2InodeClose(IN EXT2_INODE *Inode)
{
#if DEBUG_LEVEL
Print(L"Ext2InodeClose: %d '%s', %d refs\n", Inode->InodeNo, Inode->Name, Inode->RefCount);
#endif
Inode->RefCount--;
if (Inode->RefCount == 0) {
if (Inode->ParentDirInode != NULL)
Ext2InodeClose(Inode->ParentDirInode);
if (S_ISDIR(Inode->RawInode->i_mode)) {
// remove from the volume's list of open directories
if (Inode->Next)
Inode->Next->Prev = Inode->Prev;
if (Inode->Prev)
Inode->Prev->Next = Inode->Next;
if (Inode->Volume->DirInodeList == Inode)
Inode->Volume->DirInodeList = Inode->Next;
}
FreePool(Inode->RawInode);
FreePool(Inode->Name);
FreePool(Inode);
}
return EFI_SUCCESS;
}
VOID Ext2InodeFillFileInfo(IN EXT2_INODE *Inode,
OUT EFI_FILE_INFO *FileInfo)
{
FileInfo->FileSize = Inode->FileSize;
FileInfo->PhysicalSize = Inode->RawInode->i_blocks * 512; // very, very strange...
Ext2DecodeTime(&FileInfo->CreateTime, Inode->RawInode->i_ctime);
Ext2DecodeTime(&FileInfo->LastAccessTime, Inode->RawInode->i_atime);
Ext2DecodeTime(&FileInfo->ModificationTime, Inode->RawInode->i_mtime);
FileInfo->Attribute = 0;
if (S_ISDIR(Inode->RawInode->i_mode))
FileInfo->Attribute |= EFI_FILE_DIRECTORY;
if ((Inode->RawInode->i_mode & S_IWUSR) == 0)
FileInfo->Attribute |= EFI_FILE_READ_ONLY;
}
//
// inode handle functions
//
EFI_STATUS Ext2InodeHandleOpen(IN EXT2_VOLUME_DATA *Volume,
IN UINT32 InodeNo,
IN EXT2_INODE *ParentDirInode OPTIONAL,
IN struct ext2_dir_entry *DirEntry OPTIONAL,
OUT EXT2_INODE_HANDLE *InodeHandle)
{
EFI_STATUS Status;
// open the actual inode
Status = Ext2InodeOpen(Volume, InodeNo, ParentDirInode, DirEntry, &InodeHandle->Inode);
if (EFI_ERROR(Status))
return Status;
InodeHandle->CurrentPosition = 0;
InodeHandle->CurrentFileBlockNo = INVALID_BLOCK_NO;
return EFI_SUCCESS;
}
EFI_STATUS Ext2InodeHandleReopen(IN EXT2_INODE *Inode,
OUT EXT2_INODE_HANDLE *InodeHandle)
{
InodeHandle->Inode = Inode;
InodeHandle->CurrentPosition = 0;
InodeHandle->CurrentFileBlockNo = INVALID_BLOCK_NO;
Inode->RefCount++;
#if DEBUG_LEVEL
Print(L"Ext2InodeHandleReopen: %d '%s', now %d refs\n", Inode->InodeNo, Inode->Name, Inode->RefCount);
#endif
return EFI_SUCCESS;
}
EFI_STATUS Ext2InodeHandleClose(IN EXT2_INODE_HANDLE *InodeHandle)
{
Ext2InodeClose(InodeHandle->Inode);
InodeHandle->Inode = NULL; // just for safety
return EFI_SUCCESS;
}
EFI_STATUS Ext2InodeHandleMapBlock(IN EXT2_INODE_HANDLE *InodeHandle,
IN UINT32 FileBlockNo)
{
EFI_STATUS Status;
EXT2_VOLUME_DATA *Volume;
UINT32 IndBlockNo;
Volume = InodeHandle->Inode->Volume;
// try direct block pointers in the inode
if (FileBlockNo < EXT2_NDIR_BLOCKS) {
InodeHandle->CurrentVolBlockNo = InodeHandle->Inode->RawInode->i_block[FileBlockNo];
return EFI_SUCCESS;
}
FileBlockNo -= EXT2_NDIR_BLOCKS;
// try indirect block
if (FileBlockNo < Volume->IndBlockCount) {
// read the indirect block into buffer
Status = Ext2ReadBlock(Volume, InodeHandle->Inode->RawInode->i_block[EXT2_IND_BLOCK]);
if (EFI_ERROR(Status))
return Status;
InodeHandle->CurrentVolBlockNo = ((__u32 *)Volume->BlockBuffer)[FileBlockNo];
return EFI_SUCCESS;
}
FileBlockNo -= Volume->IndBlockCount;
// try double-indirect block
if (FileBlockNo < Volume->DIndBlockCount) {
// read the double-indirect block into buffer
Status = Ext2ReadBlock(Volume, InodeHandle->Inode->RawInode->i_block[EXT2_DIND_BLOCK]);
if (EFI_ERROR(Status))
return Status;
IndBlockNo = ((__u32 *)Volume->BlockBuffer)[FileBlockNo / Volume->IndBlockCount];
// read the linked indirect block into buffer
Status = Ext2ReadBlock(Volume, IndBlockNo);
if (EFI_ERROR(Status))
return Status;
InodeHandle->CurrentVolBlockNo = ((__u32 *)Volume->BlockBuffer)[FileBlockNo % Volume->IndBlockCount];
return EFI_SUCCESS;
}
FileBlockNo -= Volume->DIndBlockCount;
// use the triple-indirect block
// read the triple-indirect block into buffer
Status = Ext2ReadBlock(Volume, InodeHandle->Inode->RawInode->i_block[EXT2_TIND_BLOCK]);
if (EFI_ERROR(Status))
return Status;
IndBlockNo = ((__u32 *)Volume->BlockBuffer)[FileBlockNo / Volume->DIndBlockCount];
// read the linked double-indirect block into buffer
Status = Ext2ReadBlock(Volume, IndBlockNo);
if (EFI_ERROR(Status))
return Status;
IndBlockNo = ((__u32 *)Volume->BlockBuffer)[(FileBlockNo / Volume->IndBlockCount) % Volume->IndBlockCount];
// read the linked indirect block into buffer
Status = Ext2ReadBlock(Volume, IndBlockNo);
if (EFI_ERROR(Status))
return Status;
InodeHandle->CurrentVolBlockNo = ((__u32 *)Volume->BlockBuffer)[FileBlockNo % Volume->IndBlockCount];
return EFI_SUCCESS;
}
EFI_STATUS Ext2InodeHandleRead(IN EXT2_INODE_HANDLE *InodeHandle,
IN OUT UINTN *BufferSize,
OUT VOID *Buffer)
{
EFI_STATUS Status;
EXT2_VOLUME_DATA *Volume;
UINTN RemLength, CopyLength;
UINT8 *RemBuffer;
UINT32 Position;
UINT32 FileBlockNo;
//Print(L"Ext2InodeHandleRead %d bytes at %ld\n", *BufferSize, InodeHandle->CurrentPosition);
Volume = InodeHandle->Inode->Volume;
if (InodeHandle->CurrentPosition >= InodeHandle->Inode->FileSize) {
// end of file reached
*BufferSize = 0;
return EFI_SUCCESS;
}
// NOTE: since ext2 only supports 32 bit file sizes, we can now assume CurrentPosition fits in 32 bits.
// TODO: check how this code compiles on 64 bit archs
// initialize loop variables
RemBuffer = Buffer;
RemLength = *BufferSize;
Position = (UINT32)InodeHandle->CurrentPosition;
// constrain read to file size
if (RemLength > (InodeHandle->Inode->FileSize - Position))
RemLength = (UINTN)(InodeHandle->Inode->FileSize - Position); // the condition ensures this cast is okay
while (RemLength > 0) {
// find block number to read in the file's terms
FileBlockNo = Position / Volume->BlockSize;
// find corresponding disk block
if (InodeHandle->CurrentFileBlockNo != FileBlockNo) {
Status = Ext2InodeHandleMapBlock(InodeHandle, FileBlockNo);
if (EFI_ERROR(Status)) {
InodeHandle->CurrentFileBlockNo = INVALID_BLOCK_NO;
return Status;
}
InodeHandle->CurrentFileBlockNo = FileBlockNo;
}
// load the data block
Status = Ext2ReadBlock(Volume, InodeHandle->CurrentVolBlockNo);
if (EFI_ERROR(Status))
return Status;
// copy data to the buffer
CopyLength = Volume->BlockSize - (Position & (Volume->BlockSize - 1));
if (CopyLength > RemLength)
CopyLength = RemLength;
CopyMem(RemBuffer, Volume->BlockBuffer + (Position & (Volume->BlockSize - 1)), CopyLength);
// advance loop variables
RemBuffer += CopyLength;
RemLength -= CopyLength;
Position += CopyLength;
}
// calculate bytes actually read
*BufferSize = Position - (UINT32)InodeHandle->CurrentPosition;
InodeHandle->CurrentPosition = Position;
return EFI_SUCCESS;
}
|