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 }