Newer
Older
Callum Prentice
committed
/**
Callum Prentice
committed
* @file filesystem.h
* @brief Simulate local file system operations.
* @Note The initial implementation does actually use standard C++
* file operations but eventually, there will be another
* layer that caches and manages file meta data too.
Callum Prentice
committed
*
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
Callum Prentice
committed
*
Callum Prentice
committed
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
Callum Prentice
committed
*
Callum Prentice
committed
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
Callum Prentice
committed
*
Callum Prentice
committed
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Callum Prentice
committed
*
Callum Prentice
committed
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
Callum Prentice
committed
#include "lldir.h"
#include "llfilesystem.h"
Callum Prentice
committed
#include "llfasttimer.h"
Callum Prentice
committed
#include "lldiskcache.h"
Callum Prentice
committed
Callum Prentice
committed
static LLTrace::BlockTimerStatHandle FTM_VFILE_WAIT("VFile Wait");
Callum Prentice
committed
LLFileSystem::LLFileSystem(const LLUUID& file_id, const LLAssetType::EType file_type, S32 mode)
Callum Prentice
committed
{
Callum Prentice
committed
mFileType = file_type;
mFileID = file_id;
mPosition = 0;
Callum Prentice
committed
mBytesRead = 0;
Callum Prentice
committed
mMode = mode;
Callum Prentice
committed
}
Callum Prentice
committed
// static
bool LLFileSystem::getExists(const LLUUID& file_id, const LLAssetType::EType file_type)
Callum Prentice
committed
{
const std::string filename = LLDiskCache::getInstance()->metaDataToFilepath(file_id, file_type);
Callum Prentice
committed
Mnikolenko Productengine
committed
llifstream file(filename, std::ios::binary);
Callum Prentice
committed
if (file.is_open())
Callum Prentice
committed
{
Callum Prentice
committed
file.seekg(0, std::ios::end);
return file.tellg() > 0;
Callum Prentice
committed
}
Callum Prentice
committed
return false;
Callum Prentice
committed
}
// static
Callum Prentice
committed
bool LLFileSystem::removeFile(const LLUUID& file_id, const LLAssetType::EType file_type)
Callum Prentice
committed
{
const std::string filename = LLDiskCache::getInstance()->metaDataToFilepath(file_id, file_type);
Callum Prentice
committed

Rye Mutt
committed
LLFile::remove(filename, ENOENT);
Callum Prentice
committed
return true;
}
// static
Callum Prentice
committed
bool LLFileSystem::renameFile(const LLUUID& old_file_id, const LLAssetType::EType old_file_type,
const LLUUID& new_file_id, const LLAssetType::EType new_file_type)
Callum Prentice
committed
{
const std::string old_filename = LLDiskCache::getInstance()->metaDataToFilepath(old_file_id, old_file_type);
const std::string new_filename = LLDiskCache::getInstance()->metaDataToFilepath(new_file_id, new_file_type);
Callum Prentice
committed
Mnikolenko Productengine
committed
// Rename needs the new file to not exist.

Rye Mutt
committed
LLFile::remove(new_filename, ENOENT);
Mnikolenko Productengine
committed
Mnikolenko Productengine
committed
if (LLFile::rename(old_filename, new_filename) != 0)
Callum Prentice
committed
{
// We would like to return FALSE here indicating the operation
// failed but the original code does not and doing so seems to
Callum Prentice
committed
// break a lot of things so we go with the flow...
Callum Prentice
committed
//return FALSE;
LL_WARNS() << "Failed to rename " << old_file_id << " to " << new_file_id << " reason: " << strerror(errno) << LL_ENDL;
Callum Prentice
committed
}
return TRUE;
}
// static
Callum Prentice
committed
S32 LLFileSystem::getFileSize(const LLUUID& file_id, const LLAssetType::EType file_type)
Callum Prentice
committed
{
const std::string filename = LLDiskCache::getInstance()->metaDataToFilepath(file_id, file_type);
Callum Prentice
committed
S32 file_size = 0;
Mnikolenko Productengine
committed
llifstream file(filename, std::ios::binary);
Callum Prentice
committed
if (file.is_open())
{
file.seekg(0, std::ios::end);
file_size = file.tellg();
}
return file_size;
}
Callum Prentice
committed
BOOL LLFileSystem::read(U8* buffer, S32 bytes)
Callum Prentice
committed
{
Callum Prentice
committed
BOOL success = TRUE;
Callum Prentice
committed
const std::string filename = LLDiskCache::getInstance()->metaDataToFilepath(mFileID, mFileType);
Callum Prentice
committed
Mnikolenko Productengine
committed
llifstream file(filename, std::ios::binary);
Callum Prentice
committed
if (file.is_open())
{
file.seekg(mPosition, std::ios::beg);
file.read((char*)buffer, bytes);
if (file)
{
mBytesRead = bytes;
}
else
{
mBytesRead = file.gcount();
}
file.close();
mPosition += mBytesRead;
if (!mBytesRead)
{
success = FALSE;
}
}
Callum Prentice
committed
// update the last access time for the file - this is required
Callum Prentice
committed
// even though we are reading and not writing because this is the
// way the cache works - it relies on a valid "last accessed time" for
// each file so it knows how to remove the oldest, unused files

Rye Mutt
committed
updateFileAccessTime(filename);
Callum Prentice
committed
Callum Prentice
committed
return success;
}
Callum Prentice
committed
S32 LLFileSystem::getLastBytesRead()
Callum Prentice
committed
{
Callum Prentice
committed
return mBytesRead;
Callum Prentice
committed
}
Callum Prentice
committed
BOOL LLFileSystem::eof()
Callum Prentice
committed
{
Callum Prentice
committed
return mPosition >= getSize();
Callum Prentice
committed
}
Callum Prentice
committed
BOOL LLFileSystem::write(const U8* buffer, S32 bytes)
Callum Prentice
committed
{
const std::string filename = LLDiskCache::getInstance()->metaDataToFilepath(mFileID, mFileType);
Callum Prentice
committed
BOOL success = FALSE;
if (mMode == APPEND)
{
Mnikolenko Productengine
committed
llofstream ofs(filename, std::ios::app | std::ios::binary);
Callum Prentice
committed
if (ofs)
{
ofs.write((const char*)buffer, bytes);
success = TRUE;
}
}
else
{
Mnikolenko Productengine
committed
llofstream ofs(filename, std::ios::binary);
Callum Prentice
committed
if (ofs)
{
ofs.write((const char*)buffer, bytes);
mPosition += bytes;
success = TRUE;
}
}
return success;
}
Callum Prentice
committed
BOOL LLFileSystem::seek(S32 offset, S32 origin)
Callum Prentice
committed
{
Callum Prentice
committed
if (-1 == origin)
{
origin = mPosition;
}
Callum Prentice
committed
Callum Prentice
committed
S32 new_pos = origin + offset;
Callum Prentice
committed
Callum Prentice
committed
S32 size = getSize();
Callum Prentice
committed
Callum Prentice
committed
if (new_pos > size)
{
LL_WARNS() << "Attempt to seek past end of file" << LL_ENDL;
Callum Prentice
committed
Callum Prentice
committed
mPosition = size;
return FALSE;
}
else if (new_pos < 0)
{
LL_WARNS() << "Attempt to seek past beginning of file" << LL_ENDL;
Callum Prentice
committed
Callum Prentice
committed
mPosition = 0;
return FALSE;
}
Callum Prentice
committed
Callum Prentice
committed
mPosition = new_pos;
return TRUE;
Callum Prentice
committed
}
Callum Prentice
committed
S32 LLFileSystem::tell() const
Callum Prentice
committed
{
Callum Prentice
committed
return mPosition;
Callum Prentice
committed
}
Callum Prentice
committed
S32 LLFileSystem::getSize()
Callum Prentice
committed
{
Callum Prentice
committed
return LLFileSystem::getFileSize(mFileID, mFileType);
Callum Prentice
committed
}
Callum Prentice
committed
S32 LLFileSystem::getMaxSize()
Callum Prentice
committed
{
Callum Prentice
committed
// offer up a huge size since we don't care what the max is
Callum Prentice
committed
return INT_MAX;
}
Callum Prentice
committed
BOOL LLFileSystem::rename(const LLUUID& new_id, const LLAssetType::EType new_type)
Callum Prentice
committed
{
Callum Prentice
committed
LLFileSystem::renameFile(mFileID, mFileType, new_id, new_type);
Callum Prentice
committed
mFileID = new_id;
mFileType = new_type;
return TRUE;
}
Callum Prentice
committed
BOOL LLFileSystem::remove()
Callum Prentice
committed
{
Callum Prentice
committed
LLFileSystem::removeFile(mFileID, mFileType);
Callum Prentice
committed
return TRUE;
}

Rye Mutt
committed
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
void LLFileSystem::updateFileAccessTime(const std::string& file_path)
{
/**
* Threshold in time_t units that is used to decide if the last access time
* time of the file is updated or not. Added as a precaution for the concern
* outlined in SL-14582 about frequent writes on older SSDs reducing their
* lifespan. I think this is the right place for the threshold value - rather
* than it being a pref - do comment on that Jira if you disagree...
*
* Let's start with 1 hour in time_t units and see how that unfolds
*/
const std::time_t time_threshold = 1 * 60 * 60;
// current time
const std::time_t cur_time = std::time(nullptr);
#if LL_WINDOWS
boost::filesystem::path path(ll_convert_string_to_wide(file_path));
#else
boost::filesystem::path path(file_path);
#endif
// file last write time
const std::time_t last_write_time = boost::filesystem::last_write_time(path);
// delta between cur time and last time the file was written
const std::time_t delta_time = cur_time - last_write_time;
// we only write the new value if the time in time_threshold has elapsed
// before the last one
if (delta_time > time_threshold)
{
boost::filesystem::last_write_time(path, cur_time);
}
}