00001
00032 #include "linden_common.h"
00033
00034 #include <sys/stat.h>
00035 #include <set>
00036 #include <map>
00037 #if LL_WINDOWS
00038 #include <share.h>
00039 #elif LL_SOLARIS
00040 #include <sys/types.h>
00041 #include <unistd.h>
00042 #include <fcntl.h>
00043 #else
00044 #include <sys/file.h>
00045 #endif
00046
00047 #include "llvfs.h"
00048 #include "llstl.h"
00049
00050 const S32 FILE_BLOCK_MASK = 0x000003FF;
00051 const S32 VFS_CLEANUP_SIZE = 5242880;
00052 const S32 BLOCK_LENGTH_INVALID = -1;
00053
00054 LLVFS *gVFS = NULL;
00055
00056
00057 class LLVFSBlock
00058 {
00059 public:
00060 LLVFSBlock()
00061 {
00062 mLocation = 0;
00063 mLength = 0;
00064 }
00065
00066 LLVFSBlock(U32 loc, S32 size)
00067 {
00068 mLocation = loc;
00069 mLength = size;
00070 }
00071
00072 static bool locationSortPredicate(
00073 const LLVFSBlock* lhs,
00074 const LLVFSBlock* rhs)
00075 {
00076 return lhs->mLocation < rhs->mLocation;
00077 }
00078
00079 public:
00080 U32 mLocation;
00081 S32 mLength;
00082 };
00083
00084 LLVFSFileSpecifier::LLVFSFileSpecifier()
00085 : mFileID(),
00086 mFileType( LLAssetType::AT_NONE )
00087 {
00088 }
00089
00090 LLVFSFileSpecifier::LLVFSFileSpecifier(const LLUUID &file_id, const LLAssetType::EType file_type)
00091 {
00092 mFileID = file_id;
00093 mFileType = file_type;
00094 }
00095
00096 bool LLVFSFileSpecifier::operator<(const LLVFSFileSpecifier &rhs) const
00097 {
00098 return (mFileID == rhs.mFileID)
00099 ? mFileType < rhs.mFileType
00100 : mFileID < rhs.mFileID;
00101 }
00102
00103 bool LLVFSFileSpecifier::operator==(const LLVFSFileSpecifier &rhs) const
00104 {
00105 return (mFileID == rhs.mFileID &&
00106 mFileType == rhs.mFileType);
00107 }
00108
00109
00110 class LLVFSFileBlock : public LLVFSBlock, public LLVFSFileSpecifier
00111 {
00112 public:
00113 LLVFSFileBlock() : LLVFSBlock(), LLVFSFileSpecifier()
00114 {
00115 init();
00116 }
00117
00118 LLVFSFileBlock(const LLUUID &file_id, LLAssetType::EType file_type, U32 loc = 0, S32 size = 0)
00119 : LLVFSBlock(loc, size), LLVFSFileSpecifier( file_id, file_type )
00120 {
00121 init();
00122 }
00123
00124 void init()
00125 {
00126 mSize = 0;
00127 mIndexLocation = -1;
00128 mAccessTime = (U32)time(NULL);
00129
00130 for (S32 i = 0; i < (S32)VFSLOCK_COUNT; i++)
00131 {
00132 mLocks[(EVFSLock)i] = 0;
00133 }
00134 }
00135
00136 #ifdef LL_LITTLE_ENDIAN
00137 inline void swizzleCopy(void *dst, void *src, int size) { memcpy(dst, src, size); }
00138
00139 #else
00140
00141 inline U32 swizzle32(U32 x)
00142 {
00143 return(((x >> 24) & 0x000000FF) | ((x >> 8) & 0x0000FF00) | ((x << 8) & 0x00FF0000) |((x << 24) & 0xFF000000));
00144 }
00145
00146 inline U16 swizzle16(U16 x)
00147 {
00148 return( ((x >> 8) & 0x000000FF) | ((x << 8) & 0x0000FF00) );
00149 }
00150
00151 inline void swizzleCopy(void *dst, void *src, int size)
00152 {
00153 if(size == 4)
00154 {
00155 ((U32*)dst)[0] = swizzle32(((U32*)src)[0]);
00156 }
00157 else if(size == 2)
00158 {
00159 ((U16*)dst)[0] = swizzle16(((U16*)src)[0]);
00160 }
00161 else
00162 {
00163
00164 memcpy(dst, src, size);
00165 }
00166 }
00167
00168 #endif
00169
00170 void serialize(U8 *buffer)
00171 {
00172 swizzleCopy(buffer, &mLocation, 4);
00173 buffer += 4;
00174 swizzleCopy(buffer, &mLength, 4);
00175 buffer +=4;
00176 swizzleCopy(buffer, &mAccessTime, 4);
00177 buffer +=4;
00178 memcpy(buffer, &mFileID.mData, 16);
00179 buffer += 16;
00180 S16 temp_type = mFileType;
00181 swizzleCopy(buffer, &temp_type, 2);
00182 buffer += 2;
00183 swizzleCopy(buffer, &mSize, 4);
00184 }
00185
00186 void deserialize(U8 *buffer, const S32 index_loc)
00187 {
00188 mIndexLocation = index_loc;
00189
00190 swizzleCopy(&mLocation, buffer, 4);
00191 buffer += 4;
00192 swizzleCopy(&mLength, buffer, 4);
00193 buffer += 4;
00194 swizzleCopy(&mAccessTime, buffer, 4);
00195 buffer += 4;
00196 memcpy(&mFileID.mData, buffer, 16);
00197 buffer += 16;
00198 S16 temp_type;
00199 swizzleCopy(&temp_type, buffer, 2);
00200 mFileType = (LLAssetType::EType)temp_type;
00201 buffer += 2;
00202 swizzleCopy(&mSize, buffer, 4);
00203 }
00204
00205 static BOOL insertLRU(LLVFSFileBlock* const& first,
00206 LLVFSFileBlock* const& second)
00207 {
00208 return (first->mAccessTime == second->mAccessTime)
00209 ? *first < *second
00210 : first->mAccessTime < second->mAccessTime;
00211 }
00212
00213 public:
00214 S32 mSize;
00215 S32 mIndexLocation;
00216 U32 mAccessTime;
00217 BOOL mLocks[VFSLOCK_COUNT];
00218
00219 static const S32 SERIAL_SIZE;
00220 };
00221
00222
00223 struct LLVFSFileBlock_less
00224 {
00225 bool operator()(LLVFSFileBlock* const& lhs, LLVFSFileBlock* const& rhs) const
00226 {
00227 return (LLVFSFileBlock::insertLRU(lhs, rhs)) ? true : false;
00228 }
00229 };
00230
00231
00232 const S32 LLVFSFileBlock::SERIAL_SIZE = 34;
00233
00234
00235 LLVFS::LLVFS(const char *index_filename, const char *data_filename, const BOOL read_only, const U32 presize, const BOOL remove_after_crash)
00236 : mRemoveAfterCrash(remove_after_crash)
00237 {
00238 mDataMutex = new LLMutex(0);
00239
00240 S32 i;
00241 for (i = 0; i < VFSLOCK_COUNT; i++)
00242 {
00243 mLockCounts[i] = 0;
00244 }
00245 mValid = VFSVALID_OK;
00246 mReadOnly = read_only;
00247 mIndexFilename = new char[strlen(index_filename) + 1];
00248 mDataFilename = new char[strlen(data_filename) + 1];
00249 if (mIndexFilename == NULL || mDataFilename == NULL)
00250 {
00251 LL_ERRS("VFS") << "Memory Allocation Failure" << LL_ENDL;
00252 return;
00253 }
00254 strcpy(mIndexFilename, index_filename);
00255 strcpy(mDataFilename, data_filename);
00256
00257 const char *file_mode = mReadOnly ? "rb" : "r+b";
00258
00259 if (! (mDataFP = openAndLock(mDataFilename, file_mode, mReadOnly)))
00260 {
00261
00262 if (mReadOnly)
00263 {
00264 LL_WARNS("VFS") << "Can't find " << mDataFilename << " to open read-only VFS" << LL_ENDL;
00265 mValid = VFSVALID_BAD_CANNOT_OPEN_READONLY;
00266 return;
00267 }
00268
00269 if((mDataFP = openAndLock(mDataFilename, "w+b", FALSE)))
00270 {
00271
00272
00273 LLFile::remove(mIndexFilename);
00274 }
00275 else
00276 {
00277 LL_WARNS("VFS") << "Can't open VFS data file " << mDataFilename << " attempting to use alternate" << LL_ENDL;
00278
00279 char *temp_index = new char[strlen(mIndexFilename) + 10];
00280 if (!temp_index)
00281 {
00282 LL_ERRS("VFS") << "Out of the memory in LLVFS::LLVFS()" << LL_ENDL;
00283 return;
00284 }
00285 char *temp_data = new char[strlen(mDataFilename) + 10];
00286 if (!temp_data)
00287 {
00288 LL_ERRS("VFS") << "Out of the memory in LLVFS::LLVFS()" << LL_ENDL;
00289 return;
00290 }
00291
00292 for (U32 count = 0; count < 256; count++)
00293 {
00294 sprintf(temp_index, "%s.%u", mIndexFilename, count);
00295 sprintf(temp_data, "%s.%u", mDataFilename, count);
00296
00297
00298 if ((mDataFP = openAndLock(temp_data, "r+b", FALSE)))
00299 {
00300 break;
00301 }
00302
00303 if ((mDataFP = openAndLock(temp_data, "w+b", FALSE)))
00304 {
00305
00306 LLFile::remove(temp_index);
00307 break;
00308 }
00309 }
00310
00311 if (! mDataFP)
00312 {
00313 LL_WARNS("VFS") << "Couldn't open vfs data file after trying many alternates" << LL_ENDL;
00314 mValid = VFSVALID_BAD_CANNOT_CREATE;
00315 delete[] temp_index;
00316 delete[] temp_data;
00317 return;
00318 }
00319
00320 delete[] mIndexFilename;
00321 delete[] mDataFilename;
00322
00323 mIndexFilename = temp_index;
00324 mDataFilename = temp_data;
00325 }
00326
00327 if (presize)
00328 {
00329 presizeDataFile(presize);
00330 }
00331 }
00332
00333
00334
00335 if (!mReadOnly && mRemoveAfterCrash)
00336 {
00337 llstat marker_info;
00338 char* marker = new char[strlen(mDataFilename) + strlen(".open") + 1];
00339 if (!marker )
00340 {
00341 LL_ERRS("VFS") << "Out of memory in LLVFS::LLVFS()" << LL_ENDL;
00342 return;
00343 }
00344 sprintf(marker, "%s.open", mDataFilename);
00345 if (!LLFile::stat(marker, &marker_info))
00346 {
00347
00348 unlockAndClose(mDataFP);
00349 mDataFP = NULL;
00350
00351 LL_WARNS("VFS") << "VFS: File left open on last run, removing old VFS file " << mDataFilename << LL_ENDL;
00352 LLFile::remove(mIndexFilename);
00353 LLFile::remove(mDataFilename);
00354 LLFile::remove(marker);
00355
00356 mDataFP = openAndLock(mDataFilename, "w+b", FALSE);
00357 if (!mDataFP)
00358 {
00359 LL_WARNS("VFS") << "Can't open VFS data file in crash recovery" << LL_ENDL;
00360 mValid = VFSVALID_BAD_CANNOT_CREATE;
00361 return;
00362 }
00363
00364 if (presize)
00365 {
00366 presizeDataFile(presize);
00367 }
00368 }
00369 delete [] marker;
00370 marker = NULL;
00371 }
00372
00373
00374 fseek(mDataFP, 0, SEEK_END);
00375 U32 data_size = ftell(mDataFP);
00376
00377
00378
00379
00380 llstat fbuf;
00381 if (! LLFile::stat(mIndexFilename, &fbuf) &&
00382 fbuf.st_size >= LLVFSFileBlock::SERIAL_SIZE &&
00383 (mIndexFP = openAndLock(mIndexFilename, file_mode, mReadOnly))
00384 )
00385 {
00386 U8 *buffer = new U8[fbuf.st_size];
00387 size_t nread = fread(buffer, 1, fbuf.st_size, mIndexFP);
00388
00389 U8 *tmp_ptr = buffer;
00390
00391 std::vector<LLVFSFileBlock*> files_by_loc;
00392
00393 while (tmp_ptr < buffer + nread)
00394 {
00395 LLVFSFileBlock *block = new LLVFSFileBlock();
00396
00397 block->deserialize(tmp_ptr, (S32)(tmp_ptr - buffer));
00398
00399
00400
00401
00402 if (block->mLength > 0 &&
00403 (U32)block->mLength <= data_size &&
00404 block->mLocation < data_size &&
00405 block->mSize > 0 &&
00406 block->mSize <= block->mLength &&
00407 block->mFileType >= LLAssetType::AT_NONE &&
00408 block->mFileType < LLAssetType::AT_COUNT)
00409 {
00410 mFileBlocks.insert(fileblock_map::value_type(*block, block));
00411 files_by_loc.push_back(block);
00412 }
00413 else
00414 if (block->mLength && block->mSize > 0)
00415 {
00416
00417 LL_WARNS("VFS") << "VFS corruption: " << block->mFileID << " (" << block->mFileType << ") at index " << block->mIndexLocation << " DS: " << data_size << LL_ENDL;
00418 LL_WARNS("VFS") << "Length: " << block->mLength << "\tLocation: " << block->mLocation << "\tSize: " << block->mSize << LL_ENDL;
00419 LL_WARNS("VFS") << "File has bad data - VFS removed" << LL_ENDL;
00420
00421 delete[] buffer;
00422 delete block;
00423
00424 unlockAndClose( mIndexFP );
00425 mIndexFP = NULL;
00426 LLFile::remove( mIndexFilename );
00427
00428 unlockAndClose( mDataFP );
00429 mDataFP = NULL;
00430 LLFile::remove( mDataFilename );
00431
00432 mValid = VFSVALID_BAD_CORRUPT;
00433 return;
00434 }
00435 else
00436 {
00437
00438 S32 index_loc = (S32)(tmp_ptr - buffer);
00439 mIndexHoles.push_back(index_loc);
00440
00441 delete block;
00442 }
00443
00444 tmp_ptr += LLVFSFileBlock::SERIAL_SIZE;
00445 }
00446 delete[] buffer;
00447
00448 std::sort(
00449 files_by_loc.begin(),
00450 files_by_loc.end(),
00451 LLVFSFileBlock::locationSortPredicate);
00452
00453
00454
00455
00456
00457 if (!files_by_loc.empty())
00458 {
00459
00460 std::vector<LLVFSFileBlock*>::iterator cur = files_by_loc.begin();
00461 std::vector<LLVFSFileBlock*>::iterator end = files_by_loc.end();
00462 LLVFSFileBlock* last_file_block = *cur;
00463
00464
00465 if (last_file_block->mLocation > 0)
00466 {
00467
00468 addFreeBlock(new LLVFSBlock(0, last_file_block->mLocation));
00469 }
00470
00471
00472
00473
00474
00475 ++cur;
00476 while( cur != end )
00477 {
00478 LLVFSFileBlock* cur_file_block = *cur;
00479
00480
00481 if (cur_file_block->mLocation == last_file_block->mLocation
00482 && cur_file_block->mLength == last_file_block->mLength)
00483 {
00484 LL_WARNS("VFS") << "VFS: removing duplicate entry"
00485 << " at " << cur_file_block->mLocation
00486 << " length " << cur_file_block->mLength
00487 << " size " << cur_file_block->mSize
00488 << " ID " << cur_file_block->mFileID
00489 << " type " << cur_file_block->mFileType
00490 << LL_ENDL;
00491
00492
00493 mFileBlocks.erase(*cur_file_block);
00494 if (cur_file_block->mLength > 0)
00495 {
00496
00497 addFreeBlock(
00498 new LLVFSBlock(
00499 cur_file_block->mLocation,
00500 cur_file_block->mLength));
00501 }
00502 lockData();
00503 sync(cur_file_block, TRUE);
00504 sync(last_file_block, TRUE);
00505 unlockData();
00506 last_file_block = cur_file_block;
00507 ++cur;
00508 continue;
00509 }
00510
00511
00512 S32 loc = last_file_block->mLocation+last_file_block->mLength;
00513
00514
00515
00516 S32 length = cur_file_block->mLocation - loc;
00517
00518
00519
00520 if (length < 0 || loc < 0 || (U32)loc > data_size)
00521 {
00522
00523 unlockAndClose( mIndexFP );
00524 mIndexFP = NULL;
00525 LLFile::remove( mIndexFilename );
00526
00527 unlockAndClose( mDataFP );
00528 mDataFP = NULL;
00529 LLFile::remove( mDataFilename );
00530
00531 LL_WARNS("VFS") << "VFS: overlapping entries"
00532 << " at " << cur_file_block->mLocation
00533 << " length " << cur_file_block->mLength
00534 << " ID " << cur_file_block->mFileID
00535 << " type " << cur_file_block->mFileType
00536 << LL_ENDL;
00537 mValid = VFSVALID_BAD_CORRUPT;
00538 return;
00539 }
00540
00541
00542 if (length > 0)
00543 {
00544 addFreeBlock(new LLVFSBlock(loc, length));
00545 }
00546 last_file_block = cur_file_block;
00547 ++cur;
00548 }
00549
00550
00551 U32 loc = last_file_block->mLocation + last_file_block->mLength;
00552 if (loc < data_size)
00553 {
00554 addFreeBlock(new LLVFSBlock(loc, data_size - loc));
00555 }
00556 }
00557 else
00558 {
00559 addFreeBlock(new LLVFSBlock(0, data_size));
00560 }
00561 }
00562 else
00563 {
00564 if (mReadOnly)
00565 {
00566 LL_WARNS("VFS") << "Can't find " << mIndexFilename << " to open read-only VFS" << LL_ENDL;
00567 mValid = VFSVALID_BAD_CANNOT_OPEN_READONLY;
00568 return;
00569 }
00570
00571
00572 mIndexFP = openAndLock(mIndexFilename, "w+b", FALSE);
00573 if (!mIndexFP)
00574 {
00575 LL_WARNS("VFS") << "Couldn't open an index file for the VFS, probably a sharing violation!" << LL_ENDL;
00576
00577 unlockAndClose( mDataFP );
00578 mDataFP = NULL;
00579 LLFile::remove( mDataFilename );
00580
00581 mValid = VFSVALID_BAD_CANNOT_CREATE;
00582 return;
00583 }
00584
00585
00586 LLVFSBlock *first_block = new LLVFSBlock(0, data_size ? data_size : 0x40000000);
00587 addFreeBlock(first_block);
00588 }
00589
00590
00591 if (!mReadOnly && mRemoveAfterCrash)
00592 {
00593 char* marker = new char[strlen(mDataFilename) + strlen(".open") + 1];
00594 if (!marker)
00595 {
00596 LL_ERRS("VFS") << "Out of memory in LLVFS::LLVFS()" << LL_ENDL;
00597 return;
00598 }
00599 sprintf(marker, "%s.open", mDataFilename);
00600 LLFILE* marker_fp = LLFile::fopen(marker, "w");
00601 if (marker_fp)
00602 {
00603 fclose(marker_fp);
00604 marker_fp = NULL;
00605 }
00606 delete [] marker;
00607 marker = NULL;
00608 }
00609
00610 LL_WARNS("VFS") << "Using index file " << mIndexFilename << LL_ENDL;
00611 LL_WARNS("VFS") << "Using data file " << mDataFilename << LL_ENDL;
00612
00613 mValid = VFSVALID_OK;
00614 }
00615
00616 LLVFS::~LLVFS()
00617 {
00618 if (mDataMutex->isLocked())
00619 {
00620 LL_ERRS("VFS") << "LLVFS destroyed with mutex locked" << LL_ENDL;
00621 }
00622
00623 unlockAndClose(mIndexFP);
00624 mIndexFP = NULL;
00625
00626 fileblock_map::const_iterator it;
00627 for (it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it)
00628 {
00629 delete (*it).second;
00630 }
00631 mFileBlocks.clear();
00632
00633 mFreeBlocksByLength.clear();
00634
00635 for_each(mFreeBlocksByLocation.begin(), mFreeBlocksByLocation.end(), DeletePairedPointer());
00636
00637 unlockAndClose(mDataFP);
00638 mDataFP = NULL;
00639
00640
00641 if (!mReadOnly && mRemoveAfterCrash)
00642 {
00643 char* marker_file = new char[strlen(mDataFilename) + strlen(".open") + 1];
00644 if (marker_file == NULL)
00645 {
00646 LL_ERRS("VFS") << "Memory Allocation Failure" << LL_ENDL;
00647 return;
00648 }
00649 sprintf(marker_file, "%s.open", mDataFilename);
00650 LLFile::remove(marker_file);
00651 delete [] marker_file;
00652 marker_file = NULL;
00653 }
00654
00655 delete[] mIndexFilename;
00656 mIndexFilename = NULL;
00657 delete[] mDataFilename;
00658 mDataFilename = NULL;
00659
00660 delete mDataMutex;
00661 }
00662
00663 void LLVFS::presizeDataFile(const U32 size)
00664 {
00665 if (!mDataFP)
00666 {
00667 llerrs << "LLVFS::presizeDataFile() with no data file open" << llendl;
00668 return;
00669 }
00670
00671
00672 fseek(mDataFP, size-1, SEEK_SET);
00673 S32 tmp = 0;
00674 tmp = (S32)fwrite(&tmp, 1, 1, mDataFP);
00675
00676
00677
00678 LLFile::remove(mIndexFilename);
00679
00680 if (tmp)
00681 {
00682 llinfos << "Pre-sized VFS data file to " << ftell(mDataFP) << " bytes" << llendl;
00683 }
00684 else
00685 {
00686 llwarns << "Failed to pre-size VFS data file" << llendl;
00687 }
00688 }
00689
00690 BOOL LLVFS::getExists(const LLUUID &file_id, const LLAssetType::EType file_type)
00691 {
00692 LLVFSFileBlock *block = NULL;
00693
00694 if (!isValid())
00695 {
00696 llerrs << "Attempting to use invalid VFS!" << llendl;
00697 }
00698
00699 lockData();
00700
00701 LLVFSFileSpecifier spec(file_id, file_type);
00702 fileblock_map::iterator it = mFileBlocks.find(spec);
00703 if (it != mFileBlocks.end())
00704 {
00705 block = (*it).second;
00706 block->mAccessTime = (U32)time(NULL);
00707 }
00708
00709 BOOL res = (block && block->mLength > 0) ? TRUE : FALSE;
00710
00711 unlockData();
00712
00713 return res;
00714 }
00715
00716 S32 LLVFS::getSize(const LLUUID &file_id, const LLAssetType::EType file_type)
00717 {
00718 S32 size = 0;
00719
00720 if (!isValid())
00721 {
00722 llerrs << "Attempting to use invalid VFS!" << llendl;
00723
00724 }
00725
00726 lockData();
00727
00728 LLVFSFileSpecifier spec(file_id, file_type);
00729 fileblock_map::iterator it = mFileBlocks.find(spec);
00730 if (it != mFileBlocks.end())
00731 {
00732 LLVFSFileBlock *block = (*it).second;
00733
00734 block->mAccessTime = (U32)time(NULL);
00735 size = block->mSize;
00736 }
00737
00738 unlockData();
00739
00740 return size;
00741 }
00742
00743 S32 LLVFS::getMaxSize(const LLUUID &file_id, const LLAssetType::EType file_type)
00744 {
00745 S32 size = 0;
00746
00747 if (!isValid())
00748 {
00749 llerrs << "Attempting to use invalid VFS!" << llendl;
00750 }
00751
00752 lockData();
00753
00754 LLVFSFileSpecifier spec(file_id, file_type);
00755 fileblock_map::iterator it = mFileBlocks.find(spec);
00756 if (it != mFileBlocks.end())
00757 {
00758 LLVFSFileBlock *block = (*it).second;
00759
00760 block->mAccessTime = (U32)time(NULL);
00761 size = block->mLength;
00762 }
00763
00764 unlockData();
00765
00766 return size;
00767 }
00768
00769 BOOL LLVFS::checkAvailable(S32 max_size)
00770 {
00771 blocks_length_map_t::iterator iter = mFreeBlocksByLength.lower_bound(max_size);
00772 return (iter == mFreeBlocksByLength.end()) ? FALSE : TRUE;
00773 }
00774
00775 BOOL LLVFS::setMaxSize(const LLUUID &file_id, const LLAssetType::EType file_type, S32 max_size)
00776 {
00777 if (!isValid())
00778 {
00779 llerrs << "Attempting to use invalid VFS!" << llendl;
00780 }
00781 if (mReadOnly)
00782 {
00783 llerrs << "Attempt to write to read-only VFS" << llendl;
00784 }
00785 if (max_size <= 0)
00786 {
00787 llwarns << "VFS: Attempt to assign size " << max_size << " to vfile " << file_id << llendl;
00788 return FALSE;
00789 }
00790
00791 lockData();
00792
00793 LLVFSFileSpecifier spec(file_id, file_type);
00794 LLVFSFileBlock *block = NULL;
00795 fileblock_map::iterator it = mFileBlocks.find(spec);
00796 if (it != mFileBlocks.end())
00797 {
00798 block = (*it).second;
00799 }
00800
00801
00802
00803
00804 if (file_type != LLAssetType::AT_TEXTURE)
00805 {
00806 if (max_size & FILE_BLOCK_MASK)
00807 {
00808 max_size += FILE_BLOCK_MASK;
00809 max_size &= ~FILE_BLOCK_MASK;
00810 }
00811 }
00812
00813 if (block && block->mLength > 0)
00814 {
00815 block->mAccessTime = (U32)time(NULL);
00816
00817 if (max_size == block->mLength)
00818 {
00819 unlockData();
00820 return TRUE;
00821 }
00822 else if (max_size < block->mLength)
00823 {
00824
00825 LLVFSBlock *free_block = new LLVFSBlock(block->mLocation + max_size, block->mLength - max_size);
00826
00827 addFreeBlock(free_block);
00828
00829 block->mLength = max_size;
00830
00831 if (block->mLength < block->mSize)
00832 {
00833
00834 llerrs << "Truncating virtual file " << file_id << " to " << block->mLength << " bytes" << llendl;
00835 block->mSize = block->mLength;
00836 }
00837
00838 sync(block);
00839
00840
00841 unlockData();
00842 return TRUE;
00843 }
00844 else if (max_size > block->mLength)
00845 {
00846
00847
00848 S32 size_increase = max_size - block->mLength;
00849
00850
00851 LLVFSBlock *free_block;
00852 blocks_location_map_t::iterator iter = mFreeBlocksByLocation.upper_bound(block->mLocation);
00853 if (iter != mFreeBlocksByLocation.end())
00854 {
00855 free_block = iter->second;
00856
00857 if (free_block->mLocation == block->mLocation + block->mLength &&
00858 free_block->mLength >= size_increase)
00859 {
00860
00861
00862
00863
00864 useFreeSpace(free_block, size_increase);
00865 block->mLength += size_increase;
00866 sync(block);
00867
00868 unlockData();
00869 return TRUE;
00870 }
00871 }
00872
00873
00874 free_block = findFreeBlock(max_size, block);
00875
00876 if (free_block)
00877 {
00878 if (block->mLength > 0)
00879 {
00880
00881 LLVFSBlock *new_free_block = new LLVFSBlock(block->mLocation, block->mLength);
00882
00883 addFreeBlock(new_free_block);
00884
00885 if (block->mSize > 0)
00886 {
00887
00888 U8 *buffer = new U8[block->mSize];
00889 fseek(mDataFP, block->mLocation, SEEK_SET);
00890 if (fread(buffer, block->mSize, 1, mDataFP) == 1)
00891 {
00892 fseek(mDataFP, free_block->mLocation, SEEK_SET);
00893 if (fwrite(buffer, block->mSize, 1, mDataFP) != 1)
00894 {
00895 llwarns << "Short write" << llendl;
00896 }
00897 } else {
00898 llwarns << "Short read" << llendl;
00899 }
00900
00901 delete[] buffer;
00902 }
00903 }
00904
00905 block->mLocation = free_block->mLocation;
00906
00907 block->mLength = max_size;
00908
00909
00910
00911 useFreeSpace(free_block, max_size);
00912
00913 sync(block);
00914
00915 unlockData();
00916 return TRUE;
00917 }
00918 else
00919 {
00920 llwarns << "VFS: No space (" << max_size << ") to resize existing vfile " << file_id << llendl;
00921
00922 unlockData();
00923 dumpStatistics();
00924 return FALSE;
00925 }
00926 }
00927 }
00928 else
00929 {
00930
00931 LLVFSBlock *free_block = findFreeBlock(max_size);
00932
00933 if (free_block)
00934 {
00935 if (block)
00936 {
00937 block->mLocation = free_block->mLocation;
00938 block->mLength = max_size;
00939 }
00940 else
00941 {
00942
00943 block = new LLVFSFileBlock(file_id, file_type, free_block->mLocation, max_size);
00944 mFileBlocks.insert(fileblock_map::value_type(spec, block));
00945 }
00946
00947
00948
00949 useFreeSpace(free_block, max_size);
00950 block->mAccessTime = (U32)time(NULL);
00951
00952 sync(block);
00953 }
00954 else
00955 {
00956 llwarns << "VFS: No space (" << max_size << ") for new virtual file " << file_id << llendl;
00957
00958 unlockData();
00959 dumpStatistics();
00960 return FALSE;
00961 }
00962 }
00963 unlockData();
00964 return TRUE;
00965 }
00966
00967
00968
00969
00970 void LLVFS::renameFile(const LLUUID &file_id, const LLAssetType::EType file_type,
00971 const LLUUID &new_id, const LLAssetType::EType &new_type)
00972 {
00973 if (!isValid())
00974 {
00975 llerrs << "Attempting to use invalid VFS!" << llendl;
00976 }
00977 if (mReadOnly)
00978 {
00979 llerrs << "Attempt to write to read-only VFS" << llendl;
00980 }
00981
00982 lockData();
00983
00984 LLVFSFileSpecifier new_spec(new_id, new_type);
00985 LLVFSFileSpecifier old_spec(file_id, file_type);
00986
00987 fileblock_map::iterator it = mFileBlocks.find(old_spec);
00988 if (it != mFileBlocks.end())
00989 {
00990 LLVFSFileBlock *src_block = (*it).second;
00991
00992
00993
00994 fileblock_map::iterator new_it = mFileBlocks.find(new_spec);
00995 if (new_it != mFileBlocks.end())
00996 {
00997 LLVFSFileBlock *new_block = (*new_it).second;
00998 removeFileBlock(new_block);
00999 }
01000
01001
01002 it = mFileBlocks.find(new_spec);
01003 if (it != mFileBlocks.end())
01004 {
01005 LLVFSFileBlock *dest_block = (*it).second;
01006
01007 for (S32 i = 0; i < (S32)VFSLOCK_COUNT; i++)
01008 {
01009 if(dest_block->mLocks[i])
01010 {
01011 llerrs << "Renaming VFS block to a locked file." << llendl;
01012 }
01013 dest_block->mLocks[i] = src_block->mLocks[i];
01014 }
01015
01016 mFileBlocks.erase(new_spec);
01017 delete dest_block;
01018 }
01019
01020 src_block->mFileID = new_id;
01021 src_block->mFileType = new_type;
01022 src_block->mAccessTime = (U32)time(NULL);
01023
01024 mFileBlocks.erase(old_spec);
01025 mFileBlocks.insert(fileblock_map::value_type(new_spec, src_block));
01026
01027 sync(src_block);
01028 }
01029 else
01030 {
01031 llwarns << "VFS: Attempt to rename nonexistent vfile " << file_id << ":" << file_type << llendl;
01032 }
01033 unlockData();
01034 }
01035
01036
01037 void LLVFS::removeFileBlock(LLVFSFileBlock *fileblock)
01038 {
01039
01040
01041 sync(fileblock, TRUE);
01042
01043 if (fileblock->mLength > 0)
01044 {
01045
01046 LLVFSBlock *free_block = new LLVFSBlock(fileblock->mLocation, fileblock->mLength);
01047
01048 addFreeBlock(free_block);
01049 }
01050
01051 fileblock->mLocation = 0;
01052 fileblock->mSize = 0;
01053 fileblock->mLength = BLOCK_LENGTH_INVALID;
01054 fileblock->mIndexLocation = -1;
01055
01056
01057 }
01058
01059 void LLVFS::removeFile(const LLUUID &file_id, const LLAssetType::EType file_type)
01060 {
01061 if (!isValid())
01062 {
01063 llerrs << "Attempting to use invalid VFS!" << llendl;
01064 }
01065 if (mReadOnly)
01066 {
01067 llerrs << "Attempt to write to read-only VFS" << llendl;
01068 }
01069
01070 lockData();
01071
01072 LLVFSFileSpecifier spec(file_id, file_type);
01073 fileblock_map::iterator it = mFileBlocks.find(spec);
01074 if (it != mFileBlocks.end())
01075 {
01076 LLVFSFileBlock *block = (*it).second;
01077 removeFileBlock(block);
01078 }
01079 else
01080 {
01081 llwarns << "VFS: attempting to remove nonexistent file " << file_id << " type " << file_type << llendl;
01082 }
01083
01084 unlockData();
01085 }
01086
01087
01088 S32 LLVFS::getData(const LLUUID &file_id, const LLAssetType::EType file_type, U8 *buffer, S32 location, S32 length)
01089 {
01090 S32 bytesread = 0;
01091
01092 if (!isValid())
01093 {
01094 llerrs << "Attempting to use invalid VFS!" << llendl;
01095 }
01096 llassert(location >= 0);
01097 llassert(length >= 0);
01098
01099 BOOL do_read = FALSE;
01100
01101 lockData();
01102
01103 LLVFSFileSpecifier spec(file_id, file_type);
01104 fileblock_map::iterator it = mFileBlocks.find(spec);
01105 if (it != mFileBlocks.end())
01106 {
01107 LLVFSFileBlock *block = (*it).second;
01108
01109 block->mAccessTime = (U32)time(NULL);
01110
01111 if (location > block->mSize)
01112 {
01113 llwarns << "VFS: Attempt to read location " << location << " in file " << file_id << " of length " << block->mSize << llendl;
01114 }
01115 else
01116 {
01117 if (length > block->mSize - location)
01118 {
01119 length = block->mSize - location;
01120 }
01121 location += block->mLocation;
01122 do_read = TRUE;
01123 }
01124 }
01125
01126 unlockData();
01127
01128 if (do_read)
01129 {
01130 fseek(mDataFP, location, SEEK_SET);
01131 bytesread = (S32)fread(buffer, 1, length, mDataFP);
01132 }
01133
01134 return bytesread;
01135 }
01136
01137 S32 LLVFS::storeData(const LLUUID &file_id, const LLAssetType::EType file_type, const U8 *buffer, S32 location, S32 length)
01138 {
01139 if (!isValid())
01140 {
01141 llerrs << "Attempting to use invalid VFS!" << llendl;
01142 }
01143 if (mReadOnly)
01144 {
01145 llerrs << "Attempt to write to read-only VFS" << llendl;
01146 }
01147
01148 llassert(length > 0);
01149
01150 lockData();
01151
01152 LLVFSFileSpecifier spec(file_id, file_type);
01153 fileblock_map::iterator it = mFileBlocks.find(spec);
01154 if (it != mFileBlocks.end())
01155 {
01156 LLVFSFileBlock *block = (*it).second;
01157
01158 S32 in_loc = location;
01159 if (location == -1)
01160 {
01161 location = block->mSize;
01162 }
01163 llassert(location >= 0);
01164
01165 block->mAccessTime = (U32)time(NULL);
01166
01167 if (block->mLength == BLOCK_LENGTH_INVALID)
01168 {
01169
01170 llwarns << "VFS: Attempt to write to invalid block"
01171 << " in file " << file_id
01172 << " location: " << in_loc
01173 << " bytes: " << length
01174 << llendl;
01175 unlockData();
01176 return length;
01177 }
01178 else if (location > block->mLength)
01179 {
01180 llwarns << "VFS: Attempt to write to location " << location
01181 << " in file " << file_id
01182 << " type " << S32(file_type)
01183 << " of size " << block->mSize
01184 << " block length " << block->mLength
01185 << llendl;
01186 unlockData();
01187 return length;
01188 }
01189 else
01190 {
01191 if (length > block->mLength - location )
01192 {
01193 llwarns << "VFS: Truncating write to virtual file " << file_id << " type " << S32(file_type) << llendl;
01194 length = block->mLength - location;
01195 }
01196 U32 file_location = location + block->mLocation;
01197
01198 unlockData();
01199
01200 fseek(mDataFP, file_location, SEEK_SET);
01201 S32 write_len = (S32)fwrite(buffer, 1, length, mDataFP);
01202 if (write_len != length)
01203 {
01204 llwarns << llformat("VFS Write Error: %d != %d",write_len,length) << llendl;
01205 }
01206
01207
01208 lockData();
01209 if (location + length > block->mSize)
01210 {
01211 block->mSize = location + write_len;
01212 sync(block);
01213 }
01214 unlockData();
01215
01216 return write_len;
01217 }
01218 }
01219 else
01220 {
01221 unlockData();
01222 return 0;
01223 }
01224 }
01225
01226 void LLVFS::incLock(const LLUUID &file_id, const LLAssetType::EType file_type, EVFSLock lock)
01227 {
01228 lockData();
01229
01230 LLVFSFileSpecifier spec(file_id, file_type);
01231 LLVFSFileBlock *block;
01232
01233 fileblock_map::iterator it = mFileBlocks.find(spec);
01234 if (it != mFileBlocks.end())
01235 {
01236 block = (*it).second;
01237 }
01238 else
01239 {
01240
01241 block = new LLVFSFileBlock(file_id, file_type, 0, BLOCK_LENGTH_INVALID);
01242 block->mAccessTime = (U32)time(NULL);
01243 mFileBlocks.insert(fileblock_map::value_type(spec, block));
01244 }
01245
01246 block->mLocks[lock]++;
01247 mLockCounts[lock]++;
01248
01249 unlockData();
01250 }
01251
01252 void LLVFS::decLock(const LLUUID &file_id, const LLAssetType::EType file_type, EVFSLock lock)
01253 {
01254 lockData();
01255
01256 LLVFSFileSpecifier spec(file_id, file_type);
01257 fileblock_map::iterator it = mFileBlocks.find(spec);
01258 if (it != mFileBlocks.end())
01259 {
01260 LLVFSFileBlock *block = (*it).second;
01261
01262 if (block->mLocks[lock] > 0)
01263 {
01264 block->mLocks[lock]--;
01265 }
01266 else
01267 {
01268 llwarns << "VFS: Decrementing zero-value lock " << lock << llendl;
01269 }
01270 mLockCounts[lock]--;
01271 }
01272
01273 unlockData();
01274 }
01275
01276 BOOL LLVFS::isLocked(const LLUUID &file_id, const LLAssetType::EType file_type, EVFSLock lock)
01277 {
01278 lockData();
01279
01280 BOOL res = FALSE;
01281
01282 LLVFSFileSpecifier spec(file_id, file_type);
01283 fileblock_map::iterator it = mFileBlocks.find(spec);
01284 if (it != mFileBlocks.end())
01285 {
01286 LLVFSFileBlock *block = (*it).second;
01287 res = (block->mLocks[lock] > 0);
01288 }
01289
01290 unlockData();
01291
01292 return res;
01293 }
01294
01295
01296
01297
01298
01299 void LLVFS::eraseBlockLength(LLVFSBlock *block)
01300 {
01301
01302 S32 length = block->mLength;
01303 blocks_length_map_t::iterator iter = mFreeBlocksByLength.lower_bound(length);
01304 blocks_length_map_t::iterator end = mFreeBlocksByLength.end();
01305 bool found_block = false;
01306 while(iter != end)
01307 {
01308 LLVFSBlock *tblock = iter->second;
01309 llassert(tblock->mLength == length);
01310 if (tblock == block)
01311 {
01312 mFreeBlocksByLength.erase(iter);
01313 found_block = true;
01314 break;
01315 }
01316 ++iter;
01317 }
01318 if(!found_block)
01319 {
01320 llwarns << "eraseBlock could not find block" << llendl;
01321 }
01322 }
01323
01324
01325
01326 void LLVFS::eraseBlock(LLVFSBlock *block)
01327 {
01328 eraseBlockLength(block);
01329
01330 U32 location = block->mLocation;
01331 llverify(mFreeBlocksByLocation.erase(location) == 1);
01332 }
01333
01334
01335
01336
01337 void LLVFS::addFreeBlock(LLVFSBlock *block)
01338 {
01339 #if LL_DEBUG
01340 size_t dbgcount = mFreeBlocksByLocation.count(block->mLocation);
01341 if(dbgcount > 0)
01342 {
01343 llerrs << "addFreeBlock called with block already in list" << llendl;
01344 }
01345 #endif
01346
01347
01348 blocks_location_map_t::iterator next_free_it = mFreeBlocksByLocation.lower_bound(block->mLocation);
01349
01350
01351 LLVFSBlock* prev_block = NULL;
01352 bool merge_prev = false;
01353 if (next_free_it != mFreeBlocksByLocation.begin())
01354 {
01355 blocks_location_map_t::iterator prev_free_it = next_free_it;
01356 --prev_free_it;
01357 prev_block = prev_free_it->second;
01358 merge_prev = (prev_block->mLocation + prev_block->mLength == block->mLocation);
01359 }
01360
01361
01362 LLVFSBlock* next_block = NULL;
01363 bool merge_next = false;
01364 if (next_free_it != mFreeBlocksByLocation.end())
01365 {
01366 next_block = next_free_it->second;
01367 merge_next = (block->mLocation + block->mLength == next_block->mLocation);
01368 }
01369
01370 if (merge_prev && merge_next)
01371 {
01372
01373
01374
01375 eraseBlockLength(prev_block);
01376 eraseBlock(next_block);
01377 prev_block->mLength += block->mLength + next_block->mLength;
01378 mFreeBlocksByLength.insert(blocks_length_map_t::value_type(prev_block->mLength, prev_block));
01379 delete block;
01380 block = NULL;
01381 delete next_block;
01382 next_block = NULL;
01383 }
01384 else if (merge_prev)
01385 {
01386
01387
01388
01389 eraseBlockLength(prev_block);
01390 prev_block->mLength += block->mLength;
01391 mFreeBlocksByLength.insert(blocks_length_map_t::value_type(prev_block->mLength, prev_block));
01392 delete block;
01393 block = NULL;
01394 }
01395 else if (merge_next)
01396 {
01397
01398
01399
01400 eraseBlock(next_block);
01401 next_block->mLocation = block->mLocation;
01402 next_block->mLength += block->mLength;
01403
01404 mFreeBlocksByLocation.insert(blocks_location_map_t::value_type(next_block->mLocation, next_block));
01405 mFreeBlocksByLength.insert(blocks_length_map_t::value_type(next_block->mLength, next_block));
01406 delete block;
01407 block = NULL;
01408 }
01409 else
01410 {
01411
01412
01413 mFreeBlocksByLocation.insert(next_free_it, blocks_location_map_t::value_type(block->mLocation, block));
01414 mFreeBlocksByLength.insert(blocks_length_map_t::value_type(block->mLength, block));
01415 }
01416 }
01417
01418
01419
01420
01421
01422
01423
01424
01425
01426
01427
01428
01429
01430
01431
01432
01433
01434
01435
01436
01437
01438
01439
01440
01441
01442
01443
01444
01445
01446
01447
01448
01449
01450
01451
01452
01453
01454
01455
01456
01457
01458 void LLVFS::useFreeSpace(LLVFSBlock *free_block, S32 length)
01459 {
01460 if (free_block->mLength == length)
01461 {
01462 eraseBlock(free_block);
01463 delete free_block;
01464 }
01465 else
01466 {
01467 eraseBlock(free_block);
01468
01469 free_block->mLocation += length;
01470 free_block->mLength -= length;
01471
01472 addFreeBlock(free_block);
01473 }
01474 }
01475
01476
01477
01478
01479 void LLVFS::sync(LLVFSFileBlock *block, BOOL remove)
01480 {
01481 if (!isValid())
01482 {
01483 llerrs << "Attempting to use invalid VFS!" << llendl;
01484 }
01485 if (mReadOnly)
01486 {
01487 llwarns << "Attempt to sync read-only VFS" << llendl;
01488 return;
01489 }
01490 if (block->mLength == BLOCK_LENGTH_INVALID)
01491 {
01492
01493 return;
01494 }
01495 if (block->mLength == 0)
01496 {
01497 llerrs << "VFS syncing zero-length block" << llendl;
01498 }
01499
01500 BOOL set_index_to_end = FALSE;
01501 long seek_pos = block->mIndexLocation;
01502
01503 if (-1 == seek_pos)
01504 {
01505 if (!mIndexHoles.empty())
01506 {
01507 seek_pos = mIndexHoles.front();
01508 mIndexHoles.pop_front();
01509 }
01510 else
01511 {
01512 set_index_to_end = TRUE;
01513 }
01514 }
01515
01516 if (set_index_to_end)
01517 {
01518
01519
01520 fseek(mIndexFP, 0, SEEK_END);
01521 seek_pos = ftell(mIndexFP);
01522 }
01523
01524 block->mIndexLocation = seek_pos;
01525 if (remove)
01526 {
01527 mIndexHoles.push_back(seek_pos);
01528 }
01529
01530 U8 buffer[LLVFSFileBlock::SERIAL_SIZE];
01531 if (remove)
01532 {
01533 memset(buffer, 0, LLVFSFileBlock::SERIAL_SIZE);
01534 }
01535 else
01536 {
01537 block->serialize(buffer);
01538 }
01539
01540 unlockData();
01541
01542
01543
01544 if (!set_index_to_end)
01545 {
01546 fseek(mIndexFP, seek_pos, SEEK_SET);
01547 }
01548
01549 if (fwrite(buffer, LLVFSFileBlock::SERIAL_SIZE, 1, mIndexFP) != 1)
01550 {
01551 llwarns << "Short write" << llendl;
01552 }
01553
01554
01555
01556 lockData();
01557
01558 return;
01559 }
01560
01561
01562
01563
01564 LLVFSBlock *LLVFS::findFreeBlock(S32 size, LLVFSFileBlock *immune)
01565 {
01566 if (!isValid())
01567 {
01568 llerrs << "Attempting to use invalid VFS!" << llendl;
01569 }
01570
01571 LLVFSBlock *block = NULL;
01572 BOOL have_lru_list = FALSE;
01573
01574 typedef std::set<LLVFSFileBlock*, LLVFSFileBlock_less> lru_set;
01575 lru_set lru_list;
01576
01577 LLTimer timer;
01578
01579 while (! block)
01580 {
01581
01582 blocks_length_map_t::iterator iter = mFreeBlocksByLength.lower_bound(size);
01583 if (iter != mFreeBlocksByLength.end())
01584 block = iter->second;
01585
01586
01587 if (! block)
01588 {
01589
01590
01591 if (! have_lru_list)
01592 {
01593 for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it)
01594 {
01595 LLVFSFileBlock *tmp = (*it).second;
01596
01597 if (tmp != immune &&
01598 tmp->mLength > 0 &&
01599 ! tmp->mLocks[VFSLOCK_READ] &&
01600 ! tmp->mLocks[VFSLOCK_APPEND] &&
01601 ! tmp->mLocks[VFSLOCK_OPEN])
01602 {
01603 lru_list.insert(tmp);
01604 }
01605 }
01606
01607 have_lru_list = TRUE;
01608 }
01609
01610 if (lru_list.size() == 0)
01611 {
01612
01613 llwarns << "VFS: Can't make " << size << " bytes of free space in VFS, giving up" << llendl;
01614 break;
01615 }
01616
01617
01618 lru_set::iterator it = lru_list.begin();
01619 LLVFSFileBlock *file_block = *it;
01620 if (file_block->mLength >= size && file_block != immune)
01621 {
01622
01623
01624 llinfos << "LRU: Removing " << file_block->mFileID << ":" << file_block->mFileType << llendl;
01625 lru_list.erase(it);
01626 removeFileBlock(file_block);
01627 file_block = NULL;
01628 continue;
01629 }
01630
01631
01632 llinfos << "VFS: LRU: Aggressive: " << (S32)lru_list.size() << " files remain" << llendl;
01633 dumpLockCounts();
01634
01635
01636
01637
01638 U32 cleanup_target = (size > VFS_CLEANUP_SIZE) ? size : VFS_CLEANUP_SIZE;
01639 U32 cleaned_up = 0;
01640 for (it = lru_list.begin();
01641 it != lru_list.end() && cleaned_up < cleanup_target;
01642 )
01643 {
01644 file_block = *it;
01645
01646
01647
01648
01649 cleaned_up += file_block->mLength;
01650 lru_list.erase(it++);
01651 removeFileBlock(file_block);
01652 file_block = NULL;
01653 }
01654
01655 }
01656 }
01657
01658 F32 time = timer.getElapsedTimeF32();
01659 if (time > 0.5f)
01660 {
01661 llwarns << "VFS: Spent " << time << " seconds in findFreeBlock!" << llendl;
01662 }
01663
01664 return block;
01665 }
01666
01667
01668
01669
01670
01671 void LLVFS::pokeFiles()
01672 {
01673 if (!isValid())
01674 {
01675 llerrs << "Attempting to use invalid VFS!" << llendl;
01676 }
01677 U32 word;
01678
01679
01680
01681 fseek(mDataFP, 0, SEEK_SET);
01682 if (fread(&word, sizeof(word), 1, mDataFP) == 1)
01683 {
01684 fseek(mDataFP, 0, SEEK_SET);
01685 if (fwrite(&word, sizeof(word), 1, mDataFP) != 1)
01686 {
01687 llwarns << "Could not write to data file" << llendl;
01688 }
01689 fflush(mDataFP);
01690 }
01691
01692 fseek(mIndexFP, 0, SEEK_SET);
01693 if (fread(&word, sizeof(word), 1, mIndexFP) == 1)
01694 {
01695 fseek(mIndexFP, 0, SEEK_SET);
01696 if (fwrite(&word, sizeof(word), 1, mIndexFP) != 1)
01697 {
01698 llwarns << "Could not write to index file" << llendl;
01699 }
01700 fflush(mIndexFP);
01701 }
01702 }
01703
01704
01705 void LLVFS::dumpMap()
01706 {
01707 llinfos << "Files:" << llendl;
01708 for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it)
01709 {
01710 LLVFSFileBlock *file_block = (*it).second;
01711 llinfos << "Location: " << file_block->mLocation << "\tLength: " << file_block->mLength << "\t" << file_block->mFileID << "\t" << file_block->mFileType << llendl;
01712 }
01713
01714 llinfos << "Free Blocks:" << llendl;
01715 for (blocks_location_map_t::iterator iter = mFreeBlocksByLocation.begin(),
01716 end = mFreeBlocksByLocation.end();
01717 iter != end; iter++)
01718 {
01719 LLVFSBlock *free_block = iter->second;
01720 llinfos << "Location: " << free_block->mLocation << "\tLength: " << free_block->mLength << llendl;
01721 }
01722 }
01723
01724
01725
01726 void LLVFS::audit()
01727 {
01728
01729 LLMutexLock lock_data(mDataMutex);
01730
01731 fflush(mIndexFP);
01732
01733 fseek(mIndexFP, 0, SEEK_END);
01734 long index_size = ftell(mIndexFP);
01735 fseek(mIndexFP, 0, SEEK_SET);
01736
01737 BOOL vfs_corrupt = FALSE;
01738
01739 U8 *buffer = new U8[index_size];
01740
01741 if (fread(buffer, 1, index_size, mIndexFP) != index_size)
01742 {
01743 llwarns << "Index truncated" << llendl;
01744 vfs_corrupt = TRUE;
01745 }
01746
01747 U8 *tmp_ptr = buffer;
01748
01749 std::map<LLVFSFileSpecifier, LLVFSFileBlock*> found_files;
01750 U32 cur_time = (U32)time(NULL);
01751
01752 std::vector<LLVFSFileBlock*> audit_blocks;
01753 while (!vfs_corrupt && tmp_ptr < buffer + index_size)
01754 {
01755 LLVFSFileBlock *block = new LLVFSFileBlock();
01756 audit_blocks.push_back(block);
01757
01758 block->deserialize(tmp_ptr, (S32)(tmp_ptr - buffer));
01759 tmp_ptr += block->SERIAL_SIZE;
01760
01761
01762 if (block->mLength >= 0 &&
01763 block->mSize >= 0 &&
01764 block->mSize <= block->mLength &&
01765 block->mFileType >= LLAssetType::AT_NONE &&
01766 block->mFileType < LLAssetType::AT_COUNT &&
01767 block->mAccessTime <= cur_time &&
01768 block->mFileID != LLUUID::null)
01769 {
01770 if (mFileBlocks.find(*block) == mFileBlocks.end())
01771 {
01772 llwarns << "VFile " << block->mFileID << ":" << block->mFileType << " on disk, not in memory, loc " << block->mIndexLocation << llendl;
01773 }
01774 else if (found_files.find(*block) != found_files.end())
01775 {
01776 std::map<LLVFSFileSpecifier, LLVFSFileBlock*>::iterator it;
01777 it = found_files.find(*block);
01778 LLVFSFileBlock* dupe = it->second;
01779
01780 unlockAndClose(mIndexFP);
01781 mIndexFP = NULL;
01782 unlockAndClose(mDataFP);
01783 mDataFP = NULL;
01784 llwarns << "VFS: Original block index " << block->mIndexLocation
01785 << " location " << block->mLocation
01786 << " length " << block->mLength
01787 << " size " << block->mSize
01788 << " id " << block->mFileID
01789 << " type " << block->mFileType
01790 << llendl;
01791 llwarns << "VFS: Duplicate block index " << dupe->mIndexLocation
01792 << " location " << dupe->mLocation
01793 << " length " << dupe->mLength
01794 << " size " << dupe->mSize
01795 << " id " << dupe->mFileID
01796 << " type " << dupe->mFileType
01797 << llendl;
01798 llwarns << "VFS: Index size " << index_size << llendl;
01799 llwarns << "VFS: INDEX CORRUPT" << llendl;
01800 vfs_corrupt = TRUE;
01801 break;
01802 }
01803 else
01804 {
01805 found_files[*block] = block;
01806 }
01807 }
01808 else
01809 {
01810 if (block->mLength)
01811 {
01812 llwarns << "VFile " << block->mFileID << ":" << block->mFileType << " corrupt on disk" << llendl;
01813 }
01814
01815 }
01816 }
01817
01818 delete[] buffer;
01819
01820 if (!vfs_corrupt)
01821 {
01822 for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it)
01823 {
01824 LLVFSFileBlock* block = (*it).second;
01825
01826 if (block->mSize > 0)
01827 {
01828 if (! found_files.count(*block))
01829 {
01830 llwarns << "VFile " << block->mFileID << ":" << block->mFileType << " in memory, not on disk, loc " << block->mIndexLocation<< llendl;
01831 fseek(mIndexFP, block->mIndexLocation, SEEK_SET);
01832 U8 buf[LLVFSFileBlock::SERIAL_SIZE];
01833 if (fread(buf, LLVFSFileBlock::SERIAL_SIZE, 1, mIndexFP) != 1)
01834 {
01835 llwarns << "VFile " << block->mFileID
01836 << " gave short read" << llendl;
01837 }
01838
01839 LLVFSFileBlock disk_block;
01840 disk_block.deserialize(buf, block->mIndexLocation);
01841
01842 llwarns << "Instead found " << disk_block.mFileID << ":" << block->mFileType << llendl;
01843 }
01844 else
01845 {
01846 block = found_files.find(*block)->second;
01847 found_files.erase(*block);
01848 }
01849 }
01850 }
01851
01852 for (std::map<LLVFSFileSpecifier, LLVFSFileBlock*>::iterator iter = found_files.begin();
01853 iter != found_files.end(); iter++)
01854 {
01855 LLVFSFileBlock* block = iter->second;
01856 llwarns << "VFile " << block->mFileID << ":" << block->mFileType << " szie:" << block->mSize << " leftover" << llendl;
01857 }
01858
01859 llinfos << "VFS: audit OK" << llendl;
01860
01861 }
01862
01863 for_each(audit_blocks.begin(), audit_blocks.end(), DeletePointer());
01864 }
01865
01866
01867
01868
01869 void LLVFS::checkMem()
01870 {
01871 lockData();
01872
01873 for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it)
01874 {
01875 LLVFSFileBlock *block = (*it).second;
01876 llassert(block->mFileType >= LLAssetType::AT_NONE &&
01877 block->mFileType < LLAssetType::AT_COUNT &&
01878 block->mFileID != LLUUID::null);
01879
01880 for (std::deque<S32>::iterator iter = mIndexHoles.begin();
01881 iter != mIndexHoles.end(); ++iter)
01882 {
01883 S32 index_loc = *iter;
01884 if (index_loc == block->mIndexLocation)
01885 {
01886 llwarns << "VFile block " << block->mFileID << ":" << block->mFileType << " is marked as a hole" << llendl;
01887 }
01888 }
01889 }
01890
01891 llinfos << "VFS: mem check OK" << llendl;
01892
01893 unlockData();
01894 }
01895
01896 void LLVFS::dumpLockCounts()
01897 {
01898 S32 i;
01899 for (i = 0; i < VFSLOCK_COUNT; i++)
01900 {
01901 llinfos << "LockType: " << i << ": " << mLockCounts[i] << llendl;
01902 }
01903 }
01904
01905 void LLVFS::dumpStatistics()
01906 {
01907 lockData();
01908
01909
01910 std::map<S32, S32> size_counts;
01911 std::map<U32, S32> location_counts;
01912 std::map<LLAssetType::EType, std::pair<S32,S32> > filetype_counts;
01913
01914 S32 max_file_size = 0;
01915 S32 total_file_size = 0;
01916 S32 invalid_file_count = 0;
01917 for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it)
01918 {
01919 LLVFSFileBlock *file_block = (*it).second;
01920 if (file_block->mLength == BLOCK_LENGTH_INVALID)
01921 {
01922 invalid_file_count++;
01923 }
01924 else if (file_block->mLength <= 0)
01925 {
01926 llinfos << "Bad file block at: " << file_block->mLocation << "\tLength: " << file_block->mLength << "\t" << file_block->mFileID << "\t" << file_block->mFileType << llendl;
01927 size_counts[file_block->mLength]++;
01928 location_counts[file_block->mLocation]++;
01929 }
01930 else
01931 {
01932 total_file_size += file_block->mLength;
01933 }
01934
01935 if (file_block->mLength > max_file_size)
01936 {
01937 max_file_size = file_block->mLength;
01938 }
01939
01940 filetype_counts[file_block->mFileType].first++;
01941 filetype_counts[file_block->mFileType].second += file_block->mLength;
01942 }
01943
01944 for (std::map<S32,S32>::iterator it = size_counts.begin(); it != size_counts.end(); ++it)
01945 {
01946 S32 size = it->first;
01947 S32 size_count = it->second;
01948 llinfos << "Bad files size " << size << " count " << size_count << llendl;
01949 }
01950 for (std::map<U32,S32>::iterator it = location_counts.begin(); it != location_counts.end(); ++it)
01951 {
01952 U32 location = it->first;
01953 S32 location_count = it->second;
01954 llinfos << "Bad files location " << location << " count " << location_count << llendl;
01955 }
01956
01957
01958 S32 max_free_size = 0;
01959 S32 total_free_size = 0;
01960 std::map<S32, S32> free_length_counts;
01961 for (blocks_location_map_t::iterator iter = mFreeBlocksByLocation.begin(),
01962 end = mFreeBlocksByLocation.end();
01963 iter != end; iter++)
01964 {
01965 LLVFSBlock *free_block = iter->second;
01966 if (free_block->mLength <= 0)
01967 {
01968 llinfos << "Bad free block at: " << free_block->mLocation << "\tLength: " << free_block->mLength << llendl;
01969 }
01970 else
01971 {
01972 llinfos << "Block: " << free_block->mLocation
01973 << "\tLength: " << free_block->mLength
01974 << "\tEnd: " << free_block->mLocation + free_block->mLength
01975 << llendl;
01976 total_free_size += free_block->mLength;
01977 }
01978
01979 if (free_block->mLength > max_free_size)
01980 {
01981 max_free_size = free_block->mLength;
01982 }
01983
01984 free_length_counts[free_block->mLength]++;
01985 }
01986
01987
01988 for (std::map<S32,S32>::iterator it = free_length_counts.begin(); it != free_length_counts.end(); ++it)
01989 {
01990 llinfos << "Free length " << it->first << " count " << it->second << llendl;
01991 }
01992
01993 llinfos << "Invalid blocks: " << invalid_file_count << llendl;
01994 llinfos << "File blocks: " << mFileBlocks.size() << llendl;
01995
01996 S32 length_list_count = (S32)mFreeBlocksByLength.size();
01997 S32 location_list_count = (S32)mFreeBlocksByLocation.size();
01998 if (length_list_count == location_list_count)
01999 {
02000 llinfos << "Free list lengths match, free blocks: " << location_list_count << llendl;
02001 }
02002 else
02003 {
02004 llwarns << "Free list lengths do not match!" << llendl;
02005 llwarns << "By length: " << length_list_count << llendl;
02006 llwarns << "By location: " << location_list_count << llendl;
02007 }
02008 llinfos << "Max file: " << max_file_size/1024 << "K" << llendl;
02009 llinfos << "Max free: " << max_free_size/1024 << "K" << llendl;
02010 llinfos << "Total file size: " << total_file_size/1024 << "K" << llendl;
02011 llinfos << "Total free size: " << total_free_size/1024 << "K" << llendl;
02012 llinfos << "Sum: " << (total_file_size + total_free_size) << " bytes" << llendl;
02013 llinfos << llformat("%.0f%% full",((F32)(total_file_size)/(F32)(total_file_size+total_free_size))*100.f) << llendl;
02014
02015 llinfos << " " << llendl;
02016 for (std::map<LLAssetType::EType, std::pair<S32,S32> >::iterator iter = filetype_counts.begin();
02017 iter != filetype_counts.end(); ++iter)
02018 {
02019 llinfos << "Type: " << LLAssetType::getDesc(iter->first)
02020 << " Count: " << iter->second.first
02021 << " Bytes: " << (iter->second.second>>20) << " MB" << llendl;
02022 }
02023
02024
02025 {
02026 blocks_location_map_t::iterator iter = mFreeBlocksByLocation.begin();
02027 blocks_location_map_t::iterator end = mFreeBlocksByLocation.end();
02028 LLVFSBlock *first_block = iter->second;
02029 while(iter != end)
02030 {
02031 if (++iter == end)
02032 break;
02033 LLVFSBlock *second_block = iter->second;
02034 if (first_block->mLocation + first_block->mLength == second_block->mLocation)
02035 {
02036 llinfos << "Potential merge at " << first_block->mLocation << llendl;
02037 }
02038 first_block = second_block;
02039 }
02040 }
02041 unlockData();
02042 }
02043
02044
02045 LLString get_extension(LLAssetType::EType type)
02046 {
02047 LLString extension;
02048 switch(type)
02049 {
02050 case LLAssetType::AT_TEXTURE:
02051 extension = ".j2c";
02052 break;
02053 case LLAssetType::AT_SOUND:
02054 extension = ".ogg";
02055 break;
02056 case LLAssetType::AT_SOUND_WAV:
02057 extension = ".wav";
02058 break;
02059 case LLAssetType::AT_TEXTURE_TGA:
02060 extension = ".tga";
02061 break;
02062 case LLAssetType::AT_IMAGE_JPEG:
02063 extension = ".jpeg";
02064 break;
02065 case LLAssetType::AT_ANIMATION:
02066 extension = ".lla";
02067 break;
02068 default:
02069 extension = ".data";
02070 break;
02071 }
02072 return extension;
02073 }
02074
02075 void LLVFS::listFiles()
02076 {
02077 lockData();
02078
02079 for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it)
02080 {
02081 LLVFSFileSpecifier file_spec = it->first;
02082 LLVFSFileBlock *file_block = it->second;
02083 S32 length = file_block->mLength;
02084 S32 size = file_block->mSize;
02085 if (length != BLOCK_LENGTH_INVALID && size > 0)
02086 {
02087 LLUUID id = file_spec.mFileID;
02088 LLString extension = get_extension(file_spec.mFileType);
02089 llinfos << " File: " << id
02090 << " Type: " << LLAssetType::getDesc(file_spec.mFileType)
02091 << " Size: " << size
02092 << llendl;
02093 }
02094 }
02095
02096 unlockData();
02097 }
02098
02099 #include "llapr.h"
02100 void LLVFS::dumpFiles()
02101 {
02102 lockData();
02103
02104 for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it)
02105 {
02106 LLVFSFileSpecifier file_spec = it->first;
02107 LLVFSFileBlock *file_block = it->second;
02108 S32 length = file_block->mLength;
02109 S32 size = file_block->mSize;
02110 if (length != BLOCK_LENGTH_INVALID && size > 0)
02111 {
02112 LLUUID id = file_spec.mFileID;
02113 LLAssetType::EType type = file_spec.mFileType;
02114 U8* buffer = new U8[size];
02115
02116 unlockData();
02117 getData(id, type, buffer, 0, size);
02118 lockData();
02119
02120 LLString extension = get_extension(type);
02121 LLString filename = id.asString() + extension;
02122 llinfos << " Writing " << filename << llendl;
02123 apr_file_t* file = ll_apr_file_open(filename, LL_APR_WB);
02124 ll_apr_file_write(file, buffer, size);
02125 apr_file_close(file);
02126 delete[] buffer;
02127 }
02128 }
02129
02130 unlockData();
02131 }
02132
02133
02134
02135
02136
02137
02138 LLFILE *LLVFS::openAndLock(const char *filename, const char *mode, BOOL read_lock)
02139 {
02140 #if LL_WINDOWS
02141
02142 return LLFile::_fsopen(filename, mode, (read_lock ? _SH_DENYWR : _SH_DENYRW));
02143
02144 #else
02145
02146 LLFILE *fp;
02147 int fd;
02148
02149
02150 #if LL_SOLARIS
02151 struct flock fl;
02152 fl.l_whence = SEEK_SET;
02153 fl.l_start = 0;
02154 fl.l_len = 1;
02155 #else // !LL_SOLARIS
02156 if (strstr(mode, "w"))
02157 {
02158 fp = LLFile::fopen(filename, "rb");
02159 if (fp)
02160 {
02161 fd = fileno(fp);
02162 if (flock(fd, (read_lock ? LOCK_SH : LOCK_EX) | LOCK_NB) == -1)
02163 {
02164 fclose(fp);
02165 return NULL;
02166 }
02167
02168 fclose(fp);
02169 }
02170 }
02171 #endif // !LL_SOLARIS
02172
02173
02174 fp = LLFile::fopen(filename, mode);
02175 if (fp)
02176 {
02177 fd = fileno(fp);
02178 #if LL_SOLARIS
02179 fl.l_type = read_lock ? F_RDLCK : F_WRLCK;
02180 if (fcntl(fd, F_SETLK, &fl) == -1)
02181 #else
02182 if (flock(fd, (read_lock ? LOCK_SH : LOCK_EX) | LOCK_NB) == -1)
02183 #endif
02184 {
02185 fclose(fp);
02186 fp = NULL;
02187 }
02188 }
02189
02190 return fp;
02191
02192 #endif
02193 }
02194
02195
02196 void LLVFS::unlockAndClose(LLFILE *fp)
02197 {
02198 if (fp)
02199 {
02200
02201
02202
02203
02204
02205
02206
02207
02208
02209
02210
02211 #if LL_SOLARIS
02212 struct flock fl;
02213 fl.l_whence = SEEK_SET;
02214 fl.l_start = 0;
02215 fl.l_len = 1;
02216 fl.l_type = F_UNLCK;
02217 fcntl(fileno(fp), F_SETLK, &fl);
02218 #endif
02219 fclose(fp);
02220 }
02221 }