00001
00032 #include "linden_common.h"
00033
00034 #include "llvfile.h"
00035
00036 #include "llerror.h"
00037 #include "llthread.h"
00038 #include "llvfs.h"
00039
00040 const S32 LLVFile::READ = 0x00000001;
00041 const S32 LLVFile::WRITE = 0x00000002;
00042 const S32 LLVFile::READ_WRITE = 0x00000003;
00043 const S32 LLVFile::APPEND = 0x00000006;
00044
00045
00046 LLVFSThread* LLVFile::sVFSThread = NULL;
00047 BOOL LLVFile::sAllocdVFSThread = FALSE;
00048
00049
00050
00051
00052 LLVFile::LLVFile(LLVFS *vfs, const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode)
00053 {
00054 mFileType = file_type;
00055
00056 mFileID = file_id;
00057 mPosition = 0;
00058 mMode = mode;
00059 mVFS = vfs;
00060
00061 mBytesRead = 0;
00062 mHandle = LLVFSThread::nullHandle();
00063 mPriority = 128.f;
00064
00065 mVFS->incLock(mFileID, mFileType, VFSLOCK_OPEN);
00066 }
00067
00068 LLVFile::~LLVFile()
00069 {
00070 if (!isReadComplete())
00071 {
00072 if (mHandle != LLVFSThread::nullHandle())
00073 {
00074 if (!(mMode & LLVFile::WRITE))
00075 {
00076
00077 sVFSThread->setFlags(mHandle, LLVFSThread::FLAG_AUTO_COMPLETE | LLVFSThread::FLAG_ABORT);
00078 }
00079 else
00080 {
00081 sVFSThread->setFlags(mHandle, LLVFSThread::FLAG_AUTO_COMPLETE);
00082 }
00083 }
00084 }
00085 mVFS->decLock(mFileID, mFileType, VFSLOCK_OPEN);
00086 }
00087
00088 BOOL LLVFile::read(U8 *buffer, S32 bytes, BOOL async, F32 priority)
00089 {
00090 if (! (mMode & READ))
00091 {
00092 llwarns << "Attempt to read from file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl;
00093 return FALSE;
00094 }
00095
00096 if (mHandle != LLVFSThread::nullHandle())
00097 {
00098 llwarns << "Attempt to read from vfile object " << mFileID << " with pending async operation" << llendl;
00099 return FALSE;
00100 }
00101 mPriority = priority;
00102
00103 BOOL success = TRUE;
00104
00105
00106 waitForLock(VFSLOCK_APPEND);
00107
00108
00109 if (async)
00110 {
00111 mHandle = sVFSThread->read(mVFS, mFileID, mFileType, buffer, mPosition, bytes, threadPri());
00112 }
00113 else
00114 {
00115
00116 mBytesRead = sVFSThread->readImmediate(mVFS, mFileID, mFileType, buffer, mPosition, bytes);
00117 mPosition += mBytesRead;
00118 if (! mBytesRead)
00119 {
00120 success = FALSE;
00121 }
00122 }
00123
00124 return success;
00125 }
00126
00127
00128 U8* LLVFile::readFile(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, S32* bytes_read)
00129 {
00130 U8 *data;
00131 LLVFile file(vfs, uuid, type, LLVFile::READ);
00132 S32 file_size = file.getSize();
00133 if (file_size == 0)
00134 {
00135
00136 data = NULL;
00137 }
00138 else
00139 {
00140 data = new U8[file_size];
00141 file.read(data, file_size);
00142
00143 if (file.getLastBytesRead() != (S32)file_size)
00144 {
00145 delete[] data;
00146 data = NULL;
00147 file_size = 0;
00148 }
00149 }
00150 if (bytes_read)
00151 {
00152 *bytes_read = file_size;
00153 }
00154 return data;
00155 }
00156
00157 void LLVFile::setReadPriority(const F32 priority)
00158 {
00159 mPriority = priority;
00160 if (mHandle != LLVFSThread::nullHandle())
00161 {
00162 sVFSThread->setPriority(mHandle, threadPri());
00163 }
00164 }
00165
00166 BOOL LLVFile::isReadComplete()
00167 {
00168 BOOL res = TRUE;
00169 if (mHandle != LLVFSThread::nullHandle())
00170 {
00171 LLVFSThread::Request* req = (LLVFSThread::Request*)sVFSThread->getRequest(mHandle);
00172 LLVFSThread::status_t status = req->getStatus();
00173 if (status == LLVFSThread::STATUS_COMPLETE)
00174 {
00175 mBytesRead = req->getBytesRead();
00176 mPosition += mBytesRead;
00177 sVFSThread->completeRequest(mHandle);
00178 mHandle = LLVFSThread::nullHandle();
00179 }
00180 else
00181 {
00182 res = FALSE;
00183 }
00184 }
00185 return res;
00186 }
00187
00188 S32 LLVFile::getLastBytesRead()
00189 {
00190 return mBytesRead;
00191 }
00192
00193 BOOL LLVFile::eof()
00194 {
00195 return mPosition >= getSize();
00196 }
00197
00198 BOOL LLVFile::write(const U8 *buffer, S32 bytes)
00199 {
00200 if (! (mMode & WRITE))
00201 {
00202 llwarns << "Attempt to write to file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl;
00203 }
00204 if (mHandle != LLVFSThread::nullHandle())
00205 {
00206 llerrs << "Attempt to write to vfile object " << mFileID << " with pending async operation" << llendl;
00207 return FALSE;
00208 }
00209 BOOL success = TRUE;
00210
00211
00212 if (mMode == APPEND)
00213 {
00214 U8* writebuf = new U8[bytes];
00215 memcpy(writebuf, buffer, bytes);
00216 S32 offset = -1;
00217 mHandle = sVFSThread->write(mVFS, mFileID, mFileType,
00218 writebuf, offset, bytes,
00219 LLVFSThread::FLAG_AUTO_COMPLETE | LLVFSThread::FLAG_AUTO_DELETE);
00220 mHandle = LLVFSThread::nullHandle();
00221 }
00222 else
00223 {
00224
00225 waitForLock(VFSLOCK_READ);
00226 waitForLock(VFSLOCK_APPEND);
00227
00228 S32 pos = (mMode & APPEND) == APPEND ? -1 : mPosition;
00229
00230 S32 wrote = sVFSThread->writeImmediate(mVFS, mFileID, mFileType, (U8*)buffer, pos, bytes);
00231
00232 mPosition += wrote;
00233
00234 if (wrote < bytes)
00235 {
00236 llwarns << "Tried to write " << bytes << " bytes, actually wrote " << wrote << llendl;
00237
00238 success = FALSE;
00239 }
00240 }
00241 return success;
00242 }
00243
00244
00245 BOOL LLVFile::writeFile(const U8 *buffer, S32 bytes, LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type)
00246 {
00247 LLVFile file(vfs, uuid, type, LLVFile::WRITE);
00248 file.setMaxSize(bytes);
00249 return file.write(buffer, bytes);
00250 }
00251
00252 BOOL LLVFile::seek(S32 offset, S32 origin)
00253 {
00254 if (mMode == APPEND)
00255 {
00256 llwarns << "Attempt to seek on append-only file" << llendl;
00257 return FALSE;
00258 }
00259
00260 if (-1 == origin)
00261 {
00262 origin = mPosition;
00263 }
00264
00265 S32 new_pos = origin + offset;
00266
00267 S32 size = getSize();
00268
00269 if (new_pos > size)
00270 {
00271 llwarns << "Attempt to seek past end of file" << llendl;
00272
00273 mPosition = size;
00274 return FALSE;
00275 }
00276 else if (new_pos < 0)
00277 {
00278 llwarns << "Attempt to seek past beginning of file" << llendl;
00279
00280 mPosition = 0;
00281 return FALSE;
00282 }
00283
00284 mPosition = new_pos;
00285 return TRUE;
00286 }
00287
00288 S32 LLVFile::tell() const
00289 {
00290 return mPosition;
00291 }
00292
00293 S32 LLVFile::getSize()
00294 {
00295 waitForLock(VFSLOCK_APPEND);
00296 S32 size = mVFS->getSize(mFileID, mFileType);
00297
00298 return size;
00299 }
00300
00301 S32 LLVFile::getMaxSize()
00302 {
00303 S32 size = mVFS->getMaxSize(mFileID, mFileType);
00304
00305 return size;
00306 }
00307
00308 BOOL LLVFile::setMaxSize(S32 size)
00309 {
00310 if (! (mMode & WRITE))
00311 {
00312 llwarns << "Attempt to change size of file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl;
00313
00314 return FALSE;
00315 }
00316
00317 if (!mVFS->checkAvailable(size))
00318 {
00319 LLFastTimer t(LLFastTimer::FTM_VFILE_WAIT);
00320 S32 count = 0;
00321 while (sVFSThread->getPending() > 1000)
00322 {
00323 if (count % 100 == 0)
00324 {
00325 llinfos << "VFS catching up... Pending: " << sVFSThread->getPending() << llendl;
00326 }
00327 if (sVFSThread->isPaused())
00328 {
00329 sVFSThread->update(0);
00330 }
00331 ms_sleep(10);
00332 }
00333 }
00334 return mVFS->setMaxSize(mFileID, mFileType, size);
00335 }
00336
00337 BOOL LLVFile::rename(const LLUUID &new_id, const LLAssetType::EType new_type)
00338 {
00339 if (! (mMode & WRITE))
00340 {
00341 llwarns << "Attempt to rename file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl;
00342
00343 return FALSE;
00344 }
00345
00346 if (mHandle != LLVFSThread::nullHandle())
00347 {
00348 llwarns << "Renaming file with pending async read" << llendl;
00349 }
00350
00351 waitForLock(VFSLOCK_READ);
00352 waitForLock(VFSLOCK_APPEND);
00353
00354
00355
00356 mVFS->decLock(mFileID, mFileType, VFSLOCK_OPEN);
00357 mVFS->renameFile(mFileID, mFileType, new_id, new_type);
00358 mVFS->incLock(new_id, new_type, VFSLOCK_OPEN);
00359
00360 mFileID = new_id;
00361 mFileType = new_type;
00362
00363 return TRUE;
00364 }
00365
00366 BOOL LLVFile::remove()
00367 {
00368
00369
00370 if (! (mMode & WRITE))
00371 {
00372
00373
00374 llwarns << "Remove file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl;
00375 }
00376
00377 if (mHandle != LLVFSThread::nullHandle())
00378 {
00379 llwarns << "Removing file with pending async read" << llendl;
00380 }
00381
00382
00383 mPosition = 0;
00384
00385 waitForLock(VFSLOCK_READ);
00386 waitForLock(VFSLOCK_APPEND);
00387 mVFS->removeFile(mFileID, mFileType);
00388
00389 return TRUE;
00390 }
00391
00392
00393 void LLVFile::initClass(LLVFSThread* vfsthread)
00394 {
00395 if (!vfsthread)
00396 {
00397 if (LLVFSThread::sLocal != NULL)
00398 {
00399 vfsthread = LLVFSThread::sLocal;
00400 }
00401 else
00402 {
00403 vfsthread = new LLVFSThread();
00404 sAllocdVFSThread = TRUE;
00405 }
00406 }
00407 sVFSThread = vfsthread;
00408 }
00409
00410
00411 void LLVFile::cleanupClass()
00412 {
00413 if (sAllocdVFSThread)
00414 {
00415 delete sVFSThread;
00416 }
00417 sVFSThread = NULL;
00418 }
00419
00420 bool LLVFile::isLocked(EVFSLock lock)
00421 {
00422 return mVFS->isLocked(mFileID, mFileType, lock) ? true : false;
00423 }
00424
00425 void LLVFile::waitForLock(EVFSLock lock)
00426 {
00427 LLFastTimer t(LLFastTimer::FTM_VFILE_WAIT);
00428
00429 while (isLocked(lock))
00430 {
00431 if (sVFSThread->isPaused())
00432 {
00433 sVFSThread->update(0);
00434 }
00435 ms_sleep(1);
00436 }
00437 }