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 llerrs << "Memory Allocation Failure" << llendl;
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 llwarns << "Can't find " << mDataFilename << " to open read-only VFS" << llendl;
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 llwarns << "Can't open VFS data file " << mDataFilename << " attempting to use alternate" << llendl;
00278
00279 char *temp_index = new char[strlen(mIndexFilename) + 10];
00280 if (!temp_index)
00281 {
00282 llerrs << "Out of the memory in LLVFS::LLVFS(const char *index_filename, const char *data_filename, const BOOL read_only, const U32 presize, const BOOL remove_after_crash)" << llendl;
00283 return;
00284 }
00285 char *temp_data = new char[strlen(mDataFilename) + 10];
00286 if (!temp_data)
00287 {
00288 llerrs << "Out of the memory in LLVFS::LLVFS(const char *index_filename, const char *data_filename, const BOOL read_only, const U32 presize, const BOOL remove_after_crash)" << llendl;
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 llwarns << "Couldn't open vfs data file after trying many alternates" << llendl;
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 llerrs << "Out of memory in LLVFS::LLVFS(const char *index_filename, const char *data_filename, const BOOL read_only, const U32 presize, const BOOL remove_after_crash)" << llendl;
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 llwarns << "VFS: File left open on last run, removing old VFS file " << mDataFilename << llendl;
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 llwarns << "Can't open VFS data file in crash recovery" << llendl;
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 llwarns << "VFS corruption: " << block->mFileID << " (" << block->mFileType << ") at index " << block->mIndexLocation << " DS: " << data_size << llendl;
00418 llwarns << "Length: " << block->mLength << "\tLocation: " << block->mLocation << "\tSize: " << block->mSize << llendl;
00419 llwarns << "File has bad data - VFS removed" << llendl;
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 llwarns << "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 << llendl;
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 llwarns << "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 << llendl;
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 llwarns << "Can't find " << mIndexFilename << " to open read-only VFS" << llendl;
00567 mValid = VFSVALID_BAD_CANNOT_OPEN_READONLY;
00568 return;
00569 }
00570
00571
00572 mIndexFP = openAndLock(mIndexFilename, "w+b", FALSE);
00573 if (!mIndexFP)
00574 {
00575 llwarns << "Couldn't open an index file for the VFS, probably a sharing violation!" << llendl;
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 llerrs << "Out of memory in LLVFS::LLVFS(const char *index_filename, const char *data_filename, const BOOL read_only, const U32 presize, const BOOL remove_after_crash)" << llendl;
00597 return;
00598 }
00599 sprintf(marker, "%s.open", mDataFilename);
00600 FILE* 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 llinfos << "VFS: Using index file " << mIndexFilename << " and data file " << mDataFilename << llendl;
00611
00612 mValid = VFSVALID_OK;
00613 }
00614
00615 LLVFS::~LLVFS()
00616 {
00617 if (mDataMutex->isLocked())
00618 {
00619 llerrs << "LLVFS destroyed with mutex locked" << llendl;
00620 }
00621
00622 unlockAndClose(mIndexFP);
00623 mIndexFP = NULL;
00624
00625 fileblock_map::const_iterator it;
00626 for (it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it)
00627 {
00628 delete (*it).second;
00629 }
00630 mFileBlocks.clear();
00631
00632 mFreeBlocksByLength.clear();
00633
00634 for_each(mFreeBlocksByLocation.begin(), mFreeBlocksByLocation.end(), DeletePairedPointer());
00635
00636 unlockAndClose(mDataFP);
00637 mDataFP = NULL;
00638
00639
00640 if (!mReadOnly && mRemoveAfterCrash)
00641 {
00642 char* marker_file = new char[strlen(mDataFilename) + strlen(".open") + 1];
00643 if (marker_file == NULL)
00644 {
00645 llerrs << "Memory Allocation Failure" << llendl;
00646 return;
00647 }
00648 sprintf(marker_file, "%s.open", mDataFilename);
00649 LLFile::remove(marker_file);
00650 delete [] marker_file;
00651 marker_file = NULL;
00652 }
00653
00654 delete[] mIndexFilename;
00655 mIndexFilename = NULL;
00656 delete[] mDataFilename;
00657 mDataFilename = NULL;
00658
00659 delete mDataMutex;
00660 }
00661
00662 void LLVFS::presizeDataFile(const U32 size)
00663 {
00664 if (!mDataFP)
00665 {
00666 llerrs << "LLVFS::presizeDataFile() with no data file open" << llendl;
00667 return;
00668 }
00669
00670
00671 fseek(mDataFP, size-1, SEEK_SET);
00672 S32 tmp = 0;
00673 tmp = (S32)fwrite(&tmp, 1, 1, mDataFP);
00674
00675
00676
00677 LLFile::remove(mIndexFilename);
00678
00679 if (tmp)
00680 {
00681 llinfos << "Pre-sized VFS data file to " << ftell(mDataFP) << " bytes" << llendl;
00682 }
00683 else
00684 {
00685 llwarns << "Failed to pre-size VFS data file" << llendl;
00686 }
00687 }
00688
00689 BOOL LLVFS::getExists(const LLUUID &file_id, const LLAssetType::EType file_type)
00690 {
00691 LLVFSFileBlock *block = NULL;
00692
00693 if (!isValid())
00694 {
00695 llerrs << "Attempting to use invalid VFS!" << llendl;
00696 }
00697
00698 lockData();
00699
00700 LLVFSFileSpecifier spec(file_id, file_type);
00701 fileblock_map::iterator it = mFileBlocks.find(spec);
00702 if (it != mFileBlocks.end())
00703 {
00704 block = (*it).second;
00705 block->mAccessTime = (U32)time(NULL);
00706 }
00707
00708 BOOL res = (block && block->mLength > 0) ? TRUE : FALSE;
00709
00710 unlockData();
00711
00712 return res;
00713 }
00714
00715 S32 LLVFS::getSize(const LLUUID &file_id, const LLAssetType::EType file_type)
00716 {
00717 S32 size = 0;
00718
00719 if (!isValid())
00720 {
00721 llerrs << "Attempting to use invalid VFS!" << llendl;
00722
00723 }
00724
00725 lockData();
00726
00727 LLVFSFileSpecifier spec(file_id, file_type);
00728 fileblock_map::iterator it = mFileBlocks.find(spec);
00729 if (it != mFileBlocks.end())
00730 {
00731 LLVFSFileBlock *block = (*it).second;
00732
00733 block->mAccessTime = (U32)time(NULL);
00734 size = block->mSize;
00735 }
00736
00737 unlockData();
00738
00739 return size;
00740 }
00741
00742 S32 LLVFS::getMaxSize(const LLUUID &file_id, const LLAssetType::EType file_type)
00743 {
00744 S32 size = 0;
00745
00746 if (!isValid())
00747 {
00748 llerrs << "Attempting to use invalid VFS!" << llendl;
00749 }
00750
00751 lockData();
00752
00753 LLVFSFileSpecifier spec(file_id, file_type);
00754 fileblock_map::iterator it = mFileBlocks.find(spec);
00755 if (it != mFileBlocks.end())
00756 {
00757 LLVFSFileBlock *block = (*it).second;
00758
00759 block->mAccessTime = (U32)time(NULL);
00760 size = block->mLength;
00761 }
00762
00763 unlockData();
00764
00765 return size;
00766 }
00767
00768 BOOL LLVFS::checkAvailable(S32 max_size)
00769 {
00770 blocks_length_map_t::iterator iter = mFreeBlocksByLength.lower_bound(max_size);
00771 return (iter == mFreeBlocksByLength.end()) ? FALSE : TRUE;
00772 }
00773
00774 BOOL LLVFS::setMaxSize(const LLUUID &file_id, const LLAssetType::EType file_type, S32 max_size)
00775 {
00776 if (!isValid())
00777 {
00778 llerrs << "Attempting to use invalid VFS!" << llendl;
00779 }
00780 if (mReadOnly)
00781 {
00782 llerrs << "Attempt to write to read-only VFS" << llendl;
00783 }
00784 if (max_size <= 0)
00785 {
00786 llwarns << "VFS: Attempt to assign size " << max_size << " to vfile " << file_id << llendl;
00787 return FALSE;
00788 }
00789
00790 lockData();
00791
00792 LLVFSFileSpecifier spec(file_id, file_type);
00793 LLVFSFileBlock *block = NULL;
00794 fileblock_map::iterator it = mFileBlocks.find(spec);
00795 if (it != mFileBlocks.end())
00796 {
00797 block = (*it).second;
00798 }
00799
00800
00801
00802
00803 if (file_type != LLAssetType::AT_TEXTURE)
00804 {
00805 if (max_size & FILE_BLOCK_MASK)
00806 {
00807 max_size += FILE_BLOCK_MASK;
00808 max_size &= ~FILE_BLOCK_MASK;
00809 }
00810 }
00811
00812 if (block && block->mLength > 0)
00813 {
00814 block->mAccessTime = (U32)time(NULL);
00815
00816 if (max_size == block->mLength)
00817 {
00818 unlockData();
00819 return TRUE;
00820 }
00821 else if (max_size < block->mLength)
00822 {
00823
00824 LLVFSBlock *free_block = new LLVFSBlock(block->mLocation + max_size, block->mLength - max_size);
00825
00826 addFreeBlock(free_block);
00827
00828 block->mLength = max_size;
00829
00830 if (block->mLength < block->mSize)
00831 {
00832
00833 llerrs << "Truncating virtual file " << file_id << " to " << block->mLength << " bytes" << llendl;
00834 block->mSize = block->mLength;
00835 }
00836
00837 sync(block);
00838
00839
00840 unlockData();
00841 return TRUE;
00842 }
00843 else if (max_size > block->mLength)
00844 {
00845
00846
00847 S32 size_increase = max_size - block->mLength;
00848
00849
00850 LLVFSBlock *free_block;
00851 blocks_location_map_t::iterator iter = mFreeBlocksByLocation.upper_bound(block->mLocation);
00852 if (iter != mFreeBlocksByLocation.end())
00853 {
00854 free_block = iter->second;
00855
00856 if (free_block->mLocation == block->mLocation + block->mLength &&
00857 free_block->mLength >= size_increase)
00858 {
00859
00860
00861
00862
00863 useFreeSpace(free_block, size_increase);
00864 block->mLength += size_increase;
00865 sync(block);
00866
00867 unlockData();
00868 return TRUE;
00869 }
00870 }
00871
00872
00873 free_block = findFreeBlock(max_size, block);
00874
00875 if (free_block)
00876 {
00877 if (block->mLength > 0)
00878 {
00879
00880 LLVFSBlock *new_free_block = new LLVFSBlock(block->mLocation, block->mLength);
00881
00882 addFreeBlock(new_free_block);
00883
00884 if (block->mSize > 0)
00885 {
00886
00887 U8 *buffer = new U8[block->mSize];
00888 fseek(mDataFP, block->mLocation, SEEK_SET);
00889 if (fread(buffer, block->mSize, 1, mDataFP) == 1)
00890 {
00891 fseek(mDataFP, free_block->mLocation, SEEK_SET);
00892 if (fwrite(buffer, block->mSize, 1, mDataFP) != 1)
00893 {
00894 llwarns << "Short write" << llendl;
00895 }
00896 } else {
00897 llwarns << "Short read" << llendl;
00898 }
00899
00900 delete[] buffer;
00901 }
00902 }
00903
00904 block->mLocation = free_block->mLocation;
00905
00906 block->mLength = max_size;
00907
00908
00909
00910 useFreeSpace(free_block, max_size);
00911
00912 sync(block);
00913
00914 unlockData();
00915 return TRUE;
00916 }
00917 else
00918 {
00919 llwarns << "VFS: No space (" << max_size << ") to resize existing vfile " << file_id << llendl;
00920
00921 unlockData();
00922 dumpStatistics();
00923 return FALSE;
00924 }
00925 }
00926 }
00927 else
00928 {
00929
00930 LLVFSBlock *free_block = findFreeBlock(max_size);
00931
00932 if (free_block)
00933 {
00934 if (block)
00935 {
00936 block->mLocation = free_block->mLocation;
00937 block->mLength = max_size;
00938 }
00939 else
00940 {
00941
00942 block = new LLVFSFileBlock(file_id, file_type, free_block->mLocation, max_size);
00943 mFileBlocks.insert(fileblock_map::value_type(spec, block));
00944 }
00945
00946
00947
00948 useFreeSpace(free_block, max_size);
00949 block->mAccessTime = (U32)time(NULL);
00950
00951 sync(block);
00952 }
00953 else
00954 {
00955 llwarns << "VFS: No space (" << max_size << ") for new virtual file " << file_id << llendl;
00956
00957 unlockData();
00958 dumpStatistics();
00959 return FALSE;
00960 }
00961 }
00962 unlockData();
00963 return TRUE;
00964 }
00965
00966
00967
00968
00969 void LLVFS::renameFile(const LLUUID &file_id, const LLAssetType::EType file_type,
00970 const LLUUID &new_id, const LLAssetType::EType &new_type)
00971 {
00972 if (!isValid())
00973 {
00974 llerrs << "Attempting to use invalid VFS!" << llendl;
00975 }
00976 if (mReadOnly)
00977 {
00978 llerrs << "Attempt to write to read-only VFS" << llendl;
00979 }
00980
00981 lockData();
00982
00983 LLVFSFileSpecifier new_spec(new_id, new_type);
00984 LLVFSFileSpecifier old_spec(file_id, file_type);
00985
00986 fileblock_map::iterator it = mFileBlocks.find(old_spec);
00987 if (it != mFileBlocks.end())
00988 {
00989 LLVFSFileBlock *src_block = (*it).second;
00990
00991
00992
00993 fileblock_map::iterator new_it = mFileBlocks.find(new_spec);
00994 if (new_it != mFileBlocks.end())
00995 {
00996 LLVFSFileBlock *new_block = (*new_it).second;
00997 removeFileBlock(new_block);
00998 }
00999
01000
01001 it = mFileBlocks.find(new_spec);
01002 if (it != mFileBlocks.end())
01003 {
01004 LLVFSFileBlock *dest_block = (*it).second;
01005
01006 for (S32 i = 0; i < (S32)VFSLOCK_COUNT; i++)
01007 {
01008 if(dest_block->mLocks[i])
01009 {
01010 llerrs << "Renaming VFS block to a locked file." << llendl;
01011 }
01012 dest_block->mLocks[i] = src_block->mLocks[i];
01013 }
01014
01015 mFileBlocks.erase(new_spec);
01016 delete dest_block;
01017 }
01018
01019 src_block->mFileID = new_id;
01020 src_block->mFileType = new_type;
01021 src_block->mAccessTime = (U32)time(NULL);
01022
01023 mFileBlocks.erase(old_spec);
01024 mFileBlocks.insert(fileblock_map::value_type(new_spec, src_block));
01025
01026 sync(src_block);
01027 }
01028 else
01029 {
01030 llwarns << "VFS: Attempt to rename nonexistent vfile " << file_id << ":" << file_type << llendl;
01031 }
01032 unlockData();
01033 }
01034
01035
01036 void LLVFS::removeFileBlock(LLVFSFileBlock *fileblock)
01037 {
01038
01039
01040 sync(fileblock, TRUE);
01041
01042 if (fileblock->mLength > 0)
01043 {
01044
01045 LLVFSBlock *free_block = new LLVFSBlock(fileblock->mLocation, fileblock->mLength);
01046
01047 addFreeBlock(free_block);
01048 }
01049
01050 fileblock->mLocation = 0;
01051 fileblock->mSize = 0;
01052 fileblock->mLength = BLOCK_LENGTH_INVALID;
01053 fileblock->mIndexLocation = -1;
01054
01055
01056 }
01057
01058 void LLVFS::removeFile(const LLUUID &file_id, const LLAssetType::EType file_type)
01059 {
01060 if (!isValid())
01061 {
01062 llerrs << "Attempting to use invalid VFS!" << llendl;
01063 }
01064 if (mReadOnly)
01065 {
01066 llerrs << "Attempt to write to read-only VFS" << llendl;
01067 }
01068
01069 lockData();
01070
01071 LLVFSFileSpecifier spec(file_id, file_type);
01072 fileblock_map::iterator it = mFileBlocks.find(spec);
01073 if (it != mFileBlocks.end())
01074 {
01075 LLVFSFileBlock *block = (*it).second;
01076 removeFileBlock(block);
01077 }
01078 else
01079 {
01080 llwarns << "VFS: attempting to remove nonexistent file " << file_id << " type " << file_type << llendl;
01081 }
01082
01083 unlockData();
01084 }
01085
01086
01087 S32 LLVFS::getData(const LLUUID &file_id, const LLAssetType::EType file_type, U8 *buffer, S32 location, S32 length)
01088 {
01089 S32 bytesread = 0;
01090
01091 if (!isValid())
01092 {
01093 llerrs << "Attempting to use invalid VFS!" << llendl;
01094 }
01095 llassert(location >= 0);
01096 llassert(length >= 0);
01097
01098 BOOL do_read = FALSE;
01099
01100 lockData();
01101
01102 LLVFSFileSpecifier spec(file_id, file_type);
01103 fileblock_map::iterator it = mFileBlocks.find(spec);
01104 if (it != mFileBlocks.end())
01105 {
01106 LLVFSFileBlock *block = (*it).second;
01107
01108 block->mAccessTime = (U32)time(NULL);
01109
01110 if (location > block->mSize)
01111 {
01112 llwarns << "VFS: Attempt to read location " << location << " in file " << file_id << " of length " << block->mSize << llendl;
01113 }
01114 else
01115 {
01116 if (length > block->mSize - location)
01117 {
01118 length = block->mSize - location;
01119 }
01120 location += block->mLocation;
01121 do_read = TRUE;
01122 }
01123 }
01124
01125 unlockData();
01126
01127 if (do_read)
01128 {
01129 fseek(mDataFP, location, SEEK_SET);
01130 bytesread = (S32)fread(buffer, 1, length, mDataFP);
01131 }
01132
01133 return bytesread;
01134 }
01135
01136 S32 LLVFS::storeData(const LLUUID &file_id, const LLAssetType::EType file_type, const U8 *buffer, S32 location, S32 length)
01137 {
01138 if (!isValid())
01139 {
01140 llerrs << "Attempting to use invalid VFS!" << llendl;
01141 }
01142 if (mReadOnly)
01143 {
01144 llerrs << "Attempt to write to read-only VFS" << llendl;
01145 }
01146
01147 llassert(length > 0);
01148
01149 lockData();
01150
01151 LLVFSFileSpecifier spec(file_id, file_type);
01152 fileblock_map::iterator it = mFileBlocks.find(spec);
01153 if (it != mFileBlocks.end())
01154 {
01155 LLVFSFileBlock *block = (*it).second;
01156
01157 S32 in_loc = location;
01158 if (location == -1)
01159 {
01160 location = block->mSize;
01161 }
01162 llassert(location >= 0);
01163
01164 block->mAccessTime = (U32)time(NULL);
01165
01166 if (block->mLength == BLOCK_LENGTH_INVALID)
01167 {
01168
01169 llwarns << "VFS: Attempt to write to invalid block"
01170 << " in file " << file_id
01171 << " location: " << in_loc
01172 << " bytes: " << length
01173 << llendl;
01174 unlockData();
01175 return length;
01176 }
01177 else if (location > block->mLength)
01178 {
01179 llwarns << "VFS: Attempt to write to location " << location
01180 << " in file " << file_id
01181 << " type " << S32(file_type)
01182 << " of size " << block->mSize
01183 << " block length " << block->mLength
01184 << llendl;
01185 unlockData();
01186 return length;
01187 }
01188 else
01189 {
01190 if (length > block->mLength - location )
01191 {
01192 llwarns << "VFS: Truncating write to virtual file " << file_id << " type " << S32(file_type) << llendl;
01193 length = block->mLength - location;
01194 }
01195 U32 file_location = location + block->mLocation;
01196
01197 unlockData();
01198
01199 fseek(mDataFP, file_location, SEEK_SET);
01200 S32 write_len = (S32)fwrite(buffer, 1, length, mDataFP);
01201 if (write_len != length)
01202 {
01203 llwarns << llformat("VFS Write Error: %d != %d",write_len,length) << llendl;
01204 }
01205
01206
01207 lockData();
01208 if (location + length > block->mSize)
01209 {
01210 block->mSize = location + write_len;
01211 sync(block);
01212 }
01213 unlockData();
01214
01215 return write_len;
01216 }
01217 }
01218 else
01219 {
01220 unlockData();
01221 return 0;
01222 }
01223 }
01224
01225 void LLVFS::incLock(const LLUUID &file_id, const LLAssetType::EType file_type, EVFSLock lock)
01226 {
01227 lockData();
01228
01229 LLVFSFileSpecifier spec(file_id, file_type);
01230 LLVFSFileBlock *block;
01231
01232 fileblock_map::iterator it = mFileBlocks.find(spec);
01233 if (it != mFileBlocks.end())
01234 {
01235 block = (*it).second;
01236 }
01237 else
01238 {
01239
01240 block = new LLVFSFileBlock(file_id, file_type, 0, BLOCK_LENGTH_INVALID);
01241 block->mAccessTime = (U32)time(NULL);
01242 mFileBlocks.insert(fileblock_map::value_type(spec, block));
01243 }
01244
01245 block->mLocks[lock]++;
01246 mLockCounts[lock]++;
01247
01248 unlockData();
01249 }
01250
01251 void LLVFS::decLock(const LLUUID &file_id, const LLAssetType::EType file_type, EVFSLock lock)
01252 {
01253 lockData();
01254
01255 LLVFSFileSpecifier spec(file_id, file_type);
01256 fileblock_map::iterator it = mFileBlocks.find(spec);
01257 if (it != mFileBlocks.end())
01258 {
01259 LLVFSFileBlock *block = (*it).second;
01260
01261 if (block->mLocks[lock] > 0)
01262 {
01263 block->mLocks[lock]--;
01264 }
01265 else
01266 {
01267 llwarns << "VFS: Decrementing zero-value lock " << lock << llendl;
01268 }
01269 mLockCounts[lock]--;
01270 }
01271
01272 unlockData();
01273 }
01274
01275 BOOL LLVFS::isLocked(const LLUUID &file_id, const LLAssetType::EType file_type, EVFSLock lock)
01276 {
01277 lockData();
01278
01279 BOOL res = FALSE;
01280
01281 LLVFSFileSpecifier spec(file_id, file_type);
01282 fileblock_map::iterator it = mFileBlocks.find(spec);
01283 if (it != mFileBlocks.end())
01284 {
01285 LLVFSFileBlock *block = (*it).second;
01286 res = (block->mLocks[lock] > 0);
01287 }
01288
01289 unlockData();
01290
01291 return res;
01292 }
01293
01294
01295
01296
01297
01298 void LLVFS::eraseBlockLength(LLVFSBlock *block)
01299 {
01300
01301 S32 length = block->mLength;
01302 blocks_length_map_t::iterator iter = mFreeBlocksByLength.lower_bound(length);
01303 blocks_length_map_t::iterator end = mFreeBlocksByLength.end();
01304 bool found_block = false;
01305 while(iter != end)
01306 {
01307 LLVFSBlock *tblock = iter->second;
01308 llassert(tblock->mLength == length);
01309 if (tblock == block)
01310 {
01311 mFreeBlocksByLength.erase(iter);
01312 found_block = true;
01313 break;
01314 }
01315 ++iter;
01316 }
01317 if(!found_block)
01318 {
01319 llwarns << "eraseBlock could not find block" << llendl;
01320 }
01321 }
01322
01323
01324
01325 void LLVFS::eraseBlock(LLVFSBlock *block)
01326 {
01327 eraseBlockLength(block);
01328
01329 U32 location = block->mLocation;
01330 llverify(mFreeBlocksByLocation.erase(location) == 1);
01331 }
01332
01333
01334
01335
01336 void LLVFS::addFreeBlock(LLVFSBlock *block)
01337 {
01338 #if LL_DEBUG
01339 size_t dbgcount = mFreeBlocksByLocation.count(block->mLocation);
01340 if(dbgcount > 0)
01341 {
01342 llerrs << "addFreeBlock called with block already in list" << llendl;
01343 }
01344 #endif
01345
01346
01347 blocks_location_map_t::iterator next_free_it = mFreeBlocksByLocation.lower_bound(block->mLocation);
01348
01349
01350 LLVFSBlock* prev_block = NULL;
01351 bool merge_prev = false;
01352 if (next_free_it != mFreeBlocksByLocation.begin())
01353 {
01354 blocks_location_map_t::iterator prev_free_it = next_free_it;
01355 --prev_free_it;
01356 prev_block = prev_free_it->second;
01357 merge_prev = (prev_block->mLocation + prev_block->mLength == block->mLocation);
01358 }
01359
01360
01361 LLVFSBlock* next_block = NULL;
01362 bool merge_next = false;
01363 if (next_free_it != mFreeBlocksByLocation.end())
01364 {
01365 next_block = next_free_it->second;
01366 merge_next = (block->mLocation + block->mLength == next_block->mLocation);
01367 }
01368
01369 if (merge_prev && merge_next)
01370 {
01371
01372
01373
01374 eraseBlockLength(prev_block);
01375 eraseBlock(next_block);
01376 prev_block->mLength += block->mLength + next_block->mLength;
01377 mFreeBlocksByLength.insert(blocks_length_map_t::value_type(prev_block->mLength, prev_block));
01378 delete block;
01379 block = NULL;
01380 delete next_block;
01381 next_block = NULL;
01382 }
01383 else if (merge_prev)
01384 {
01385
01386
01387
01388 eraseBlockLength(prev_block);
01389 prev_block->mLength += block->mLength;
01390 mFreeBlocksByLength.insert(blocks_length_map_t::value_type(prev_block->mLength, prev_block));
01391 delete block;
01392 block = NULL;
01393 }
01394 else if (merge_next)
01395 {
01396
01397
01398
01399 eraseBlock(next_block);
01400 next_block->mLocation = block->mLocation;
01401 next_block->mLength += block->mLength;
01402
01403 mFreeBlocksByLocation.insert(blocks_location_map_t::value_type(next_block->mLocation, next_block));
01404 mFreeBlocksByLength.insert(blocks_length_map_t::value_type(next_block->mLength, next_block));
01405 delete block;
01406 block = NULL;
01407 }
01408 else
01409 {
01410
01411
01412 mFreeBlocksByLocation.insert(next_free_it, blocks_location_map_t::value_type(block->mLocation, block));
01413 mFreeBlocksByLength.insert(blocks_length_map_t::value_type(block->mLength, block));
01414 }
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 void LLVFS::useFreeSpace(LLVFSBlock *free_block, S32 length)
01458 {
01459 if (free_block->mLength == length)
01460 {
01461 eraseBlock(free_block);
01462 delete free_block;
01463 }
01464 else
01465 {
01466 eraseBlock(free_block);
01467
01468 free_block->mLocation += length;
01469 free_block->mLength -= length;
01470
01471 addFreeBlock(free_block);
01472 }
01473 }
01474
01475
01476
01477
01478 void LLVFS::sync(LLVFSFileBlock *block, BOOL remove)
01479 {
01480 if (!isValid())
01481 {
01482 llerrs << "Attempting to use invalid VFS!" << llendl;
01483 }
01484 if (mReadOnly)
01485 {
01486 llwarns << "Attempt to sync read-only VFS" << llendl;
01487 return;
01488 }
01489 if (block->mLength == BLOCK_LENGTH_INVALID)
01490 {
01491
01492 return;
01493 }
01494 if (block->mLength == 0)
01495 {
01496 llerrs << "VFS syncing zero-length block" << llendl;
01497 }
01498
01499 BOOL set_index_to_end = FALSE;
01500 long seek_pos = block->mIndexLocation;
01501
01502 if (-1 == seek_pos)
01503 {
01504 if (!mIndexHoles.empty())
01505 {
01506 seek_pos = mIndexHoles.front();
01507 mIndexHoles.pop_front();
01508 }
01509 else
01510 {
01511 set_index_to_end = TRUE;
01512 }
01513 }
01514
01515 if (set_index_to_end)
01516 {
01517
01518
01519 fseek(mIndexFP, 0, SEEK_END);
01520 seek_pos = ftell(mIndexFP);
01521 }
01522
01523 block->mIndexLocation = seek_pos;
01524 if (remove)
01525 {
01526 mIndexHoles.push_back(seek_pos);
01527 }
01528
01529 U8 buffer[LLVFSFileBlock::SERIAL_SIZE];
01530 if (remove)
01531 {
01532 memset(buffer, 0, LLVFSFileBlock::SERIAL_SIZE);
01533 }
01534 else
01535 {
01536 block->serialize(buffer);
01537 }
01538
01539 unlockData();
01540
01541
01542
01543 if (!set_index_to_end)
01544 {
01545 fseek(mIndexFP, seek_pos, SEEK_SET);
01546 }
01547
01548 if (fwrite(buffer, LLVFSFileBlock::SERIAL_SIZE, 1, mIndexFP) != 1)
01549 {
01550 llwarns << "Short write" << llendl;
01551 }
01552
01553
01554
01555 lockData();
01556
01557 return;
01558 }
01559
01560
01561
01562
01563 LLVFSBlock *LLVFS::findFreeBlock(S32 size, LLVFSFileBlock *immune)
01564 {
01565 if (!isValid())
01566 {
01567 llerrs << "Attempting to use invalid VFS!" << llendl;
01568 }
01569
01570 LLVFSBlock *block = NULL;
01571 BOOL have_lru_list = FALSE;
01572
01573 typedef std::set<LLVFSFileBlock*, LLVFSFileBlock_less> lru_set;
01574 lru_set lru_list;
01575
01576 LLTimer timer;
01577
01578 while (! block)
01579 {
01580
01581 blocks_length_map_t::iterator iter = mFreeBlocksByLength.lower_bound(size);
01582 if (iter != mFreeBlocksByLength.end())
01583 block = iter->second;
01584
01585
01586 if (! block)
01587 {
01588
01589
01590 if (! have_lru_list)
01591 {
01592 for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it)
01593 {
01594 LLVFSFileBlock *tmp = (*it).second;
01595
01596 if (tmp != immune &&
01597 tmp->mLength > 0 &&
01598 ! tmp->mLocks[VFSLOCK_READ] &&
01599 ! tmp->mLocks[VFSLOCK_APPEND] &&
01600 ! tmp->mLocks[VFSLOCK_OPEN])
01601 {
01602 lru_list.insert(tmp);
01603 }
01604 }
01605
01606 have_lru_list = TRUE;
01607 }
01608
01609 if (lru_list.size() == 0)
01610 {
01611
01612 llwarns << "VFS: Can't make " << size << " bytes of free space in VFS, giving up" << llendl;
01613 break;
01614 }
01615
01616
01617 lru_set::iterator it = lru_list.begin();
01618 LLVFSFileBlock *file_block = *it;
01619 if (file_block->mLength >= size && file_block != immune)
01620 {
01621
01622
01623 llinfos << "LRU: Removing " << file_block->mFileID << ":" << file_block->mFileType << llendl;
01624 lru_list.erase(it);
01625 removeFileBlock(file_block);
01626 file_block = NULL;
01627 continue;
01628 }
01629
01630
01631 llinfos << "VFS: LRU: Aggressive: " << (S32)lru_list.size() << " files remain" << llendl;
01632 dumpLockCounts();
01633
01634
01635
01636
01637 U32 cleanup_target = (size > VFS_CLEANUP_SIZE) ? size : VFS_CLEANUP_SIZE;
01638 U32 cleaned_up = 0;
01639 for (it = lru_list.begin();
01640 it != lru_list.end() && cleaned_up < cleanup_target;
01641 )
01642 {
01643 file_block = *it;
01644
01645
01646
01647
01648 cleaned_up += file_block->mLength;
01649 lru_list.erase(it++);
01650 removeFileBlock(file_block);
01651 file_block = NULL;
01652 }
01653
01654 }
01655 }
01656
01657 F32 time = timer.getElapsedTimeF32();
01658 if (time > 0.5f)
01659 {
01660 llwarns << "VFS: Spent " << time << " seconds in findFreeBlock!" << llendl;
01661 }
01662
01663 return block;
01664 }
01665
01666
01667
01668
01669
01670 void LLVFS::pokeFiles()
01671 {
01672 if (!isValid())
01673 {
01674 llerrs << "Attempting to use invalid VFS!" << llendl;
01675 }
01676 U32 word;
01677
01678
01679
01680 fseek(mDataFP, 0, SEEK_SET);
01681 if (fread(&word, sizeof(word), 1, mDataFP) == 1)
01682 {
01683 fseek(mDataFP, 0, SEEK_SET);
01684 if (fwrite(&word, sizeof(word), 1, mDataFP) != 1)
01685 {
01686 llwarns << "Could not write to data file" << llendl;
01687 }
01688 fflush(mDataFP);
01689 }
01690
01691 fseek(mIndexFP, 0, SEEK_SET);
01692 if (fread(&word, sizeof(word), 1, mIndexFP) == 1)
01693 {
01694 fseek(mIndexFP, 0, SEEK_SET);
01695 if (fwrite(&word, sizeof(word), 1, mIndexFP) != 1)
01696 {
01697 llwarns << "Could not write to index file" << llendl;
01698 }
01699 fflush(mIndexFP);
01700 }
01701 }
01702
01703
01704 void LLVFS::dumpMap()
01705 {
01706 llinfos << "Files:" << llendl;
01707 for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it)
01708 {
01709 LLVFSFileBlock *file_block = (*it).second;
01710 llinfos << "Location: " << file_block->mLocation << "\tLength: " << file_block->mLength << "\t" << file_block->mFileID << "\t" << file_block->mFileType << llendl;
01711 }
01712
01713 llinfos << "Free Blocks:" << llendl;
01714 for (blocks_location_map_t::iterator iter = mFreeBlocksByLocation.begin(),
01715 end = mFreeBlocksByLocation.end();
01716 iter != end; iter++)
01717 {
01718 LLVFSBlock *free_block = iter->second;
01719 llinfos << "Location: " << free_block->mLocation << "\tLength: " << free_block->mLength << llendl;
01720 }
01721 }
01722
01723
01724
01725 void LLVFS::audit()
01726 {
01727
01728 LLMutexLock lock_data(mDataMutex);
01729
01730 fflush(mIndexFP);
01731
01732 fseek(mIndexFP, 0, SEEK_END);
01733 long index_size = ftell(mIndexFP);
01734 fseek(mIndexFP, 0, SEEK_SET);
01735
01736 BOOL vfs_corrupt = FALSE;
01737
01738 U8 *buffer = new U8[index_size];
01739
01740 if (fread(buffer, 1, index_size, mIndexFP) != index_size)
01741 {
01742 llwarns << "Index truncated" << llendl;
01743 vfs_corrupt = TRUE;
01744 }
01745
01746 U8 *tmp_ptr = buffer;
01747
01748 std::map<LLVFSFileSpecifier, LLVFSFileBlock*> found_files;
01749 U32 cur_time = (U32)time(NULL);
01750
01751 std::vector<LLVFSFileBlock*> audit_blocks;
01752 while (!vfs_corrupt && tmp_ptr < buffer + index_size)
01753 {
01754 LLVFSFileBlock *block = new LLVFSFileBlock();
01755 audit_blocks.push_back(block);
01756
01757 block->deserialize(tmp_ptr, (S32)(tmp_ptr - buffer));
01758 tmp_ptr += block->SERIAL_SIZE;
01759
01760
01761 if (block->mLength >= 0 &&
01762 block->mSize >= 0 &&
01763 block->mSize <= block->mLength &&
01764 block->mFileType >= LLAssetType::AT_NONE &&
01765 block->mFileType < LLAssetType::AT_COUNT &&
01766 block->mAccessTime <= cur_time &&
01767 block->mFileID != LLUUID::null)
01768 {
01769 if (mFileBlocks.find(*block) == mFileBlocks.end())
01770 {
01771 llwarns << "VFile " << block->mFileID << ":" << block->mFileType << " on disk, not in memory, loc " << block->mIndexLocation << llendl;
01772 }
01773 else if (found_files.find(*block) != found_files.end())
01774 {
01775 std::map<LLVFSFileSpecifier, LLVFSFileBlock*>::iterator it;
01776 it = found_files.find(*block);
01777 LLVFSFileBlock* dupe = it->second;
01778
01779 unlockAndClose(mIndexFP);
01780 mIndexFP = NULL;
01781 unlockAndClose(mDataFP);
01782 mDataFP = NULL;
01783 llwarns << "VFS: Original block index " << block->mIndexLocation
01784 << " location " << block->mLocation
01785 << " length " << block->mLength
01786 << " size " << block->mSize
01787 << " id " << block->mFileID
01788 << " type " << block->mFileType
01789 << llendl;
01790 llwarns << "VFS: Duplicate block index " << dupe->mIndexLocation
01791 << " location " << dupe->mLocation
01792 << " length " << dupe->mLength
01793 << " size " << dupe->mSize
01794 << " id " << dupe->mFileID
01795 << " type " << dupe->mFileType
01796 << llendl;
01797 llwarns << "VFS: Index size " << index_size << llendl;
01798 llwarns << "VFS: INDEX CORRUPT" << llendl;
01799 vfs_corrupt = TRUE;
01800 break;
01801 }
01802 else
01803 {
01804 found_files[*block] = block;
01805 }
01806 }
01807 else
01808 {
01809 if (block->mLength)
01810 {
01811 llwarns << "VFile " << block->mFileID << ":" << block->mFileType << " corrupt on disk" << llendl;
01812 }
01813
01814 }
01815 }
01816
01817 delete[] buffer;
01818
01819 if (!vfs_corrupt)
01820 {
01821 for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it)
01822 {
01823 LLVFSFileBlock* block = (*it).second;
01824
01825 if (block->mSize > 0)
01826 {
01827 if (! found_files.count(*block))
01828 {
01829 llwarns << "VFile " << block->mFileID << ":" << block->mFileType << " in memory, not on disk, loc " << block->mIndexLocation<< llendl;
01830 fseek(mIndexFP, block->mIndexLocation, SEEK_SET);
01831 U8 buf[LLVFSFileBlock::SERIAL_SIZE];
01832 if (fread(buf, LLVFSFileBlock::SERIAL_SIZE, 1, mIndexFP) != 1)
01833 {
01834 llwarns << "VFile " << block->mFileID
01835 << " gave short read" << llendl;
01836 }
01837
01838 LLVFSFileBlock disk_block;
01839 disk_block.deserialize(buf, block->mIndexLocation);
01840
01841 llwarns << "Instead found " << disk_block.mFileID << ":" << block->mFileType << llendl;
01842 }
01843 else
01844 {
01845 block = found_files.find(*block)->second;
01846 found_files.erase(*block);
01847 }
01848 }
01849 }
01850
01851 for (std::map<LLVFSFileSpecifier, LLVFSFileBlock*>::iterator iter = found_files.begin();
01852 iter != found_files.end(); iter++)
01853 {
01854 LLVFSFileBlock* block = iter->second;
01855 llwarns << "VFile " << block->mFileID << ":" << block->mFileType << " szie:" << block->mSize << " leftover" << llendl;
01856 }
01857
01858 llinfos << "VFS: audit OK" << llendl;
01859
01860 }
01861
01862 for_each(audit_blocks.begin(), audit_blocks.end(), DeletePointer());
01863 }
01864
01865
01866
01867
01868 void LLVFS::checkMem()
01869 {
01870 lockData();
01871
01872 for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it)
01873 {
01874 LLVFSFileBlock *block = (*it).second;
01875 llassert(block->mFileType >= LLAssetType::AT_NONE &&
01876 block->mFileType < LLAssetType::AT_COUNT &&
01877 block->mFileID != LLUUID::null);
01878
01879 for (std::deque<S32>::iterator iter = mIndexHoles.begin();
01880 iter != mIndexHoles.end(); ++iter)
01881 {
01882 S32 index_loc = *iter;
01883 if (index_loc == block->mIndexLocation)
01884 {
01885 llwarns << "VFile block " << block->mFileID << ":" << block->mFileType << " is marked as a hole" << llendl;
01886 }
01887 }
01888 }
01889
01890 llinfos << "VFS: mem check OK" << llendl;
01891
01892 unlockData();
01893 }
01894
01895 void LLVFS::dumpLockCounts()
01896 {
01897 S32 i;
01898 for (i = 0; i < VFSLOCK_COUNT; i++)
01899 {
01900 llinfos << "LockType: " << i << ": " << mLockCounts[i] << llendl;
01901 }
01902 }
01903
01904 void LLVFS::dumpStatistics()
01905 {
01906 lockData();
01907
01908
01909 std::map<S32, S32> size_counts;
01910 std::map<U32, S32> location_counts;
01911 std::map<LLAssetType::EType, std::pair<S32,S32> > filetype_counts;
01912
01913 S32 max_file_size = 0;
01914 S32 total_file_size = 0;
01915 S32 invalid_file_count = 0;
01916 for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it)
01917 {
01918 LLVFSFileBlock *file_block = (*it).second;
01919 if (file_block->mLength == BLOCK_LENGTH_INVALID)
01920 {
01921 invalid_file_count++;
01922 }
01923 else if (file_block->mLength <= 0)
01924 {
01925 llinfos << "Bad file block at: " << file_block->mLocation << "\tLength: " << file_block->mLength << "\t" << file_block->mFileID << "\t" << file_block->mFileType << llendl;
01926 size_counts[file_block->mLength]++;
01927 location_counts[file_block->mLocation]++;
01928 }
01929 else
01930 {
01931 total_file_size += file_block->mLength;
01932 }
01933
01934 if (file_block->mLength > max_file_size)
01935 {
01936 max_file_size = file_block->mLength;
01937 }
01938
01939 filetype_counts[file_block->mFileType].first++;
01940 filetype_counts[file_block->mFileType].second += file_block->mLength;
01941 }
01942
01943 for (std::map<S32,S32>::iterator it = size_counts.begin(); it != size_counts.end(); ++it)
01944 {
01945 S32 size = it->first;
01946 S32 size_count = it->second;
01947 llinfos << "Bad files size " << size << " count " << size_count << llendl;
01948 }
01949 for (std::map<U32,S32>::iterator it = location_counts.begin(); it != location_counts.end(); ++it)
01950 {
01951 U32 location = it->first;
01952 S32 location_count = it->second;
01953 llinfos << "Bad files location " << location << " count " << location_count << llendl;
01954 }
01955
01956
01957 S32 max_free_size = 0;
01958 S32 total_free_size = 0;
01959 std::map<S32, S32> free_length_counts;
01960 for (blocks_location_map_t::iterator iter = mFreeBlocksByLocation.begin(),
01961 end = mFreeBlocksByLocation.end();
01962 iter != end; iter++)
01963 {
01964 LLVFSBlock *free_block = iter->second;
01965 if (free_block->mLength <= 0)
01966 {
01967 llinfos << "Bad free block at: " << free_block->mLocation << "\tLength: " << free_block->mLength << llendl;
01968 }
01969 else
01970 {
01971 llinfos << "Block: " << free_block->mLocation
01972 << "\tLength: " << free_block->mLength
01973 << "\tEnd: " << free_block->mLocation + free_block->mLength
01974 << llendl;
01975 total_free_size += free_block->mLength;
01976 }
01977
01978 if (free_block->mLength > max_free_size)
01979 {
01980 max_free_size = free_block->mLength;
01981 }
01982
01983 free_length_counts[free_block->mLength]++;
01984 }
01985
01986
01987 for (std::map<S32,S32>::iterator it = free_length_counts.begin(); it != free_length_counts.end(); ++it)
01988 {
01989 llinfos << "Free length " << it->first << " count " << it->second << llendl;
01990 }
01991
01992 llinfos << "Invalid blocks: " << invalid_file_count << llendl;
01993 llinfos << "File blocks: " << mFileBlocks.size() << llendl;
01994
01995 S32 length_list_count = (S32)mFreeBlocksByLength.size();
01996 S32 location_list_count = (S32)mFreeBlocksByLocation.size();
01997 if (length_list_count == location_list_count)
01998 {
01999 llinfos << "Free list lengths match, free blocks: " << location_list_count << llendl;
02000 }
02001 else
02002 {
02003 llwarns << "Free list lengths do not match!" << llendl;
02004 llwarns << "By length: " << length_list_count << llendl;
02005 llwarns << "By location: " << location_list_count << llendl;
02006 }
02007 llinfos << "Max file: " << max_file_size/1024 << "K" << llendl;
02008 llinfos << "Max free: " << max_free_size/1024 << "K" << llendl;
02009 llinfos << "Total file size: " << total_file_size/1024 << "K" << llendl;
02010 llinfos << "Total free size: " << total_free_size/1024 << "K" << llendl;
02011 llinfos << "Sum: " << (total_file_size + total_free_size) << " bytes" << llendl;
02012 llinfos << llformat("%.0f%% full",((F32)(total_file_size)/(F32)(total_file_size+total_free_size))*100.f) << llendl;
02013
02014 llinfos << " " << llendl;
02015 for (std::map<LLAssetType::EType, std::pair<S32,S32> >::iterator iter = filetype_counts.begin();
02016 iter != filetype_counts.end(); ++iter)
02017 {
02018 llinfos << "Type: " << LLAssetType::getDesc(iter->first)
02019 << " Count: " << iter->second.first
02020 << " Bytes: " << (iter->second.second>>20) << " MB" << llendl;
02021 }
02022
02023
02024 {
02025 blocks_location_map_t::iterator iter = mFreeBlocksByLocation.begin();
02026 blocks_location_map_t::iterator end = mFreeBlocksByLocation.end();
02027 LLVFSBlock *first_block = iter->second;
02028 while(iter != end)
02029 {
02030 if (++iter == end)
02031 break;
02032 LLVFSBlock *second_block = iter->second;
02033 if (first_block->mLocation + first_block->mLength == second_block->mLocation)
02034 {
02035 llinfos << "Potential merge at " << first_block->mLocation << llendl;
02036 }
02037 first_block = second_block;
02038 }
02039 }
02040 unlockData();
02041 }
02042
02043
02044 LLString get_extension(LLAssetType::EType type)
02045 {
02046 LLString extension;
02047 switch(type)
02048 {
02049 case LLAssetType::AT_TEXTURE:
02050 extension = ".j2c";
02051 break;
02052 case LLAssetType::AT_SOUND:
02053 extension = ".ogg";
02054 break;
02055 case LLAssetType::AT_SOUND_WAV:
02056 extension = ".wav";
02057 break;
02058 case LLAssetType::AT_TEXTURE_TGA:
02059 extension = ".tga";
02060 break;
02061 case LLAssetType::AT_IMAGE_JPEG:
02062 extension = ".jpeg";
02063 break;
02064 case LLAssetType::AT_ANIMATION:
02065 extension = ".lla";
02066 break;
02067 default:
02068 extension = ".data";
02069 break;
02070 }
02071 return extension;
02072 }
02073
02074 void LLVFS::listFiles()
02075 {
02076 lockData();
02077
02078 for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it)
02079 {
02080 LLVFSFileSpecifier file_spec = it->first;
02081 LLVFSFileBlock *file_block = it->second;
02082 S32 length = file_block->mLength;
02083 S32 size = file_block->mSize;
02084 if (length != BLOCK_LENGTH_INVALID && size > 0)
02085 {
02086 LLUUID id = file_spec.mFileID;
02087 LLString extension = get_extension(file_spec.mFileType);
02088 llinfos << " File: " << id
02089 << " Type: " << LLAssetType::getDesc(file_spec.mFileType)
02090 << " Size: " << size
02091 << llendl;
02092 }
02093 }
02094
02095 unlockData();
02096 }
02097
02098 #include "llapr.h"
02099 void LLVFS::dumpFiles()
02100 {
02101 lockData();
02102
02103 for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it)
02104 {
02105 LLVFSFileSpecifier file_spec = it->first;
02106 LLVFSFileBlock *file_block = it->second;
02107 S32 length = file_block->mLength;
02108 S32 size = file_block->mSize;
02109 if (length != BLOCK_LENGTH_INVALID && size > 0)
02110 {
02111 LLUUID id = file_spec.mFileID;
02112 LLAssetType::EType type = file_spec.mFileType;
02113 U8* buffer = new U8[size];
02114
02115 unlockData();
02116 getData(id, type, buffer, 0, size);
02117 lockData();
02118
02119 LLString extension = get_extension(type);
02120 LLString filename = id.asString() + extension;
02121 llinfos << " Writing " << filename << llendl;
02122 apr_file_t* file = ll_apr_file_open(filename, LL_APR_WB);
02123 ll_apr_file_write(file, buffer, size);
02124 apr_file_close(file);
02125 delete[] buffer;
02126 }
02127 }
02128
02129 unlockData();
02130 }
02131
02132
02133
02134
02135
02136
02137 FILE *LLVFS::openAndLock(const char *filename, const char *mode, BOOL read_lock)
02138 {
02139 #if LL_WINDOWS
02140
02141 return LLFile::_fsopen(filename, mode, (read_lock ? _SH_DENYWR : _SH_DENYRW));
02142
02143 #else
02144
02145 FILE *fp;
02146 int fd;
02147
02148
02149 #if LL_SOLARIS
02150 struct flock fl;
02151 fl.l_whence = SEEK_SET;
02152 fl.l_start = 0;
02153 fl.l_len = 1;
02154 #else // !LL_SOLARIS
02155 if (strstr(mode, "w"))
02156 {
02157 fp = LLFile::fopen(filename, "rb");
02158 if (fp)
02159 {
02160 fd = fileno(fp);
02161 if (flock(fd, (read_lock ? LOCK_SH : LOCK_EX) | LOCK_NB) == -1)
02162 {
02163 fclose(fp);
02164 return NULL;
02165 }
02166
02167 fclose(fp);
02168 }
02169 }
02170 #endif // !LL_SOLARIS
02171
02172
02173 fp = LLFile::fopen(filename, mode);
02174 if (fp)
02175 {
02176 fd = fileno(fp);
02177 #if LL_SOLARIS
02178 fl.l_type = read_lock ? F_RDLCK : F_WRLCK;
02179 if (fcntl(fd, F_SETLK, &fl) == -1)
02180 #else
02181 if (flock(fd, (read_lock ? LOCK_SH : LOCK_EX) | LOCK_NB) == -1)
02182 #endif
02183 {
02184 fclose(fp);
02185 fp = NULL;
02186 }
02187 }
02188
02189 return fp;
02190
02191 #endif
02192 }
02193
02194
02195 void LLVFS::unlockAndClose(FILE *fp)
02196 {
02197 if (fp)
02198 {
02199
02200
02201
02202
02203
02204
02205
02206
02207
02208
02209
02210 #if LL_SOLARIS
02211 struct flock fl;
02212 fl.l_whence = SEEK_SET;
02213 fl.l_start = 0;
02214 fl.l_len = 1;
02215 fl.l_type = F_UNLCK;
02216 fcntl(fileno(fp), F_SETLK, &fl);
02217 #endif
02218 fclose(fp);
02219 }
02220 }