123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407 |
- /**
- * @file llfilesystem.cpp
- * @brief Implementation of the local file system.
- *
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2020, Linden Research, Inc. (c) 2021 Henri Beauchamp.
- *
- * Modifications by Henri Beauchamp:
- * - Pointless per-asset-type file naming removed.
- * - Cached filename for faster operations.
- * - Use of faster LLFile operations where possible.
- * - Fixed various bugs in write operations. Removed the pointless READ_WRITE
- * mode, added the OVERWRITE one, and changed seek() to auto-padding files
- * with zeros in WRITE mode when seeking past the end of an existing file.
- * - Real time tracking of bytes added to/removed from cache.
- * - Proper cache validity verification.
- * - Immediate date-stamping on creation of LLFileSystem instances, to prevent
- * potential race conditions with the threaded cache purging mechanism.
- * - Multiple threads and multiple viewer instances deconfliction.
- * - Added LLFile::sFlushOnWrite to work around a bug in Wine (*) which
- * reports a wrong file position after non flushed writes. (*) This is for
- * people perverted enough to run a Windows build under Wine under Linux
- * instead of a Linux native build: yes, I'm perverted since I do it to test
- * Windows builds under Linux... :-P
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
- #include "linden_common.h"
- #include "llfilesystem.h"
- #include "lldiskcache.h"
- LLFileSystem::LLFileSystem(const LLUUID& id, S32 mode, const char* extra_info)
- : mFileID(id),
- mMode(mode),
- mPosition(0),
- mBytesRead(0),
- mTotalBytesWritten(0),
- mFilename(LLDiskCache::getFilePath(id, extra_info)),
- mValid(LLDiskCache::isValid())
- {
- if (extra_info && *extra_info)
- {
- mExtraInfo.assign(extra_info);
- }
- mExists = mValid && LLFile::exists(mFilename);
- if (mExists)
- {
- // Update the last access time for the file since this is the way the
- // cache works; it relies on a valid "last accessed time" for each file
- // so that it knows how to remove the oldest, unused files.
- // Since LLFileSystem instances are short-lived, we update the file
- // access time on construction (which also allows to update that time
- // at once during cache purging, preventing an old file that would be
- // reused to get purged in between LLFileSystem instance construction
- // and its actual usage). HB
- LLDiskCache::updateFileAccessTime(mFilename);
- }
- // In append mode, we always write to the end of file, so make sure to
- // initialize the current position there... HB
- if (mExists && mMode == APPEND)
- {
- mPosition = LLFile::getFileSize(mFilename);
- }
- }
- LLFileSystem::~LLFileSystem()
- {
- if (mTotalBytesWritten)
- {
- // Inform the disk cache about how much bytes we added or removed. HB
- LLDiskCache::addBytesWritten(mTotalBytesWritten);
- }
- }
- bool LLFileSystem::read(U8* buffer, S32 bytes)
- {
- if (!mValid || bytes < 0 || !buffer)
- {
- return false;
- }
- if (!bytes)
- {
- mExists = LLFile::isfile(mFilename);
- return mExists;
- }
- LLFILE* file = LLFile::open(mFilename, "rb");
- mExists = file != NULL;
- if (!mExists)
- {
- return false;
- }
- if (mPosition > 0)
- {
- fseek(file, mPosition, SEEK_SET);
- }
- mBytesRead = fread(buffer, 1, bytes, file);
- LLFile::close(file);
- if (mBytesRead > 0)
- {
- mPosition += mBytesRead;
- // Short reads are also considered a success (needed due to how
- // buffered reads are implemented in the viewer code such as in,
- // for example, LLAssetStorage::legacyGetDataCallback())... HB
- return true;
- }
- return false;
- }
- bool LLFileSystem::write(const U8* buffer, S32 bytes)
- {
- if (!mValid)
- {
- return false;
- }
- if (mMode == APPEND)
- {
- // Write to file, appending to it if it already exists.
- LLFILE* file = LLFile::open(mFilename, "a+b");
- if (file)
- {
- fwrite((void*)buffer, 1, bytes, file);
- if (LLFile::sFlushOnWrite)
- {
- fflush(file);
- }
- mPosition = ftell(file);
- LLFile::close(file);
- mTotalBytesWritten += bytes;
- mExists = true;
- return true;
- }
- }
- else if (mMode == OVERWRITE)
- {
- // Discard any existing contents and write.
- mTotalBytesWritten -= LLFile::getFileSize(mFilename);
- LLFILE* file = LLFile::open(mFilename, "wb");
- if (file)
- {
- fwrite((void*)buffer, 1, bytes, file);
- if (LLFile::sFlushOnWrite)
- {
- fflush(file);
- }
- mPosition = ftell(file);
- LLFile::close(file);
- mTotalBytesWritten += bytes;
- mExists = true;
- return true;
- }
- }
- else if (mMode == WRITE)
- {
- // Write at current position, without truncating
- S32 size = LLFile::getFileSize(mFilename); // Remember current size
- mExists = size > 0;
- const char* mode = mExists ? "r+b" : "wb";
- LLFILE* file = LLFile::open(mFilename, mode);
- if (file)
- {
- if (mExists && mPosition > 0)
- {
- fseek(file, mPosition, SEEK_SET);
- }
- fwrite((void*)buffer, 1, bytes, file);
- if (LLFile::sFlushOnWrite)
- {
- fflush(file);
- }
- mPosition = ftell(file);
- LLFile::close(file);
- if (mPosition > size)
- {
- mTotalBytesWritten += mPosition - size;
- }
- mExists = true;
- return true;
- }
- }
- else
- {
- llerrs << "Cannot write in READ mode." << llendl;
- }
- mExists = false;
- return false;
- }
- bool LLFileSystem::seek(S32 offset, S32 origin)
- {
- if (!mValid)
- {
- return false;
- }
- if (mMode == OVERWRITE || mMode == APPEND)
- {
- llerrs << "Cannot seek in file before writing into it in mode "
- << (mMode == APPEND ? "APPEND" : "OVERWRITE") << llendl;
- }
- if (origin < 0)
- {
- origin = mPosition;
- }
- S32 new_pos = origin + offset;
- S32 size = LLFile::getFileSize(mFilename);
- if (new_pos > size)
- {
- if (mMode == READ)
- {
- llwarns << "Attempt to seek past end of file: " << mFilename
- << llendl;
- mPosition = size;
- return false;
- }
- else // Append zeros to the file up to the new position. HB
- {
- mPosition = size;
- LLFILE* file = LLFile::open(mFilename, "a+b");
- if (!file)
- {
- llwarns << "Attempt to seek past end of file \"" << mFilename
- << "\", and could not open it to pad it with zeros."
- << llendl;
- return false;
- }
- mExists = true;
- size_t bytes = new_pos - size;
- char* buffer = new (std::nothrow) char[bytes];
- if (buffer)
- {
- LL_DEBUGS("FileSystem") << "Appending " << bytes
- << " padding bytes to: " << mFilename
- << LL_ENDL;
- memset((void*)buffer, 0, bytes);
- fwrite((void*)buffer, 1, bytes, file);
- if (LLFile::sFlushOnWrite)
- {
- fflush(file);
- }
- mPosition = ftell(file);
- mTotalBytesWritten += mPosition - size;
- delete[] buffer;
- }
- LLFile::close(file);
- if (mPosition == new_pos)
- {
- return true;
- }
- llwarns << "Could not append enough padding bytes to seek to position: "
- << size << " in \"" << mFilename << "\" (position "
- << mPosition << " reached)." << llendl;
- return false;
- }
- }
- if (new_pos < 0)
- {
- llwarns << "Attempt to seek past beginning of file: " << mFilename
- << llendl;
- mPosition = 0;
- return false;
- }
- mPosition = new_pos;
- return true;
- }
- S32 LLFileSystem::getSize() const
- {
- return mValid ? (S32)LLFile::getFileSize(mFilename) : 0;
- }
- bool LLFileSystem::remove()
- {
- if (!mValid)
- {
- return false;
- }
- mExists = false;
- llstat st;
- if (LLFile::stat(mFilename, &st))
- {
- // No such file, we are done.
- return true;
- }
- mTotalBytesWritten -= st.st_size;
- return LLFile::remove(mFilename);
- }
- bool LLFileSystem::rename(const LLUUID& new_id)
- {
- mFileID = new_id;
- if (!mValid)
- {
- return false;
- }
- std::string newfname = LLDiskCache::getFilePath(new_id,
- mExtraInfo.c_str());
- // First remove the new file when it exists
- llstat st;
- if (LLFile::stat(newfname, &st) == 0)
- {
- mTotalBytesWritten -= st.st_size;
- LLFile::remove(newfname);
- }
- // Note: this call may fail and will appropriately warn in the log...
- mExists = LLFile::rename(mFilename, newfname);
- mFilename = newfname;
- return mExists;
- }
- //static
- bool LLFileSystem::getExists(const LLUUID& id, const char* extra_info)
- {
- if (!LLDiskCache::isValid())
- {
- return false;
- }
- return LLFile::isfile(LLDiskCache::getFilePath(id, extra_info));
- }
- //static
- S32 LLFileSystem::getFileSize(const LLUUID& id, const char* extra_info)
- {
- if (!LLDiskCache::isValid())
- {
- return 0;
- }
- return LLFile::getFileSize(LLDiskCache::getFilePath(id, extra_info));
- }
- //static
- bool LLFileSystem::removeFile(const LLUUID& id, const char* extra_info)
- {
- if (!LLDiskCache::isValid())
- {
- return false;
- }
- std::string filename = LLDiskCache::getFilePath(id, extra_info);
- llstat st;
- if (LLFile::stat(filename, &st))
- {
- // No such file, we are done.
- return true;
- }
- if (st.st_size)
- {
- LLDiskCache::addBytesWritten(-st.st_size);
- }
- return LLFile::remove(filename);
- }
- //static
- bool LLFileSystem::renameFile(const LLUUID& old_id, const LLUUID& new_id,
- const char* extra_info)
- {
- if (!LLDiskCache::isValid())
- {
- return false;
- }
- std::string old_filename = LLDiskCache::getFilePath(old_id, extra_info);
- std::string new_filename = LLDiskCache::getFilePath(new_id, extra_info);
- // First remove the new file when it exists
- llstat st;
- if (LLFile::stat(old_filename, &st) == 0)
- {
- if (st.st_size)
- {
- LLDiskCache::addBytesWritten(-st.st_size);
- }
- LLFile::remove(new_filename);
- }
- // Note: this call may fail and will appropriately warn in the log...
- return LLFile::rename(old_filename, new_filename);
- }
|