summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrederick Martian <fredmartian@gmail.com>2025-11-12 19:19:59 +0100
committerAndrey Kleshchev <117672381+akleshchev@users.noreply.github.com>2025-12-10 20:33:58 +0200
commitf07762a46830005b6ff4218c1f070ce27a9ecebe (patch)
tree7870932ad0c2ec651ed26373a59c8880aaeb733f
parent9756b4b70b3cd24c03142d7e52eb2acae52002ef (diff)
Refactoring of LLFile class to support additional methods
- LLFile with its own class method interface to access files for read and write - Remove rudimentary LLUniqueFile class as LLFile supports now all of that and more - Implement most of the filename based functions using std::filesystem functions - Replace LLFile::rmdir() with LLFile::remove() since this function now supports deleting files and directories on all platforms.
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llerror.cpp10
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llfile.cpp1157
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llfile.h396
-rwxr-xr-x[-rw-r--r--]indra/llcrashlogger/llcrashlock.cpp7
-rwxr-xr-x[-rw-r--r--]indra/llfilesystem/lldir.cpp51
-rwxr-xr-x[-rw-r--r--]indra/llfilesystem/lldir_win32.cpp13
-rwxr-xr-x[-rw-r--r--]indra/llfilesystem/llfilesystem.cpp19
-rwxr-xr-x[-rw-r--r--]indra/llfilesystem/tests/lldir_test.cpp4
-rwxr-xr-x[-rw-r--r--]indra/llrender/llshadermgr.cpp17
-rwxr-xr-x[-rw-r--r--]indra/llxml/tests/llcontrol_test.cpp2
-rwxr-xr-x[-rw-r--r--]indra/newview/llappviewer.cpp10
-rwxr-xr-x[-rw-r--r--]indra/newview/llfloaterpreference.cpp12
-rwxr-xr-x[-rw-r--r--]indra/newview/llfloateruipreview.cpp11
-rwxr-xr-x[-rw-r--r--]indra/newview/llpreviewnotecard.cpp3
-rwxr-xr-x[-rw-r--r--]indra/newview/llpreviewscript.cpp3
-rwxr-xr-x[-rw-r--r--]indra/newview/lltexturecache.cpp2
-rwxr-xr-x[-rw-r--r--]indra/newview/llviewerdisplay.cpp5
-rwxr-xr-x[-rw-r--r--]indra/newview/llviewermedia.cpp5
-rwxr-xr-x[-rw-r--r--]indra/newview/llvocache.cpp2
-rwxr-xr-x[-rw-r--r--]indra/newview/llvoicevivox.cpp3
-rwxr-xr-x[-rw-r--r--]indra/test/llmessageconfig_tut.cpp2
-rwxr-xr-x[-rw-r--r--]indra/test/message_tut.cpp2
22 files changed, 1135 insertions, 601 deletions
diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp
index b14464382b..bfa8bca224 100644..100755
--- a/indra/llcommon/llerror.cpp
+++ b/indra/llcommon/llerror.cpp
@@ -435,13 +435,9 @@ namespace
std::string file = user_dir + "/logcontrol-dev.xml";
- llstat stat_info;
- if (LLFile::stat(file, &stat_info)) {
- // NB: stat returns non-zero if it can't read the file, for example
- // if it doesn't exist. LLFile has no better abstraction for
- // testing for file existence.
-
- file = app_dir + "/logcontrol.xml";
+ if (!LLFile::isfile(file))
+ {
+ file = app_dir + "/logcontrol.xml";
}
return * new LogControlFile(file);
// NB: This instance is never freed
diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp
index 9fff614efb..6ccf01dd78 100644..100755
--- a/indra/llcommon/llfile.cpp
+++ b/indra/llcommon/llfile.cpp
@@ -34,17 +34,13 @@
#include "stringize.h"
#if LL_WINDOWS
-#include "llwin32headers.h"
-#include <vector>
+#include <fcntl.h>
#else
#include <errno.h>
+#include <sys/file.h>
#endif
-using namespace std;
-
-static std::string empty;
-
-// Many of the methods below use OS-level functions that mess with errno. Wrap
+// Some of the methods below use OS-level functions that mess with errno. Wrap
// variants of strerror() to report errors.
#if LL_WINDOWS
@@ -79,6 +75,7 @@ static errentry const errtable[]
{ ERROR_CURRENT_DIRECTORY, EACCES }, // 16
{ ERROR_NOT_SAME_DEVICE, EXDEV }, // 17
{ ERROR_NO_MORE_FILES, ENOENT }, // 18
+ { ERROR_SHARING_VIOLATION, EACCES }, // 32
{ ERROR_LOCK_VIOLATION, EACCES }, // 33
{ ERROR_BAD_NETPATH, ENOENT }, // 53
{ ERROR_NETWORK_ACCESS_DENIED, EACCES }, // 65
@@ -109,22 +106,25 @@ static errentry const errtable[]
{ ERROR_NOT_ENOUGH_QUOTA, ENOMEM } // 1816
};
-static int set_errno_from_oserror(unsigned long oserr)
+static int get_errno_from_oserror(int oserr)
{
if (!oserr)
return 0;
// Check the table for the Windows OS error code
- for (const struct errentry &entry : errtable)
+ for (const struct errentry& entry : errtable)
{
if (oserr == entry.oserr)
{
- _set_errno(entry.errcode);
- return -1;
+ return entry.errcode;
}
}
+ return EINVAL;
+}
- _set_errno(EINVAL);
+static int set_errno_from_oserror(unsigned long oserr)
+{
+ _set_errno(get_errno_from_oserror(oserr));
return -1;
}
@@ -136,72 +136,8 @@ std::string strerr(int errn)
return buffer;
}
-inline bool is_slash(wchar_t const c)
-{
- return c == L'\\' || c == L'/';
-}
-
-static std::wstring utf8path_to_wstring(const std::string& utf8path)
-{
- if (utf8path.size() >= MAX_PATH)
- {
- // By prepending "\\?\" to a path, Windows widechar file APIs will not fail on long path names
- std::wstring utf16path = L"\\\\?\\" + ll_convert<std::wstring>(utf8path);
- // We need to make sure that the path does not contain forward slashes as above
- // prefix does bypass the path normalization that replaces slashes with backslashes
- // before passing the path to kernel mode APIs
- std::replace(utf16path.begin(), utf16path.end(), L'/', L'\\');
- return utf16path;
- }
- return ll_convert<std::wstring>(utf8path);
-}
-// This function retrieves the file attributes as they are used in Posix
-// Like the Posix file functions it returns 0 on success, on failure it returns -1 and sets the errno variable
-// if dontfollowSymlink is true it returns the attributes of the symlink itself if it is one
-static int get_fileattr(const std::wstring& utf16path, unsigned short& st_mode, bool dontFollowSymLink = false)
-{
- unsigned long flags = FILE_FLAG_BACKUP_SEMANTICS; // This allows CreateFile() to open a handle to a directory
- if (dontFollowSymLink)
- {
- // This lets CreateFile() open a handle to a symlink rather than to the resolved target
- flags |= FILE_FLAG_OPEN_REPARSE_POINT;
- }
- HANDLE file_handle = CreateFileW(utf16path.c_str(), FILE_READ_ATTRIBUTES,
- FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
- nullptr, OPEN_EXISTING, flags, nullptr);
- if (file_handle != INVALID_HANDLE_VALUE)
- {
- FILE_ATTRIBUTE_TAG_INFO attribute_info;
- if (GetFileInformationByHandleEx(file_handle, FileAttributeTagInfo, &attribute_info, sizeof(attribute_info)))
- {
- // A volume path alone (only drive letter) is not recognized as directory while it technically is
- bool is_directory = (attribute_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
- (iswalpha(utf16path[0]) && utf16path[1] == ':' &&
- (!utf16path[2] || (is_slash(utf16path[2]) && !utf16path[3])));
- st_mode = is_directory ? S_IFDIR :
- (attribute_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ? S_IFLNK : S_IFREG);
- st_mode |= (attribute_info.FileAttributes & FILE_ATTRIBUTE_READONLY) ? S_IREAD : S_IREAD | S_IWRITE;
- // we do not try to guess the executable flag
-
- // propagate user bits to group/other fields:
- st_mode |= (st_mode & 0700) >> 3;
- st_mode |= (st_mode & 0700) >> 6;
-
- CloseHandle(file_handle);
- return 0;
- }
- }
- // Retrieve last error and set errno before calling CloseHandle()
- int rc = set_errno_from_oserror(GetLastError());
-
- if (file_handle != INVALID_HANDLE_VALUE)
- {
- CloseHandle(file_handle);
- }
- return rc;
-}
-
#else
+
// On Posix we want to call strerror_r(), but alarmingly, there are two
// different variants. The one that returns int always populates the passed
// buffer (except in case of error), whereas the other one always returns a
@@ -248,8 +184,49 @@ std::string strerr(int errn)
return message_from(errn, buffer, sizeof(buffer),
strerror_r(errn, buffer, sizeof(buffer)));
}
+
#endif // ! LL_WINDOWS
+#if LL_WINDOWS && 0 // turn on to debug file-locking problems
+#define PROCESS_LOCKING_CHECK 1
+static void find_locking_process(const std::string& filename)
+{
+ // Only do any of this stuff (before LL_ENDL) if it will be logged.
+ LL_DEBUGS("LLFile") << "";
+ // wrong way
+ std::string TEMP = LLFile::tmpdir();
+ if (TEMP.empty())
+ {
+ LL_CONT << "No $TEMP, not running 'handle'";
+ }
+ else
+ {
+ std::string tf(TEMP);
+ tf += "\\handle.tmp";
+ // http://technet.microsoft.com/en-us/sysinternals/bb896655
+ std::string cmd(STRINGIZE("handle \"" << filename
+ // "openfiles /query /v | fgrep -i \"" << filename
+ << "\" > \"" << tf << '"'));
+ LL_CONT << cmd;
+ if (system(cmd.c_str()) != 0)
+ {
+ LL_CONT << "\nDownload 'handle.exe' from http://technet.microsoft.com/en-us/sysinternals/bb896655";
+ }
+ else
+ {
+ std::ifstream inf(tf);
+ std::string line;
+ while (std::getline(inf, line))
+ {
+ LL_CONT << '\n' << line;
+ }
+ }
+ LLFile::remove(tf);
+ }
+ LL_CONT << LL_ENDL;
+}
+#endif // LL_WINDOWS hack to identify processes holding file open
+
static int warnif(const std::string& desc, const std::string& filename, int rc, int accept = 0)
{
if (rc < 0)
@@ -262,437 +239,905 @@ static int warnif(const std::string& desc, const std::string& filename, int rc,
// EEXIST. Don't warn if caller explicitly says this errno is okay.
if (errn != accept)
{
- LL_WARNS("LLFile") << "Couldn't " << desc << " '" << filename
- << "' (errno " << errn << "): " << strerr(errn) << LL_ENDL;
+ LL_WARNS("LLFile") << "Couldn't " << desc << " '" << filename << "' (errno " << errn << "): " << strerr(errn) << LL_ENDL;
}
-#if 0 && LL_WINDOWS // turn on to debug file-locking problems
+#if PROCESS_LOCKING_CHECK
// If the problem is "Permission denied," maybe it's because another
// process has the file open. Try to find out.
- if (errn == EACCES) // *not* EPERM
+ if (errn == EACCES) // *not* EPERM
{
- // Only do any of this stuff (before LL_ENDL) if it will be logged.
- LL_DEBUGS("LLFile") << empty;
- // would be nice to use LLDir for this, but dependency goes the
- // wrong way
- const char* TEMP = LLFile::tmpdir();
- if (! (TEMP && *TEMP))
- {
- LL_CONT << "No $TEMP, not running 'handle'";
- }
- else
- {
- std::string tf(TEMP);
- tf += "\\handle.tmp";
- // http://technet.microsoft.com/en-us/sysinternals/bb896655
- std::string cmd(STRINGIZE("handle \"" << filename
- // "openfiles /query /v | fgrep -i \"" << filename
- << "\" > \"" << tf << '"'));
- LL_CONT << cmd;
- if (system(cmd.c_str()) != 0)
- {
- LL_CONT << "\nDownload 'handle.exe' from http://technet.microsoft.com/en-us/sysinternals/bb896655";
- }
- else
- {
- std::ifstream inf(tf);
- std::string line;
- while (std::getline(inf, line))
- {
- LL_CONT << '\n' << line;
- }
- }
- LLFile::remove(tf);
- }
- LL_CONT << LL_ENDL;
+ find_locking_process(filename);
}
-#endif // LL_WINDOWS hack to identify processes holding file open
+#endif
}
return rc;
}
-// static
-int LLFile::mkdir(const std::string& dirname, int perms)
+static int warnif(const std::string& desc, const std::string& filename, std::error_code& ec, int accept = 0)
{
- // We often use mkdir() to ensure the existence of a directory that might
- // already exist. There is no known case in which we want to call out as
- // an error the requested directory already existing.
+ if (ec)
+ {
+ // get Posix errno from the std::error_code so we can compare it to the accept parameter to see
+ // when a caller wants us to not generate a warning for a particular error code
+#if LL_WINDOWS
+ int errn = get_errno_from_oserror(ec.value());
+#else
+ int errn = ec.value();
+#endif
+ // For certain operations, a particular errno value might be acceptable
+ // Don't warn if caller explicitly says this errno is okay.
+ if (errn != accept)
+ {
+ LL_WARNS("LLFile") << "Couldn't " << desc << " '" << filename << "' (errno " << errn << "): " << ec.message() << LL_ENDL;
+ }
+#if PROCESS_LOCKING_CHECK
+ // Try to detect locked files by other processes
+ if (ec.value() == ERROR_SHARING_VIOLATION || ec.value() == ERROR_LOCK_VIOLATION)
+ {
+ find_locking_process(filename);
+ }
+#endif
+ return -1;
+ }
+ return 0;
+}
+
#if LL_WINDOWS
- // permissions are ignored on Windows
- int rc = 0;
- std::wstring utf16dirname = utf8path_to_wstring(dirname);
- if (!CreateDirectoryW(utf16dirname.c_str(), nullptr))
+
+inline int set_ec_from_system_error(std::error_code& ec, DWORD error)
+{
+ ec.assign(error, std::system_category());
+ return -1;
+}
+
+static int set_ec_from_system_error(std::error_code& ec)
+{
+ return set_ec_from_system_error(ec, GetLastError());
+}
+
+inline int set_ec_to_parameter_error(std::error_code& ec)
+{
+ return set_ec_from_system_error(ec, ERROR_INVALID_PARAMETER);
+}
+
+inline int set_ec_to_outofmemory_error(std::error_code& ec)
+{
+ return set_ec_from_system_error(ec, ERROR_NOT_ENOUGH_MEMORY);
+}
+
+inline DWORD decode_access_mode(std::ios_base::openmode omode)
+{
+ switch (omode & (LLFile::in | LLFile::out))
+ {
+ case LLFile::in:
+ return GENERIC_READ;
+ case LLFile::out:
+ return GENERIC_WRITE;
+ case LLFile::in | LLFile::out:
+ return GENERIC_READ | GENERIC_WRITE;
+ }
+ if (omode & LLFile::app)
+ {
+ return GENERIC_WRITE;
+ }
+ return 0;
+}
+
+inline DWORD decode_open_create_flags(std::ios_base::openmode omode)
+{
+ if (omode & LLFile::noreplace)
+ {
+ return CREATE_NEW; // create if it does not exist, otherwise fail
+ }
+ if (omode & LLFile::trunc)
+ {
+ if (!(omode & LLFile::out))
+ {
+ return TRUNCATE_EXISTING; // open and truncatte if it exists, otherwise fail
+ }
+ return CREATE_ALWAYS; // open and truncate if it exists, otherwise create it
+ }
+ if (!(omode & LLFile::out))
+ {
+ return OPEN_EXISTING; // open if exists, otherwise fail
+ }
+ // LLFile::app or (LLFile::out and (!LLFile::trunc or !LLFile::noreplace))
+ return OPEN_ALWAYS; // open if it exists, otherwise create it
+}
+
+inline DWORD decode_share_mode(int omode)
+{
+ if (omode & LLFile::exclusive)
{
- // Only treat other errors than an already existing file as a real error
- unsigned long oserr = GetLastError();
- if (oserr != ERROR_ALREADY_EXISTS)
+ return 0; // allow no other access
+ }
+ if (omode & LLFile::shared)
+ {
+ return FILE_SHARE_READ; // allow read access
+ }
+ return FILE_SHARE_READ | FILE_SHARE_WRITE; // allow read and write access to others
+}
+
+inline DWORD decode_attributes(std::ios_base::openmode omode, int perm)
+{
+ return (perm & S_IWRITE) ? FILE_ATTRIBUTE_NORMAL : FILE_ATTRIBUTE_READONLY;
+}
+
+// Under Windows the values for the std::ios_base::seekdir constants match the according FILE_BEGIN
+// and other constants but we do a programmatic translation for now to be sure
+static DWORD seek_mode_from_dir(std::ios_base::seekdir seekdir)
+{
+ switch (seekdir)
+ {
+ case LLFile::beg:
+ return FILE_BEGIN;
+ case LLFile::cur:
+ return FILE_CURRENT;
+ case LLFile::end:
+ return FILE_END;
+ }
+ return FILE_BEGIN;
+}
+
+#else
+
+inline int set_ec_from_system_error(std::error_code& ec, int error)
+{
+ ec.assign(error, std::system_category());
+ return -1;
+}
+
+static int set_ec_from_system_error(std::error_code& ec)
+{
+ return set_ec_from_system_error(ec, errno);
+}
+
+inline int set_ec_to_parameter_error(std::error_code& ec)
+{
+ return set_ec_from_system_error(ec, EINVAL);
+}
+
+inline int set_ec_to_outofmemory_error(std::error_code& ec)
+{
+ return set_ec_from_system_error(ec, ENOMEM);
+}
+
+inline int decode_access_mode(std::ios_base::openmode omode)
+{
+ switch (omode & (LLFile::in | LLFile::out))
+ {
+ case LLFile::out:
+ return O_WRONLY;
+ case LLFile::in | LLFile::out:
+ return O_RDWR;
+ }
+ return O_RDONLY;
+}
+
+inline int decode_open_mode(std::ios_base::openmode omode)
+{
+ int flags = O_CREAT | decode_access_mode(omode);
+ if (omode & LLFile::app)
+ {
+ flags |= O_APPEND;
+ }
+ if (omode & LLFile::trunc)
+ {
+ flags |= O_TRUNC;
+ }
+ if (omode & LLFile::binary)
+ {
+ // Not a thing under *nix
+ }
+ if (omode & LLFile::noreplace)
+ {
+ flags |= O_EXCL;
+ }
+ return flags;
+}
+
+inline int decode_lock_mode(std::ios_base::openmode omode)
+{
+ int lmode = omode & LLFile::noblock ? LOCK_NB : 0;
+ if (omode & LLFile::lock_mask)
+ {
+ if (omode & LLFile::exclusive)
{
- rc = set_errno_from_oserror(oserr);
+ return lmode | LOCK_EX;
}
+ return lmode | LOCK_SH;
}
+ return lmode | LOCK_UN;
+}
+
+// Under Linux and Mac the values for the std::ios_base::seekdir constants match the according SEEK_SET
+// and other constants but we do a programmatic translation for now to be sure
+inline int seek_mode_from_dir(std::ios_base::seekdir seekdir)
+{
+ switch (seekdir)
+ {
+ case LLFile::beg:
+ return SEEK_SET;
+ case LLFile::cur:
+ return SEEK_CUR;
+ case LLFile::end:
+ return SEEK_END;
+ }
+ return SEEK_SET;
+}
+
+#endif
+
+inline int clear_error(std::error_code& ec)
+{
+ ec.clear();
+ return 0;
+}
+
+inline bool are_open_mode_flags_invalid(std::ios_base::openmode omode)
+{
+ // at least one of input or output needs to be specified
+ if (!(omode & (LLFile::in | LLFile::out)))
+ {
+ return true;
+ }
+ // output must be possible for any of the extra options
+ if (!(omode & LLFile::out) && (omode & (LLFile::trunc | LLFile::app | LLFile::noreplace)))
+ {
+ return true;
+ }
+ // invalid combination, mutually exclusive
+ if ((omode & LLFile::app) && (omode & (LLFile::trunc | LLFile::noreplace)))
+ {
+ return true;
+ }
+ return false;
+}
+
+//----------------------------------------------------------------------------------------
+// class member functions
+//----------------------------------------------------------------------------------------
+int LLFile::open(const std::string& filename, std::ios_base::openmode omode, std::error_code& ec, int perm)
+{
+ close(ec);
+ if (are_open_mode_flags_invalid(omode))
+ {
+ return set_ec_to_parameter_error(ec);
+ }
+#if LL_WINDOWS
+ DWORD access = decode_access_mode(omode),
+ share = decode_share_mode(omode),
+ create = decode_open_create_flags(omode),
+ attributes = decode_attributes(omode, perm);
+
+ std::wstring file_path = utf8StringToWstring(filename);
+ mHandle = CreateFileW(file_path.c_str(), access, share, nullptr, create, attributes, nullptr);
#else
- int rc = ::mkdir(dirname.c_str(), (mode_t)perms);
- if (rc < 0 && errno == EEXIST)
+ int oflags = decode_open_mode(omode);
+ int lmode = LLFile::noblock | (omode & LLFile::lock_mask);
+ mHandle = ::open(filename.c_str(), oflags, perm);
+ if (mHandle != InvalidHandle && omode & LLFile::lock_mask &&
+ lock(omode | LLFile::noblock, ec) != 0)
{
- // this is not the error you want, move along
- return 0;
+ close();
+ return -1;
}
#endif
- // anything else might be a problem
- return warnif("mkdir", dirname, rc);
+ if (mHandle == InvalidHandle)
+ {
+ return set_ec_from_system_error(ec);
+ }
+
+ if (omode & LLFile::ate && seek(0, LLFile::end, ec) != 0)
+ {
+ close();
+ return -1;
+ }
+ mOpen = omode;
+ return clear_error(ec);
}
-// static
-int LLFile::rmdir(const std::string& dirname, int suppress_error)
+S64 LLFile::size(std::error_code& ec)
{
#if LL_WINDOWS
- std::wstring utf16dirname = utf8path_to_wstring(dirname);
- if (RemoveDirectoryW(utf16dirname.c_str()))
+ LARGE_INTEGER value = { 0 };
+ if (GetFileSizeEx(mHandle, &value))
{
- return 0;
+ clear_error(ec);
+ return value.QuadPart;
}
- int rc = set_errno_from_oserror(GetLastError());
#else
- int rc = ::rmdir(dirname.c_str());
+ struct stat statval;
+ if (fstat(mHandle, &statval) == 0)
+ {
+ clear_error(ec);
+ return statval.st_size;
+ }
#endif
- return warnif("rmdir", dirname, rc, suppress_error);
+ return set_ec_from_system_error(ec);
}
-// static
-LLFILE* LLFile::fopen(const std::string& filename, const char* mode)
+S64 LLFile::tell(std::error_code& ec)
{
#if LL_WINDOWS
- std::wstring utf16filename = utf8path_to_wstring(filename);
- std::wstring utf16mode = ll_convert<std::wstring>(std::string(mode));
- return _wfopen(utf16filename.c_str(), utf16mode.c_str());
+ LARGE_INTEGER value = { 0 };
+ if (SetFilePointerEx(mHandle, value, &value, FILE_CURRENT))
+ {
+ clear_error(ec);
+ return value.QuadPart;
+ }
#else
- return ::fopen(filename.c_str(),mode);
+ off_t offset = lseek(mHandle, 0, SEEK_CUR);
+ if (offset != -1)
+ {
+ clear_error(ec);
+ return offset;
+ }
#endif
+ return set_ec_from_system_error(ec);
}
-// static
-int LLFile::close(LLFILE * file)
+int LLFile::seek(S64 pos, std::error_code& ec)
{
- int ret_value = 0;
- if (file)
+ return seek(pos, LLFile::beg, ec);
+}
+
+int LLFile::seek(S64 offset, std::ios_base::seekdir dir, std::error_code& ec)
+{
+ S64 newOffset = 0;
+#if LL_WINDOWS
+ DWORD seekdir = seek_mode_from_dir(dir);
+ LARGE_INTEGER value;
+ value.QuadPart = offset;
+ if (SetFilePointerEx(mHandle, value, (PLARGE_INTEGER)&newOffset, seekdir))
+#else
+ newOffset = lseek(mHandle, offset, seek_mode_from_dir(dir));
+ if (newOffset != -1)
+#endif
{
- ret_value = fclose(file);
+ return clear_error(ec);
}
- return ret_value;
+ return set_ec_from_system_error(ec);
}
-// static
-std::string LLFile::getContents(const std::string& filename)
+#if LL_WINDOWS
+inline DWORD next_buffer_size(S64 nbytes)
{
- LLFILE* fp = LLFile::fopen(filename, "rb");
- if (fp)
+ return nbytes > 0x80000000 ? 0x80000000 : (DWORD)nbytes;
+}
+#endif
+
+S64 LLFile::read(void* buffer, S64 nbytes, std::error_code& ec)
+{
+ if (nbytes == 0)
+ {
+ // Nothing to do
+ clear_error(ec);
+ return 0;
+ }
+ else if (!buffer || nbytes < 0)
+ {
+ return set_ec_to_parameter_error(ec);
+ }
+#if LL_WINDOWS
+ S64 totalBytes = 0;
+ char *ptr = (char*)buffer;
+ DWORD bytesRead, bytesToRead = next_buffer_size(nbytes);
+
+ // Read in chunks to support >4GB which the S64 nbytes value makes possible
+ while (ReadFile(mHandle, ptr, bytesToRead, &bytesRead, nullptr))
+ {
+ totalBytes += bytesRead;
+ if (nbytes <= totalBytes || // requested amount read
+ bytesRead < bytesToRead) // ReadFile encountered eof
+ {
+ clear_error(ec);
+ return totalBytes;
+ }
+ ptr += bytesRead;
+ bytesToRead = next_buffer_size(nbytes - totalBytes);
+ }
+#else
+ ssize_t bytesRead = ::read(mHandle, buffer, nbytes);
+ if (bytesRead != -1)
+ {
+ clear_error(ec);
+ return bytesRead;
+ }
+#endif
+ return set_ec_from_system_error(ec);
+}
+
+S64 LLFile::write(const void* buffer, S64 nbytes, std::error_code& ec)
+{
+ if (nbytes == 0)
+ {
+ // Nothing to do here
+ clear_error(ec);
+ return 0;
+ }
+ else if (!buffer || nbytes < 0)
+ {
+ return set_ec_to_parameter_error(ec);
+ }
+#if LL_WINDOWS
+ // If this was opened in append mode, we emulate it on Windows
+ if (mOpen & LLFile::app && seek(0, LLFile::end, ec) != 0)
{
- fseek(fp, 0, SEEK_END);
- U32 length = ftell(fp);
- fseek(fp, 0, SEEK_SET);
+ return -1;
+ }
- std::vector<char> buffer(length);
- size_t nread = fread(buffer.data(), 1, length, fp);
- fclose(fp);
+ S64 totalBytes = 0;
+ char* ptr = (char*)buffer;
+ DWORD bytesWritten, bytesToWrite = next_buffer_size(nbytes);
- return std::string(buffer.data(), nread);
+ // Write in chunks to support >4GB which the S64 nbytes value makes possible
+ while (WriteFile(mHandle, ptr, bytesToWrite, &bytesWritten, nullptr))
+ {
+ totalBytes += bytesWritten;
+ if (nbytes <= totalBytes)
+ {
+ clear_error(ec);
+ return totalBytes;
+ }
+ ptr += bytesWritten;
+ bytesToWrite = next_buffer_size(nbytes - totalBytes);
}
+#else
+ ssize_t bytesWritten = ::write(mHandle, buffer, nbytes);
+ if (bytesWritten != -1)
+ {
+ clear_error(ec);
+ return bytesWritten;
+ }
+#endif
+ return set_ec_from_system_error(ec);
+}
- return LLStringUtil::null;
+S64 LLFile::printf(const char* fmt, ...)
+{
+ va_list args1;
+ va_start(args1, fmt);
+ va_list args2;
+ va_copy(args2, args1);
+ int length = vsnprintf(NULL, 0, fmt, args1);
+ va_end(args1);
+ if (length < 0)
+ {
+ va_end(args2);
+ return -1;
+ }
+ void* buffer = malloc(length + 1);
+ if (!buffer)
+ {
+ va_end(args2);
+ return -1;
+ }
+ length = vsnprintf((char*)buffer, length + 1, fmt, args2);
+ va_end(args2);
+ std::error_code ec;
+ S64 written = write(buffer, length, ec);
+ free(buffer);
+ return written;
}
-// static
-int LLFile::remove(const std::string& filename, int suppress_error)
+int LLFile::lock(int mode, std::error_code& ec)
{
#if LL_WINDOWS
- // Posix remove() works on both files and directories although on Windows
- // remove() and its wide char variant _wremove() only removes files just
- // as its siblings unlink() and _wunlink().
- // If we really only want to support files we should instead use
- // unlink() in the non-Windows part below too
- std::wstring utf16filename = utf8path_to_wstring(filename);
- unsigned short st_mode;
- int rc = get_fileattr(utf16filename, st_mode);
- if (rc == 0)
+ if (!(mode & LLFile::lock_mask))
{
- if (S_ISDIR(st_mode))
+ if (UnlockFile(mHandle, 0, 0, MAXDWORD, MAXDWORD))
{
- if (RemoveDirectoryW(utf16filename.c_str()))
- {
- return 0;
- }
+ return clear_error(ec);
}
- else if (S_ISREG(st_mode))
+ }
+ else
+ {
+ OVERLAPPED overlapped = { 0 };
+ DWORD flags = (mode & LLFile::noblock) ? LOCKFILE_FAIL_IMMEDIATELY : 0;
+ if (mode & LLFile::exclusive)
{
- if (DeleteFileW(utf16filename.c_str()))
- {
- return 0;
- }
+ flags |= LOCKFILE_EXCLUSIVE_LOCK;
}
- else
+ // We lock the maximum range, since flock only supports locking the entire file too
+ if (LockFileEx(mHandle, flags, 0, MAXDWORD, MAXDWORD, &overlapped))
{
- SetLastError(ERROR_INVALID_PARAMETER);
+ return clear_error(ec);
}
- // Deleting the file or directory failed
- rc = set_errno_from_oserror(GetLastError());
}
#else
- int rc = ::remove(filename.c_str());
+ if (flock(mHandle, decode_lock_mode(mode)) == 0)
+ {
+ return clear_error(ec);
+ }
#endif
- return warnif("remove", filename, rc, suppress_error);
+ return set_ec_from_system_error(ec);
+}
+
+int LLFile::close(std::error_code& ec)
+{
+ if (mHandle != InvalidHandle)
+ {
+ llfile_handle_t handle = InvalidHandle;
+ std::swap(handle, mHandle);
+#if LL_WINDOWS
+ if (!CloseHandle(handle))
+#else
+ if (::close(handle))
+#endif
+ {
+ return set_ec_from_system_error(ec);
+ }
+ }
+ return clear_error(ec);
+}
+
+int LLFile::close()
+{
+ std::error_code ec;
+ return close(ec);
}
+//----------------------------------------------------------------------------------------
+// static member functions
+//----------------------------------------------------------------------------------------
+
// static
-int LLFile::rename(const std::string& filename, const std::string& newname, int suppress_error)
+LLFILE* LLFile::fopen(const std::string& filename, const char* mode, int lmode)
{
+ LLFILE* file;
#if LL_WINDOWS
- // Posix rename() will gladly overwrite a file at newname if it exists, the Windows
- // rename(), respectively _wrename(), will bark on that. Instead call directly the Windows
- // API MoveFileEx() and use its flags to specify that overwrite is allowed.
- std::wstring utf16filename = utf8path_to_wstring(filename);
- std::wstring utf16newname = utf8path_to_wstring(newname);
- if (MoveFileExW(utf16filename.c_str(), utf16newname.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
+ int shflag = _SH_DENYNO;
+ switch (lmode)
{
- return 0;
+ case LLFile::exclusive:
+ shflag = _SH_DENYRW;
+ break;
+ case LLFile::shared:
+ shflag = _SH_DENYWR;
+ break;
}
- int rc = set_errno_from_oserror(GetLastError());
+ std::wstring file_path = utf8StringToWstring(filename);
+ std::wstring utf16mode = ll_convert<std::wstring>(std::string(mode));
+ file = _wfsopen(file_path.c_str(), utf16mode.c_str(), shflag);
#else
- int rc = ::rename(filename.c_str(),newname.c_str());
+ file = ::fopen(filename.c_str(), mode);
+ if (file && (lmode & (LLFile::lock_mask)))
+ {
+ // Rather fail on a sharing conflict than block
+ if (flock(fileno(file), decode_lock_mode(lmode | LLFile::noblock)))
+ {
+ LLFile::close(file);
+ file = nullptr;
+ }
+ }
#endif
- return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, rc, suppress_error);
+ return file;
}
// static
-U64 LLFile::read(const std::string& filename, void* buf, U64 offset, U64 nbytes)
+int LLFile::close(LLFILE* file)
{
- LLFILE* file_handle = LLFile::fopen(filename, "rb");
- if (file_handle == nullptr)
+ int ret_value = 0;
+ if (file)
{
- warnif("read, opening file", filename, -1);
- return 0;
+ // Read the current errno and restore it if it was not 0
+ int errn = errno;
+ ret_value = ::fclose(file);
+ if (errn)
+ {
+ errno = errn;
+ }
}
+ return ret_value;
+}
- if (offset > 0)
+// static
+std::string LLFile::getContents(const std::string& filename)
+{
+ std::error_code ec;
+ return getContents(filename, ec);
+}
+
+// static
+std::string LLFile::getContents(const std::string& filename, std::error_code& ec)
+{
+ std::string buffer;
+ LLFile file(filename, LLFile::in | LLFile::binary, ec);
+ if (file)
{
-#if LL_WINDOWS
- // On Windows fseek() uses a long value as offset which is always 32-bit
- int rc = _fseeki64(file_handle, (long long)offset, SEEK_SET);
-#else
- // offset is of type long which is 64-bit in 64-bit GCC
- int rc = fseek(file_handle, (long)offset, SEEK_SET);
-#endif
- if (rc)
+ S64 length = file.size(ec);
+ if (!ec && length > 0)
{
- warnif("read, setting file offset", filename, rc);
- fclose(file_handle);
- return 0;
+ buffer = std::string(length, 0);
+ file.read(&buffer[0], length, ec);
+ if (ec)
+ {
+ buffer.clear();
+ }
}
}
+ return buffer;
+}
- // element_count is of size_t which is 64-bit on all 64-bit systems
- U64 bytes_read = fread(buf, 1, nbytes, file_handle);
- if (bytes_read == 0)
- {
- warnif("read from file", filename, -1);
- }
- fclose(file_handle);
- return bytes_read;
+// static
+int LLFile::mkdir(const std::string& dirname)
+{
+ std::error_code ec;
+ std::filesystem::path file_path = utf8StringToPath(dirname);
+ // We often use mkdir() to ensure the existence of a directory that might
+ // already exist. There is no known case in which we want to call out as
+ // an error the requested directory already existing.
+ std::filesystem::create_directory(file_path, ec);
+ // The return value is only true if the directory was actually created.
+ // But if it already existed, ec still indicates success.
+ return warnif("mkdir", dirname, ec);
+}
+
+// static
+int LLFile::remove(const std::string& filename, int suppress_error)
+{
+ std::error_code ec;
+ std::filesystem::path file_path = utf8StringToPath(filename);
+ std::filesystem::remove(file_path, ec);
+ return warnif("remove", filename, ec, suppress_error);
+}
+
+// static
+int LLFile::rename(const std::string& filename, const std::string& newname, int suppress_error)
+{
+ std::error_code ec;
+ std::filesystem::path file_path = utf8StringToPath(filename);
+ std::filesystem::path new_path = utf8StringToPath(newname);
+ std::filesystem::rename(file_path, new_path, ec);
+ return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, ec, suppress_error);
+}
+
+// static
+S64 LLFile::read(const std::string& filename, void* buf, S64 offset, S64 nbytes)
+{
+ std::error_code ec;
+ return read(filename, buf, offset, nbytes, ec);
}
// static
-U64 LLFile::write(const std::string& filename, const void* buf, U64 offset, U64 nbytes)
+S64 LLFile::read(const std::string& filename, void* buf, S64 offset, S64 nbytes, std::error_code& ec)
{
- LLFILE* file_handle = LLFile::fopen(filename, "wb");
- if (file_handle == nullptr)
+ // if number of bytes is 0 or less there is nothing to do here
+ if (nbytes <= 0)
{
- warnif("write, opening file", filename, -1);
+ clear_error(ec);
return 0;
}
- if (offset > 0)
+ std::ios_base::openmode omode = LLFile::in | LLFile::binary;
+
+ LLFile file(filename, omode, ec);
+ if (file)
{
-#if LL_WINDOWS
- // On Windows fseek() uses a long value as offset which is always 32-bit
- int rc = _fseeki64(file_handle, (long long)offset, SEEK_SET);
-#else
- // offset is of type long which is 64-bit in 64-bit GCC
- int rc = fseek(file_handle, (long)offset, SEEK_SET);
-#endif
- if (rc)
+ S64 bytes_read = 0;
+ if (offset > 0)
{
- warnif("write, setting file offset", filename, rc);
- fclose(file_handle);
- return 0;
+ file.seek(offset, ec);
+ }
+ if (!ec)
+ {
+ bytes_read = file.read(buf, nbytes, ec);
+ if (!ec)
+ {
+ return bytes_read;
+ }
}
}
-
- // element_count is of size_t which is 64-bit on all 64-bit systems
- U64 bytes_written = fwrite(buf, 1, nbytes, file_handle);
- if (bytes_written == 0)
- {
- warnif("write to file", filename, -1);
- }
- fclose(file_handle);
- return bytes_written;
+ return warnif("read from file failed", filename, ec);
}
-// Make this a define rather than using magic numbers multiple times in the code
-#define LLFILE_COPY_BUFFER_SIZE 16384
+// static
+S64 LLFile::write(const std::string& filename, const void* buf, S64 offset, S64 nbytes)
+{
+ std::error_code ec;
+ return write(filename, buf, offset, nbytes, ec);
+}
// static
-bool LLFile::copy(const std::string& from, const std::string& to)
+S64 LLFile::write(const std::string& filename, const void* buf, S64 offset, S64 nbytes, std::error_code& ec)
{
- bool copied = false;
- LLFILE* in = LLFile::fopen(from, "rb");
- if (in)
+ // if number of bytes is 0 or less there is nothing to do here
+ if (nbytes <= 0)
+ {
+ clear_error(ec);
+ return 0;
+ }
+
+ std::ios_base::openmode omode = LLFile::out | LLFile::binary;
+ if (offset < 0)
+ {
+ omode |= LLFile::app;
+ }
+
+ LLFile file(filename, omode, ec);
+ if (file)
{
- LLFILE* out = LLFile::fopen(to, "wb");
- if (out)
+ S64 bytes_written = 0;
+ if (offset > 0)
{
- char buf[LLFILE_COPY_BUFFER_SIZE];
- size_t readbytes;
- bool write_ok = true;
- while (write_ok && (readbytes = fread(buf, 1, LLFILE_COPY_BUFFER_SIZE, in)))
- {
- if (fwrite(buf, 1, readbytes, out) != readbytes)
- {
- LL_WARNS("LLFile") << "Short write" << LL_ENDL;
- write_ok = false;
- }
- }
- if ( write_ok )
+ file.seek(offset, ec);
+ }
+ if (!ec)
+ {
+ bytes_written = file.write(buf, nbytes, ec);
+ if (!ec)
{
- copied = true;
+ return bytes_written;
}
- fclose(out);
}
- fclose(in);
+ }
+ return warnif("write to file failed", filename, ec);
+}
+
+// static
+bool LLFile::copy(const std::string& from, const std::string& to)
+{
+ std::error_code ec;
+ std::filesystem::path from_path = utf8StringToPath(from);
+ std::filesystem::path to_path = utf8StringToPath(to);
+ bool copied = std::filesystem::copy_file(from_path, to_path, ec);
+ if (!copied)
+ {
+ LL_WARNS("LLFile") << "copy failed" << LL_ENDL;
}
return copied;
}
// static
-int LLFile::stat(const std::string& filename, llstat* filestatus, int suppress_error)
+int LLFile::stat(const std::string& filename, llstat* filestatus, const char *fname, int suppress_warning)
{
#if LL_WINDOWS
- std::wstring utf16filename = utf8path_to_wstring(filename);
- int rc = _wstat64(utf16filename.c_str(), filestatus);
+ std::wstring file_path = utf8StringToWstring(filename);
+ int rc = _wstat64(file_path.c_str(), filestatus);
#else
int rc = ::stat(filename.c_str(), filestatus);
#endif
- return warnif("stat", filename, rc, suppress_error);
+ return warnif(fname ? fname : "stat", filename, rc, suppress_warning);
}
// static
-S64 LLFile::size(const std::string& filename, int suppress_error)
+std::time_t LLFile::getCreationTime(const std::string& filename, int suppress_warning)
{
-#if LL_WINDOWS
- std::wstring utf16filename = utf8path_to_wstring(filename);
- HANDLE file_handle = CreateFileW(utf16filename.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
- if (INVALID_HANDLE_VALUE != file_handle)
- {
- LARGE_INTEGER file_size;
- if (GetFileSizeEx(file_handle, &file_size))
- {
- CloseHandle(file_handle);
- return file_size.QuadPart;
- }
- }
- // Get last error before calling the CloseHandle() function
- int rc = set_errno_from_oserror(GetLastError());
- if (INVALID_HANDLE_VALUE != file_handle)
+ // As if C++20 there is no functionality in std::filesystem to retrieve this information
+ llstat filestat;
+ int rc = stat(filename, &filestat, "getCreationTime", suppress_warning);
+ if (rc == 0)
{
- CloseHandle(file_handle);
- }
+#if LL_DARWIN
+ return filestat.st_birthtime;
#else
- llstat filestatus;
- int rc = ::stat(filename.c_str(), &filestatus);
+ // Linux stat() doesn't have a creation/birth time (st_ctime really is the last status
+ // change or inode attributes change) unless we would use statx() instead. But that is
+ // a major effort, which would require Linux specific changes to LLFile::stat() above
+ // and possibly adaptions for other platforms that we leave for a later exercise if it
+ // is ever desired.
+ return filestat.st_ctime;
+#endif
+ }
+ return 0;
+}
+
+// static
+std::time_t LLFile::getModificationTime(const std::string& filename, int suppress_warning)
+{
+ // tried to use std::filesystem::last_write_time() but the whole std::chrono infrastructure is as of
+ // C++20 still not fully implemented on all platforms. Specifically MacOS C++20 seems lacking here,
+ // and Windows requires a roundabout through std::chrono::utc_clock to then get a
+ // std::chrono::system_clock that can return a more useful time_t.
+ // So we take the easy way out in a similar way as with getCreationTime().
+ llstat filestat;
+ int rc = stat(filename, &filestat, "getModificationTime", suppress_warning);
if (rc == 0)
{
- if (S_ISREG(filestatus.st_mode))
- {
- return filestatus.st_size;
- }
- // We got something else than a file
- rc = -1;
- errno = EACCES;
+ return filestat.st_mtime;
}
-#endif
- warnif("size", filename, rc, suppress_error);
return 0;
}
// static
-unsigned short LLFile::getattr(const std::string& filename, bool dontFollowSymLink, int suppress_error)
+S64 LLFile::size(const std::string& filename, int suppress_warning)
{
-#if LL_WINDOWS
- // _wstat64() is a bit heavyweight on Windows, use a more lightweight API
- // to just get the attributes
- std::wstring utf16filename = utf8path_to_wstring(filename);
- unsigned short st_mode;
- int rc = get_fileattr(utf16filename, st_mode, dontFollowSymLink);
- if (rc == 0)
+ std::error_code ec;
+ std::filesystem::path file_path = utf8StringToPath(filename);
+ std::intmax_t size = (std::intmax_t)std::filesystem::file_size(file_path, ec);
+ return warnif("size", filename, ec, suppress_warning) ? -1 : size;
+}
+
+// static
+std::filesystem::file_status LLFile::getStatus(const std::string& filename, bool dontFollowSymLink, int suppress_warning)
+{
+ std::error_code ec;
+ std::filesystem::path file_path = utf8StringToPath(filename);
+ std::filesystem::file_status status;
+ if (dontFollowSymLink)
{
- return st_mode;
+ status = std::filesystem::status(file_path, ec);
}
-#else
- llstat filestatus;
- int rc = dontFollowSymLink ? ::lstat(filename.c_str(), &filestatus) : ::stat(filename.c_str(), &filestatus);
- if (rc == 0)
+ else
{
- return filestatus.st_mode;
+ status = std::filesystem::symlink_status(file_path, ec);
}
-#endif
- warnif("getattr", filename, rc, suppress_error);
- return 0;
+ warnif("getattr", filename, ec, suppress_warning);
+ return status;
+}
+
+// static
+bool LLFile::exists(const std::string& filename)
+{
+ std::filesystem::file_status status = getStatus(filename);
+ return std::filesystem::exists(status);
}
// static
bool LLFile::isdir(const std::string& filename)
{
- return S_ISDIR(getattr(filename));
+ std::filesystem::file_status status = getStatus(filename);
+ return std::filesystem::is_directory(status);
}
// static
bool LLFile::isfile(const std::string& filename)
{
- return S_ISREG(getattr(filename));
+ std::filesystem::file_status status = getStatus(filename);
+ return std::filesystem::is_regular_file(status);
}
// static
bool LLFile::islink(const std::string& filename)
{
- return S_ISLNK(getattr(filename, true));
+ std::filesystem::file_status status = getStatus(filename, true);
+ return std::filesystem::is_symlink(status);
}
// static
-const char *LLFile::tmpdir()
+const std::string& LLFile::tmpdir()
{
- static std::string utf8path;
-
- if (utf8path.empty())
+ static std::string temppath;
+ if (temppath.empty())
{
- char sep;
-#if LL_WINDOWS
- sep = '\\';
+ temppath = std::filesystem::temp_directory_path().string();
+ }
+ return temppath;
+}
- std::vector<wchar_t> utf16path(MAX_PATH + 1);
- GetTempPathW(static_cast<DWORD>(utf16path.size()), &utf16path[0]);
- utf8path = ll_convert_wide_to_string(&utf16path[0]);
+// static
+std::filesystem::path LLFile::utf8StringToPath(const std::string& pathname)
+{
+#if LL_WINDOWS
+ return ll_convert<std::wstring>(pathname);
#else
- sep = '/';
-
- utf8path = LLStringUtil::getenv("TMPDIR", "/tmp/");
+ return pathname;
#endif
- if (utf8path[utf8path.size() - 1] != sep)
- {
- utf8path += sep;
- }
- }
- return utf8path.c_str();
}
#if LL_WINDOWS
+// static
+std::wstring LLFile::utf8StringToWstring(const std::string& pathname)
+{
+ std::wstring utf16string(ll_convert<std::wstring>(pathname));
+ if (utf16string.size() >= MAX_PATH)
+ {
+ // By going through std::filesystem::path we get a lot of path sanitation done for us that
+ // is needed when passing a path with a kernel object space prefix to Windows API functions
+ // since this prefix disables the kernel32 path normalization
+ std::filesystem::path utf16path(utf16string);
+
+ // By prepending "\\?\" to a path, Windows widechar file APIs will not fail on long path names
+ utf16string.assign(L"\\\\?\\").append(utf16path);
+
+ /* remove trailing spaces and dots (yes, Windows really does that) */
+ return utf16string.substr(0, utf16string.find_last_not_of(L" \t."));
+ }
+ return utf16string;
+}
+
/************** input file stream ********************************/
llifstream::llifstream() {}
@@ -735,11 +1180,11 @@ std::streamsize llifstream_size(llifstream& ifstr)
{
if(!ifstr.is_open()) return 0;
std::streampos pos_old = ifstr.tellg();
- ifstr.seekg(0, ios_base::beg);
+ ifstr.seekg(0, std::ios_base::beg);
std::streampos pos_beg = ifstr.tellg();
- ifstr.seekg(0, ios_base::end);
+ ifstr.seekg(0, std::ios_base::end);
std::streampos pos_end = ifstr.tellg();
- ifstr.seekg(pos_old, ios_base::beg);
+ ifstr.seekg(pos_old, std::ios_base::beg);
return pos_end - pos_beg;
}
@@ -747,11 +1192,11 @@ std::streamsize llofstream_size(llofstream& ofstr)
{
if(!ofstr.is_open()) return 0;
std::streampos pos_old = ofstr.tellp();
- ofstr.seekp(0, ios_base::beg);
+ ofstr.seekp(0, std::ios_base::beg);
std::streampos pos_beg = ofstr.tellp();
- ofstr.seekp(0, ios_base::end);
+ ofstr.seekp(0, std::ios_base::end);
std::streampos pos_end = ofstr.tellp();
- ofstr.seekp(pos_old, ios_base::beg);
+ ofstr.seekp(pos_old, std::ios_base::beg);
return pos_end - pos_beg;
}
diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h
index a856f34be6..2bfc4d1ffb 100644..100755
--- a/indra/llcommon/llfile.h
+++ b/indra/llcommon/llfile.h
@@ -35,57 +35,218 @@
* Attempts to mostly mirror the POSIX style IO functions.
*/
-typedef FILE LLFILE;
-
#include <fstream>
+#include <filesystem>
#include <sys/stat.h>
#if LL_WINDOWS
+#include <windows.h>
// The Windows version of stat function and stat data structure are called _stat64
// We use _stat64 here to support 64-bit st_size and time_t values
-typedef struct _stat64 llstat;
+typedef struct _stat64 llstat;
#else
#include <sys/types.h>
typedef struct stat llstat;
#endif
-#ifndef S_ISREG
-# define S_ISREG(x) (((x) & S_IFMT) == S_IFREG)
-#endif
-
-#ifndef S_ISDIR
-# define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR)
-#endif
-
-// Windows C runtime library does not define this and does not support symlink detection in the
-// stat functions but we do in our getattr() function
-#ifndef S_IFLNK
-#define S_IFLNK 0xA000 /* symlink */
-#endif
-
-#ifndef S_ISLNK
-#define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK)
-#endif
+typedef FILE LLFILE;
#include "llstring.h" // safe char* -> std::string conversion
-/// LLFile is a class of static functions operating on paths
/// All the functions with a path string input take UTF8 path/filenames
class LL_COMMON_API LLFile
{
public:
-//----------------------------------------------------------------------------------------
-// Static member functions
-//----------------------------------------------------------------------------------------
+ //--------------------------------------------------------------------------------------
+ // constants
+ //--------------------------------------------------------------------------------------
+ // These can be passed to the omode parameter of LLFile::open() and its constructor
+ /*
+ This is similar to the openmode flags for std:fstream but not exactly the same
+ std::fstream open() does not allow to open a file for writing without either
+ forcing the file to be truncated on open or all write operations being always
+ appended to the end of the file.
+ But to allow implementing the LLAPRFile::writeEx() functionality we need to be
+ able to write at a random position to the file without truncating it on open.
+
+ any other combinations than listed here are not allowed and will cause an error
+
+ bin in out trunc app norep File exists File does not exist
+ -------------------------------------------------------------------------------
+ - + - - - - Open at begin Failure to open
+ + + - - - - " "
+ - - + - - - " Create new
+ + - + - - - " "
+ - + + - - - " "
+ + + + - - - " "
+ -------------------------------------------------------------------------------
+ - - + + - - Destroy contents Create new
+ + - + + - - " "
+ - + + + - - " "
+ + + + + - - " "
+ -------------------------------------------------------------------------------
+ - - + x - + Failure to open Create new
+ + - + x - + " "
+ - + + x - + " "
+ + + + x - + " "
+ -------------------------------------------------------------------------------
+ - - + - + - Write to end Create new
+ + - + - + - " "
+ - + + - + - " "
+ + + + - + - " "
+ -------------------------------------------------------------------------------
+ */
+ static const std::ios_base::openmode app = 1 << 1; // append to end
+ static const std::ios_base::openmode ate = 1 << 2; // initialize to end
+ static const std::ios_base::openmode binary = 1 << 3; // binary mode
+ static const std::ios_base::openmode in = 1 << 4; // for reading
+ static const std::ios_base::openmode out = 1 << 5; // for writing
+ static const std::ios_base::openmode trunc = 1 << 6; // truncate on open
+ static const std::ios_base::openmode noreplace = 1 << 7; // no replace if it exists
+
+ // Additional optional omode flags to open() and lmode to fopen() or mode flags to lock()
+ // to indicate which sort of lock if any to attempt to get
+ static const std::ios_base::openmode exclusive = 1 << 16;
+ static const std::ios_base::openmode shared = 1 << 17;
+ // When used either in omode for LLFile::open() or in lmode for LLFile::fopen() there is a
+ // difference between platforms.
+ // On Windows this lock is mandatory as it is part of the API to open a file and other processes
+ // can not avoid it. If a file was opened denying other processes read and/or write access,
+ // trying to open the same file with that access will fail.
+ // On Mac and Linux it is only an advisory lock. This means that the other application
+ // needs to also attempt to at least acquire a shared lock on the file in order to notice that
+ // the file is actually already locked. It can therefore not be used to prevent random other
+ // applications from accessing the file, but it works for other viewer processes when they use
+ // either the LLFile::open() or LLFile::fopen() functions with the appropriate lock flags to
+ // open a file.
+
+ // Additional mode flag to indicate to rather fail instead of blocking when trying
+ // to acquire a lock with LLFile::lock()
+ static const std::ios_base::openmode noblock = 1 << 18;
+
+ static const std::ios_base::openmode lock_mask = exclusive | shared;
+
+ // These can be passed to the dir parameter of LLFile::seek()
+ static const std::ios_base::seekdir beg = std::ios_base::beg;
+ static const std::ios_base::seekdir cur = std::ios_base::cur;
+ static const std::ios_base::seekdir end = std::ios_base::end;
+
+ //--------------------------------------------------------------------------------------
+ // constructor/deconstructor
+ //--------------------------------------------------------------------------------------
+ LLFile() : mHandle(InvalidHandle) {}
+
+ // no copy
+ LLFile(const LLFile&) = delete;
+
+ // move construction
+ LLFile(LLFile&& other) noexcept
+ {
+ mHandle = other.mHandle;
+ other.mHandle = InvalidHandle;
+ }
+
+ // open a file
+ LLFile(const std::string& filename, std::ios_base::openmode omode, std::error_code& ec, int perm = 0666) :
+ mHandle(InvalidHandle)
+ {
+ open(filename, omode, ec, perm);
+ }
+
+ // Make it a RAII class
+ ~LLFile() { close(); }
+
+ //--------------------------------------------------------------------------------------
+ // operators
+ //--------------------------------------------------------------------------------------
+ // copy assignment deleted
+ LLFile& operator=(const LLFile&) = delete;
+
+ // move assignment
+ LLFile& operator=(LLFile&& other) noexcept
+ {
+ close();
+ std::swap(mHandle, other.mHandle);
+ return *this;
+ }
+
+ // detect whether the wrapped file descriptor/handle is open or not
+ explicit operator bool() const { return (mHandle != InvalidHandle); }
+ bool operator!() { return (mHandle == InvalidHandle); }
+
+ //--------------------------------------------------------------------------------------
+ // class member methods
+ //--------------------------------------------------------------------------------------
+
+ /// All of these functions take as one of their parameters a std::error_code object which can be used to
+ /// determine in more detail what error occurred if required
+
+ /// Open a file with the specific open mode flags
+ int open(const std::string& filename, std::ios_base::openmode omode, std::error_code& ec, int perm = 0666);
+ ///< @returns 0 on success, -1 on failure
+
+ /// Determine the size of the opened file
+ S64 size(std::error_code& ec);
+ ///< @returns the number of bytes in the file or -1 on failure
+
+ /// Return the current file pointer into the file
+ S64 tell(std::error_code& ec);
+ ///< @returns the absolute offset of the file pointer in bytes relative to the start of the file or -1 on failure
+
+ /// Move the file pointer to the specified absolute position relative to the start of the file
+ int seek(S64 pos, std::error_code& ec);
+ ///< @returns 0 on success, -1 on failure
+
+ /// Move the file pointer to the specified position relative to dir
+ int seek(S64 offset, std::ios_base::seekdir dir, std::error_code& ec);
+ ///< @returns 0 on success, -1 on failure
+
+ /// Read a number of bytes into the buffer starting at the current file pointer
+ S64 read(void* buffer, S64 nbytes, std::error_code& ec);
+ ///< If the file ends before the requested amount of bytes could be read, the function succeeds and
+ /// returns the bytes up to the end of the file. The return value indicates the number of actually
+ /// read bytes and can be therefore smaller than the requested amount.
+ /// @returns the number of bytes read from the file or -1 on failure
+
+ /// Write a number of bytes to the file starting at the current file pointer
+ S64 write(const void* buffer, S64 nbytes, std::error_code& ec);
+ /// @returns the number of bytes written to the file or -1 on failure
+
+ /// Write into the file starting at the current file pointer using printf style format
+ S64 printf(const char* fmt, ...);
+ /// @returns the number of bytes written to the file or -1 on failure
+
+ /// Attempt to acquire or release a lock on the file
+ int lock(int mode, std::error_code& ec);
+ ///< mode can be one of LLFile::exclusive or LLFile::shared to acquire the according lock
+ /// or 0 to give up an earlier acquired lock. Adding LLFile::noblock together with one of
+ /// the lock requests will cause the function to fail if the lock can not be acquired,
+ /// otherwise the function will block until the lock can be acquired.
+ /// @returns 0 on success, -1 on failure
+
+ /// close the file explicitely
+ int close(std::error_code& ec);
+ ///< @returns 0 on success, -1 on failure
+
+ /// Convinience function to close the file without parameters
+ int close();
+ ///< @returns 0 on success, -1 on failure
+
+ //----------------------------------------------------------------------------------------
+ // static member functions
+ //
+ // All filename parameters are UTF8 file paths
+ //
+ //----------------------------------------------------------------------------------------
/// open a file with the specified access mode
- static LLFILE* fopen(const std::string& filename, const char* accessmode);
+ static LLFILE* fopen(const std::string& filename, const char* accessmode, int lmode = 0);
///< 'accessmode' follows the rules of the Posix fopen() mode parameter
/// "r" open the file for reading only and positions the stream at the beginning
/// "r+" open the file for reading and writing and positions the stream at the beginning
/// "w" open the file for reading and writing and truncate it to zero length
/// "w+" open or create the file for reading and writing and truncate to zero length if it existed
- /// "a" open the file for reading and writing and position the stream at the end of the file
- /// "a+" open or create the file for reading and writing and position the stream at the end of the file
+ /// "a" open the file for reading and writing and and before every write position the stream at the end of the file
+ /// "a+" open or create the file for reading and writing and before every write position the stream at the end of the file
///
/// in addition to these values, "b" can be appended to indicate binary stream access, but on Linux and Mac
/// this is strictly for compatibility and has no effect. On Windows this makes the file functions not
@@ -93,35 +254,37 @@ public:
/// "b" or "t" is defined, Windows uses the value set by _fmode which by default is _O_TEXT.
/// This means that it is always a good idea to append "b" specifically for binary file access to
/// avoid corruption of the binary consistency of the data stream when reading or writing
- /// Other characters in 'accessmode' will usually cause an error as fopen will verify this parameter
+ /// Other characters in 'accessmode', while possible on some platforms (Windows), will usually
+ /// cause an error as fopen will verify this parameter
+ ///
+ /// lmode is optional and allows to lock the file for other processes either as a shared lock or an
+ /// exclusive lock. If the requested lock conflicts with an already existing lock, the open fails.
+ /// Pass either LLFIle::exclusive or LLFile::shared to this parameter if you want to prevent other
+ /// processes from reading (exclusive lock) or writing (shared lock) to the file. It will always use
+ /// LLFile::noblock, meaning the open will immediately fail if it conflicts with an existing lock on the
+ /// file.
+ ///
/// @returns a valid LLFILE* pointer on success that can be passed to the fread() and fwrite() functions
/// and some other f<something> functions in the Standard C library that accept a FILE* as parameter
/// or NULL on failure
/// Close a file handle opened with fopen() above
static int close(LLFILE * file);
-
- /// create a directory
- static int mkdir(const std::string& filename, int perms = 0700);
- ///< perms is a permissions mask like 0777 or 0700. In most cases it will be
- /// overridden by the user's umask. It is ignored on Windows.
- /// mkdir() considers "directory already exists" to be not an error.
/// @returns 0 on success and -1 on failure.
- //// remove a directory
- static int rmdir(const std::string& filename, int suppress_error = 0);
- ///< pass ENOENT in the optional 'suppress_error' parameter
- /// if you don't want a warning in the log when the directory does not exist
+ /// create a directory
+ static int mkdir(const std::string& filename);
+ ///< mkdir() considers "directory already exists" to be not an error.
/// @returns 0 on success and -1 on failure.
/// remove a file or directory
- static int remove(const std::string& filename, int suppress_error = 0);
- ///< pass ENOENT in the optional 'suppress_error' parameter
- /// if you don't want a warning in the log when the directory does not exist
+ static int remove(const std::string& filename, int suppress_warning = 0);
+ ///< pass ENOENT in the optional 'suppress_warning' parameter if you don't want
+ /// a warning in the log when the directory does not exist
/// @returns 0 on success and -1 on failure.
/// rename a file
- static int rename(const std::string& filename, const std::string& newname, int suppress_error = 0);
+ static int rename(const std::string& filename, const std::string& newname, int suppress_warning = 0);
///< it will silently overwrite newname if it exists without returning an error
/// Posix guarantees that if newname already exists, then there will be no moment
/// in which for other processes newname does not exist. There is no such guarantee
@@ -135,116 +298,103 @@ public:
/// retrieve the content of a file into a string
static std::string getContents(const std::string& filename);
+ static std::string getContents(const std::string& filename, std::error_code& ec);
///< @returns the content of the file or an empty string on failure
/// read nBytes from the file into the buffer, starting at offset in the file
- static U64 read(const std::string& filename, void* buf, U64 offset, U64 nbytes);
- ///< @returns bytes read on success, 0 on failure
+ static S64 read(const std::string& filename, void* buf, S64 offset, S64 nbytes);
+ static S64 read(const std::string& filename, void* buf, S64 offset, S64 nbytes, std::error_code& ec);
+ ///< @returns bytes read on success, or -1 on failure
/// write nBytes from the buffer into the file, starting at offset in the file
- static U64 write(const std::string& filename, const void* buf, U64 offset, U64 nbytes);
- ///< @returns bytes written on success, 0 on failure
+ static S64 write(const std::string& filename, const void* buf, S64 offset, S64 nbytes);
+ static S64 write(const std::string& filename, const void* buf, S64 offset, S64 nbytes, std::error_code& ec);
+ ///< A negative offset will append the data to the end of the file
+ /// @returns bytes written on success, or -1 on failure
/// return the file stat structure for filename
- static int stat(const std::string& filename, llstat* file_status, int suppress_error = ENOENT);
+ static int stat(const std::string& filename, llstat* file_status, const char *fname = nullptr, int suppress_warning = ENOENT);
///< for compatibility with existing uses of LL_File::stat() we use ENOENT as default in the
- /// optional 'suppress_error' parameter to avoid spamming the log with warnings when the API
+ /// optional 'suppress_warning' parameter to avoid spamming the log with warnings when the API
/// is used to detect if a file exists
/// @returns 0 on success and -1 on failure.
+ /// get the creation data and time of a file
+ static std::time_t getCreationTime(const std::string& filename, int suppress_warning = 0);
+ ///< @returns the creation time of the file or 0 on error
+
+ /// get the last modification data and time of a file
+ static std::time_t getModificationTime(const std::string& filename, int suppress_warning = 0);
+ ///< @returns the modification time of the file or 0 on error
+
/// get the file or directory attributes for filename
- static unsigned short getattr(const std::string& filename, bool dontFollowSymLink = false, int suppress_error = ENOENT);
- ///< a more lightweight function on Windows to stat, that just returns the file attribute flags
- /// dontFollowSymLinks set to true returns the attributes of the symlink if it is one, rather than resolving it
- /// we pass by default ENOENT in the optional 'suppress_error' parameter to not spam the log with
+ static std::filesystem::file_status getStatus(const std::string& filename, bool dontFollowSymLink = false, int suppress_warning = ENOENT);
+ ///< dontFollowSymLinks set to true returns the attributes of the symlink if it is one, rather than resolving it
+ /// we pass by default ENOENT in the optional 'suppress_warning' parameter to not spam the log with
/// warnings when the file or directory does not exist
- /// @returns 0 on failure and a st_mode value with either S_IFDIR, S_IFREG or S_IFLNK set,
- /// together with the three access bits which under Windows only the write bit is relevant.
+ /// @returns a std::filesystem::file_status value that can be passed to the according std::filesystem::exists()
+ /// and other APIs accepting a file_status.
/// get the size of a file in bytes
- static S64 size(const std::string& filename, int suppress_error = ENOENT);
- ///> returns the size of a file in bytes
- /// we pass by default ENOENT in the optional 'suppress_error' parameter to not spam the log with
- /// warnings when the file does not exist
- /// @returns the file size on success and 0 on failure.
+ static S64 size(const std::string& filename, int suppress_warning = ENOENT);
+ ///< we pass by default ENOENT in the optional 'suppress_warning' parameter to not spam
+ /// the log with warnings when the file does not exist
+ /// @returns the file size on success or -1 on failure.
+
+ /// check if filename is an existing file or directory
+ static bool exists(const std::string& filename);
+ ///< @returns true if the path is for an existing file or directory
/// check if filename is an existing directory
- static bool isdir(const std::string& filename);
+ static bool isdir(const std::string& filename);
///< @returns true if the path is for an existing directory
/// check if filename is an existing file
- static bool isfile(const std::string& filename);
+ static bool isfile(const std::string& filename);
///< @returns true if the path is for an existing file
/// check if filename is a symlink
- static bool islink(const std::string& filename);
+ static bool islink(const std::string& filename);
///< @returns true if the path is pointing at a symlink
/// return a path to the temporary directory on the system
- static const char * tmpdir();
-};
+ static const std::string& tmpdir();
-/// RAII class
-class LLUniqueFile
-{
-public:
- // empty
- LLUniqueFile(): mFileHandle(nullptr) {}
- // wrap (e.g.) result of LLFile::fopen()
- LLUniqueFile(LLFILE* f): mFileHandle(f) {}
- // no copy
- LLUniqueFile(const LLUniqueFile&) = delete;
- // move construction
- LLUniqueFile(LLUniqueFile&& other) noexcept
- {
- mFileHandle = other.mFileHandle;
- other.mFileHandle = nullptr;
- }
- // The point of LLUniqueFile is to close on destruction.
- ~LLUniqueFile()
- {
- close();
- }
-
- // simple assignment
- LLUniqueFile& operator=(LLFILE* f)
- {
- close();
- mFileHandle = f;
- return *this;
- }
- // copy assignment deleted
- LLUniqueFile& operator=(const LLUniqueFile&) = delete;
- // move assignment
- LLUniqueFile& operator=(LLUniqueFile&& other) noexcept
- {
- close();
- std::swap(mFileHandle, other.mFileHandle);
- return *this;
- }
-
- // explicit close operation
- void close()
- {
- if (mFileHandle)
- {
- // in case close() throws, set mFileHandle null FIRST
- LLFILE* h{nullptr};
- std::swap(h, mFileHandle);
- LLFile::close(h);
- }
- }
-
- // detect whether the wrapped LLFILE is open or not
- explicit operator bool() const { return bool(mFileHandle); }
- bool operator!() { return ! mFileHandle; }
-
- // LLUniqueFile should be usable for any operation that accepts LLFILE*
- // (or FILE* for that matter)
- operator LLFILE*() const { return mFileHandle; }
+ /// converts a string containing a path in utf8 encoding into an explicit filesystem path
+ static std::filesystem::path utf8StringToPath(const std::string& pathname);
+ ///< @returns the path as a std::filesystem::path
private:
- LLFILE* mFileHandle;
+#if LL_WINDOWS
+ typedef HANDLE llfile_handle_t;
+ const llfile_handle_t InvalidHandle = INVALID_HANDLE_VALUE;
+#else
+ typedef int llfile_handle_t;
+ const llfile_handle_t InvalidHandle = -1;
+#endif
+
+ //----------------------------------------------------------------------------------------
+ // static member functions
+ //----------------------------------------------------------------------------------------
+#if LL_WINDOWS
+ /// convert a string containing a path in utf8 encoding into a Windows format std::wstring
+ static std::wstring utf8StringToWstring(const std::string& pathname);
+ ///< this will prepend the path with the Windows kernel object space prefix when the path is
+ /// equal or longer than MAX_PATH characters and do some sanitation on the path.
+ /// This allows the underlaying Windows APIs to process long path names. Do not pass such a path
+ /// to std::filesystem functions. These functions are not guaranteed to handle such paths properly.
+ /// It's only useful to pass the resulting string buffer to Microsoft Windows widechar APIs or
+ /// the Microsoft C runtime widechar file functions.
+ ///
+ /// Example:
+ ///
+ /// std::wstring file_path = utf8StringToWstring(filename);
+ /// HANDLE CreateFileW(file_path.c_str(), ......);
+ ///
+ /// @returns the path as a std::wstring path
+#endif
+ llfile_handle_t mHandle;
+ std::ios_base::openmode mOpen; // Used to emulate std::ios_base::app under Windows
};
#if LL_WINDOWS
diff --git a/indra/llcrashlogger/llcrashlock.cpp b/indra/llcrashlogger/llcrashlock.cpp
index bc34f6798f..731b53b7e9 100644..100755
--- a/indra/llcrashlogger/llcrashlock.cpp
+++ b/indra/llcrashlogger/llcrashlock.cpp
@@ -188,12 +188,7 @@ LLSD LLCrashLock::getProcessList()
//static
bool LLCrashLock::fileExists(std::string filename)
{
-#ifdef LL_WINDOWS // or BOOST_WINDOWS_API
- boost::filesystem::path file_path(ll_convert<std::wstring>(filename));
-#else
- boost::filesystem::path file_path(filename);
-#endif
- return boost::filesystem::exists(file_path);
+ return LLFile::exists(filename);
}
void LLCrashLock::cleanupProcess(std::string proc_dir)
diff --git a/indra/llfilesystem/lldir.cpp b/indra/llfilesystem/lldir.cpp
index 190539cea5..4c51cc12ab 100644..100755
--- a/indra/llfilesystem/lldir.cpp
+++ b/indra/llfilesystem/lldir.cpp
@@ -94,29 +94,19 @@ LLDir::~LLDir()
std::vector<std::string> LLDir::getFilesInDir(const std::string &dirname)
{
//Returns a vector of fullpath filenames.
-
-#ifdef LL_WINDOWS // or BOOST_WINDOWS_API
- boost::filesystem::path p(ll_convert<std::wstring>(dirname));
-#else
- boost::filesystem::path p(dirname);
-#endif
-
+ std::filesystem::path p = LLFile::utf8StringToPath(dirname);
std::vector<std::string> v;
-
- boost::system::error_code ec;
- if (exists(p, ec) && !ec.failed())
+ std::error_code ec;
+ if (std::filesystem::is_directory(p, ec) && !ec)
{
- if (is_directory(p, ec) && !ec.failed())
+ std::filesystem::directory_iterator end_iter;
+ for (std::filesystem::directory_iterator dir_itr(p);
+ dir_itr != end_iter;
+ ++dir_itr)
{
- boost::filesystem::directory_iterator end_iter;
- for (boost::filesystem::directory_iterator dir_itr(p);
- dir_itr != end_iter;
- ++dir_itr)
+ if (std::filesystem::is_regular_file(dir_itr->status()))
{
- if (boost::filesystem::is_regular_file(dir_itr->status()))
- {
- v.push_back(dir_itr->path().filename().string());
- }
+ v.push_back(dir_itr->path().string());
}
}
}
@@ -186,28 +176,23 @@ U32 LLDir::deleteDirAndContents(const std::string& dir_name)
//Removes the directory and its contents. Returns number of files deleted.
U32 num_deleted = 0;
+ std::filesystem::path dir_path = LLFile::utf8StringToPath(dir_name);
try
{
-#ifdef LL_WINDOWS // or BOOST_WINDOWS_API
- boost::filesystem::path dir_path(ll_convert<std::wstring>(dir_name));
-#else
- boost::filesystem::path dir_path(dir_name);
-#endif
-
- if (boost::filesystem::exists(dir_path))
+ if (std::filesystem::is_directory(dir_path))
{
- if (!boost::filesystem::is_empty(dir_path))
+ if (!std::filesystem::is_empty(dir_path))
{ // Directory has content
- num_deleted = (U32)boost::filesystem::remove_all(dir_path);
+ num_deleted = (U32)std::filesystem::remove_all(dir_path);
}
else
{ // Directory is empty
- boost::filesystem::remove(dir_path);
+ std::filesystem::remove(dir_path);
}
}
}
- catch (boost::filesystem::filesystem_error &er)
+ catch (std::filesystem::filesystem_error &er)
{
LL_WARNS() << "Failed to delete " << dir_name << " with error " << er.code().message() << LL_ENDL;
}
@@ -1105,15 +1090,15 @@ void dir_exists_or_crash(const std::string &dir_name)
#if LL_WINDOWS
// *FIX: lame - it doesn't do the same thing on windows. not so
// important since we don't deploy simulator to windows boxes.
- LLFile::mkdir(dir_name, 0700);
+ LLFile::mkdir(dir_name);
#else
- struct stat dir_stat;
+ llstat dir_stat;
if(0 != LLFile::stat(dir_name, &dir_stat))
{
S32 stat_rv = errno;
if(ENOENT == stat_rv)
{
- if(0 != LLFile::mkdir(dir_name, 0700)) // octal
+ if(0 != LLFile::mkdir(dir_name))
{
LL_ERRS() << "Unable to create directory: " << dir_name << LL_ENDL;
}
diff --git a/indra/llfilesystem/lldir_win32.cpp b/indra/llfilesystem/lldir_win32.cpp
index 58c080c982..2b478e5dce 100644..100755
--- a/indra/llfilesystem/lldir_win32.cpp
+++ b/indra/llfilesystem/lldir_win32.cpp
@@ -376,18 +376,7 @@ std::string LLDir_Win32::getCurPath()
bool LLDir_Win32::fileExists(const std::string &filename) const
{
- llstat stat_data;
- // Check the age of the file
- // Now, we see if the files we've gathered are recent...
- int res = LLFile::stat(filename, &stat_data);
- if (!res)
- {
- return true;
- }
- else
- {
- return false;
- }
+ return LLFile::exists(filename);
}
diff --git a/indra/llfilesystem/llfilesystem.cpp b/indra/llfilesystem/llfilesystem.cpp
index 541266af4f..4238dd64e9 100644..100755
--- a/indra/llfilesystem/llfilesystem.cpp
+++ b/indra/llfilesystem/llfilesystem.cpp
@@ -77,13 +77,8 @@ bool LLFileSystem::getExists(const LLUUID& file_id, const LLAssetType::EType fil
LL_PROFILE_ZONE_SCOPED;
const std::string filename = LLDiskCache::metaDataToFilepath(file_id, file_type);
- llifstream file(filename, std::ios::binary);
- if (file.is_open())
- {
- file.seekg(0, std::ios::end);
- return file.tellg() > 0;
- }
- return false;
+ // not only test for existence but for the file to be not empty
+ return LLFile::size(filename) > 0;
}
// static
@@ -120,15 +115,7 @@ S32 LLFileSystem::getFileSize(const LLUUID& file_id, const LLAssetType::EType fi
{
const std::string filename = LLDiskCache::metaDataToFilepath(file_id, file_type);
- S32 file_size = 0;
- llifstream file(filename, std::ios::binary);
- if (file.is_open())
- {
- file.seekg(0, std::ios::end);
- file_size = (S32)file.tellg();
- }
-
- return file_size;
+ return (S32)LLFile::size(filename);
}
bool LLFileSystem::read(U8* buffer, S32 bytes)
diff --git a/indra/llfilesystem/tests/lldir_test.cpp b/indra/llfilesystem/tests/lldir_test.cpp
index 13db6fad80..17e4ffecf1 100644..100755
--- a/indra/llfilesystem/tests/lldir_test.cpp
+++ b/indra/llfilesystem/tests/lldir_test.cpp
@@ -558,8 +558,8 @@ namespace tut
LLFile::remove(dir1files[i]);
LLFile::remove(dir2files[i]);
}
- LLFile::rmdir(dir1);
- LLFile::rmdir(dir2);
+ LLFile::remove(dir1);
+ LLFile::remove(dir2);
}
template<> template<>
diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp
index 2c35a6acae..49df9cd88c 100644..100755
--- a/indra/llrender/llshadermgr.cpp
+++ b/indra/llrender/llshadermgr.cpp
@@ -1059,7 +1059,7 @@ void LLShaderMgr::clearShaderCache()
LL_INFOS("ShaderMgr") << "Removing shader cache at " << shader_cache << LL_ENDL;
const std::string mask = "*";
gDirUtilp->deleteFilesInDir(shader_cache, mask);
- LLFile::rmdir(shader_cache);
+ LLFile::remove(shader_cache);
mShaderBinaryCache.clear();
}
@@ -1131,11 +1131,11 @@ bool LLShaderMgr::loadCachedProgramBinary(LLGLSLShader* shader)
{
std::vector<U8> in_data;
in_data.resize(shader_info.mBinaryLength);
-
- LLUniqueFile filep = LLFile::fopen(in_path, "rb");
+ std::error_code ec;
+ LLFile filep = LLFile(in_path, LLFile::in | LLFile::binary, ec);
if (filep)
{
- size_t result = fread(in_data.data(), sizeof(U8), in_data.size(), filep);
+ size_t result = filep.read(in_data.data(), in_data.size(), ec);
filep.close();
if (result == in_data.size())
@@ -1180,11 +1180,12 @@ bool LLShaderMgr::saveCachedProgramBinary(LLGLSLShader* shader)
if (error == GL_NO_ERROR)
{
std::string out_path = gDirUtilp->add(mShaderCacheDir, shader->mShaderHash.asString() + ".shaderbin");
- LLUniqueFile outfile = LLFile::fopen(out_path, "wb");
- if (outfile)
+ std::error_code ec;
+ LLFile filep = LLFile(out_path, LLFile::out | LLFile::binary, ec);
+ if (filep)
{
- fwrite(program_binary.data(), sizeof(U8), program_binary.size(), outfile);
- outfile.close();
+ filep.write(program_binary.data(), program_binary.size(), ec);
+ filep.close();
binary_info.mLastUsedTime = (F32)LLTimer::getTotalSeconds();
diff --git a/indra/llxml/tests/llcontrol_test.cpp b/indra/llxml/tests/llcontrol_test.cpp
index 4192e029c5..e15117fc29 100644..100755
--- a/indra/llxml/tests/llcontrol_test.cpp
+++ b/indra/llxml/tests/llcontrol_test.cpp
@@ -69,7 +69,7 @@ namespace tut
LLFile::remove(filename);
}
LLFile::remove(mTestConfigFile);
- LLFile::rmdir(mTestConfigDir);
+ LLFile::remove(mTestConfigDir);
}
void writeSettingsFile(const LLSD& config)
{
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index f6e3139cc4..962a91bb73 100644..100755
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -3031,13 +3031,11 @@ void LLAppViewer::initStrings()
}
else
{
- llstat st;
- int rc = LLFile::stat(strings_path_full, &st);
- if (rc != 0)
+ if (!LLFile::exists(strings_path_full))
{
- crash_reason = "The file '" + strings_path_full + "' failed to get status. Error code: " + std::to_string(rc);
+ crash_reason = "The file '" + strings_path_full + "' doesn't seem to exist";
}
- else if (S_ISDIR(st.st_mode))
+ else if (LLFile::isdir(strings_path_full))
{
crash_reason = "The filename '" + strings_path_full + "' is a directory name";
}
@@ -4293,7 +4291,7 @@ void LLAppViewer::migrateCacheDirectory()
LLFile::remove(ds_store);
}
#endif
- if (LLFile::rmdir(old_cache_dir) != 0)
+ if (LLFile::remove(old_cache_dir) != 0)
{
LL_WARNS() << "could not delete old cache directory " << old_cache_dir << LL_ENDL;
}
diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp
index c5c1e01538..66066a45b2 100644..100755
--- a/indra/newview/llfloaterpreference.cpp
+++ b/indra/newview/llfloaterpreference.cpp
@@ -1500,7 +1500,7 @@ bool LLFloaterPreference::moveTranscriptsAndLog()
//Couldn't move the log and created a new directory so remove the new directory
if(madeDirectory)
{
- LLFile::rmdir(chatLogPath);
+ LLFile::remove(chatLogPath);
}
return false;
}
@@ -1526,7 +1526,7 @@ bool LLFloaterPreference::moveTranscriptsAndLog()
if(madeDirectory)
{
- LLFile::rmdir(chatLogPath);
+ LLFile::remove(chatLogPath);
}
return false;
@@ -2031,17 +2031,15 @@ void LLFloaterPreference::changed()
{
if (LLConversationLog::instance().getIsLoggingEnabled())
{
- getChild<LLButton>("clear_log")->setEnabled(LLConversationLog::instance().getConversations().size() > 0);
+ getChild<LLButton>("clear_log")->setEnabled(LLConversationLog::instance().getConversations().size() > 0);
}
else
{
// onClearLog clears list, then notifies changed() and only then clears file,
// so check presence of conversations before checking file, file will cleared later.
- llstat st;
bool has_logs = LLConversationLog::instance().getConversations().size() > 0
- && LLFile::stat(LLConversationLog::instance().getFileName(), &st) == 0
- && S_ISREG(st.st_mode)
- && st.st_size > 0;
+ && LLFile::isfile(LLConversationLog::instance().getFileName())
+ && LLFile::size(LLConversationLog::instance().getFileName()) > 0;
getChild<LLButton>("clear_log")->setEnabled(has_logs);
}
diff --git a/indra/newview/llfloateruipreview.cpp b/indra/newview/llfloateruipreview.cpp
index c3bc24c6b9..db98a70573 100644..100755
--- a/indra/newview/llfloateruipreview.cpp
+++ b/indra/newview/llfloateruipreview.cpp
@@ -892,8 +892,7 @@ void LLFloaterUIPreview::displayFloater(bool click, S32 ID)
// Add localization to title so user knows whether it's localized or defaulted to en
std::string full_path = getLocalizedDirectory() + path;
std::string floater_lang = "EN";
- llstat dummy;
- if(!LLFile::stat(full_path.c_str(), &dummy)) // if the file does not exist
+ if (LLFile::isfile(full_path.c_str())) // if the file does not exist
{
floater_lang = getLocStr(ID);
}
@@ -966,9 +965,8 @@ void LLFloaterUIPreview::onClickEditFloater()
}
file_path = getLocalizedDirectory() + file_name;
- // stat file to see if it exists (some localized versions may not have it there are no diffs, and then we try to open an nonexistent file)
- llstat dummy;
- if(LLFile::stat(file_path.c_str(), &dummy)) // if the file does not exist
+ // does it exist? (some localized versions may not have it there are no diffs, and then we try to open an nonexistent file
+ if(!LLFile::isfile(file_path.c_str())) // if the file does not exist
{
popupAndPrintWarning("No file for this floater exists in the selected localization. Opening the EN version instead.");
file_path = get_xui_dir() + mDelim + "en" + mDelim + file_name; // open the en version instead, by default
@@ -1124,8 +1122,7 @@ void LLFloaterUIPreview::onClickToggleDiffHighlighting()
error = true;
}
- llstat dummy;
- if(LLFile::stat(path_in_textfield.c_str(), &dummy) && !error) // check if the file exists (empty check is reduntant but useful for the informative error message)
+ if (!LLFile::isfile(path_in_textfield.c_str()) && !error) // check if the file exists (empty check is reduntant but useful for the informative error message)
{
std::string warning = std::string("Unable to highlight differences because an invalid path to a difference file was provided:\"") + path_in_textfield + "\"";
popupAndPrintWarning(warning);
diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp
index 9a991727b2..4fe661b055 100644..100755
--- a/indra/newview/llpreviewnotecard.cpp
+++ b/indra/newview/llpreviewnotecard.cpp
@@ -575,8 +575,7 @@ void LLPreviewNotecard::syncExternal()
{
// Sync with external editor.
std::string tmp_file = getTmpFileName();
- llstat s;
- if (LLFile::stat(tmp_file, &s) == 0) // file exists
+ if (LLFile::isfile(tmp_file)) // file exists
{
if (mLiveFile) mLiveFile->ignoreNextUpdate();
writeToFile(tmp_file);
diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp
index c2aa4925bd..2c436198e3 100644..100755
--- a/indra/newview/llpreviewscript.cpp
+++ b/indra/newview/llpreviewscript.cpp
@@ -694,8 +694,7 @@ void LLScriptEdCore::sync()
if (mLiveFile)
{
std::string tmp_file = mLiveFile->filename();
- llstat s;
- if (LLFile::stat(tmp_file, &s) == 0) // file exists
+ if (LLFile::isfile(tmp_file)) // file exists
{
mLiveFile->ignoreNextUpdate();
writeToFile(tmp_file);
diff --git a/indra/newview/lltexturecache.cpp b/indra/newview/lltexturecache.cpp
index e59cf70177..ef7542fe2e 100644..100755
--- a/indra/newview/lltexturecache.cpp
+++ b/indra/newview/lltexturecache.cpp
@@ -1614,7 +1614,7 @@ void LLTextureCache::purgeAllTextures(bool purge_directories)
gDirUtilp->deleteFilesInDir(mTexturesDirName, mask); // headers, fast cache
if (purge_directories)
{
- LLFile::rmdir(mTexturesDirName);
+ LLFile::remove(mTexturesDirName);
}
}
mHeaderIDMap.clear();
diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp
index 35ac7919ac..7534d778a5 100644..100755
--- a/indra/newview/llviewerdisplay.cpp
+++ b/indra/newview/llviewerdisplay.cpp
@@ -1140,15 +1140,12 @@ std::string getProfileStatsFilename()
// same second), may produce (e.g.) sec==61, but avoids collisions and
// preserves chronological filename sort order.
std::string name;
- std::error_code ec;
do
{
// base + missing 2-digit seconds, append ".json"
// post-increment sec in case we have to try again
name = stringize(base, std::setw(2), std::setfill('0'), sec++, ".json");
- } while (std::filesystem::exists(fsyspath(name), ec));
- // Ignoring ec means we might potentially return a name that does already
- // exist -- but if we can't check its existence, what more can we do?
+ } while (LLFile::exists(fsyspath(name)));
return name;
}
diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp
index a77b9f6103..5cb05460bc 100644..100755
--- a/indra/newview/llviewermedia.cpp
+++ b/indra/newview/llviewermedia.cpp
@@ -1827,12 +1827,11 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_
user_data_path_cache += gDirUtilp->getDirDelimiter();
// See if the plugin executable exists
- llstat s;
- if(LLFile::stat(launcher_name, &s))
+ if (!LLFile::isfile(launcher_name))
{
LL_WARNS_ONCE("Media") << "Couldn't find launcher at " << launcher_name << LL_ENDL;
}
- else if(LLFile::stat(plugin_name, &s))
+ else if (!LLFile::isfile(plugin_name))
{
#if !LL_LINUX
LL_WARNS_ONCE("Media") << "Couldn't find plugin at " << plugin_name << LL_ENDL;
diff --git a/indra/newview/llvocache.cpp b/indra/newview/llvocache.cpp
index 9537ad745b..cdc41baa88 100644..100755
--- a/indra/newview/llvocache.cpp
+++ b/indra/newview/llvocache.cpp
@@ -1250,7 +1250,7 @@ void LLVOCache::removeCache(ELLPath location, bool started)
std::string cache_dir = gDirUtilp->getExpandedFilename(location, object_cache_dirname);
LL_INFOS() << "Removing cache at " << cache_dir << LL_ENDL;
gDirUtilp->deleteFilesInDir(cache_dir, mask); //delete all files
- LLFile::rmdir(cache_dir);
+ LLFile::remove(cache_dir);
clearCacheInMemory();
mInitialized = false;
diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp
index d132cbfa36..e58a6577f1 100644..100755
--- a/indra/newview/llvoicevivox.cpp
+++ b/indra/newview/llvoicevivox.cpp
@@ -943,8 +943,7 @@ bool LLVivoxVoiceClient::startAndLaunchDaemon()
gDirUtilp->append(exe_path, "SLVoice");
#endif
// See if the vivox executable exists
- llstat s;
- if (!LLFile::stat(exe_path, &s))
+ if (LLFile::isfile(exe_path))
{
// vivox executable exists. Build the command line and launch the daemon.
LLProcess::Params params;
diff --git a/indra/test/llmessageconfig_tut.cpp b/indra/test/llmessageconfig_tut.cpp
index 93443467a2..b8942e99b3 100644..100755
--- a/indra/test/llmessageconfig_tut.cpp
+++ b/indra/test/llmessageconfig_tut.cpp
@@ -62,7 +62,7 @@ namespace tut
int rmfile = LLFile::remove((mTestConfigDir + "/message.xml"));
ensure_equals("rmfile value", rmfile, 0);
// rm temp dir
- int rmdir = LLFile::rmdir(mTestConfigDir);
+ int rmdir = LLFile::remove(mTestConfigDir);
ensure_equals("rmdir value", rmdir, 0);
}
diff --git a/indra/test/message_tut.cpp b/indra/test/message_tut.cpp
index 11cd710ef6..6fb2b92803 100644..100755
--- a/indra/test/message_tut.cpp
+++ b/indra/test/message_tut.cpp
@@ -113,7 +113,7 @@ namespace tut
ensure_equals("rmfile value", rmfile, 0);
// rm temp dir
- int rmdir = LLFile::rmdir(mTestConfigDir);
+ int rmdir = LLFile::remove(mTestConfigDir);
ensure_equals("rmdir value", rmdir, 0);
}