summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrey Kleshchev <117672381+akleshchev@users.noreply.github.com>2025-12-11 01:42:52 +0200
committerGitHub <noreply@github.com>2025-12-11 01:42:52 +0200
commitc92b0b74cbd963cd79d1cb7754256b801f1479b1 (patch)
tree5f3cf34559856bdafd950acf0c9a4d6dec59f0bc
parentdbbce566e7d66c907dde7bd6c4212b0954b9a5e1 (diff)
Revert #4899 "Add more functionality to LLFile and cleanup LLAPRFile"
Interferes with linux work, will be moved to a different branch and applied separately.
-rwxr-xr-xindra/llaudio/llaudiodecodemgr.cpp2
-rw-r--r--indra/llcommon/llapp.cpp4
-rw-r--r--indra/llcommon/llapr.cpp220
-rw-r--r--indra/llcommon/llapr.h19
-rw-r--r--[-rwxr-xr-x]indra/llcommon/llerror.cpp10
-rw-r--r--[-rwxr-xr-x]indra/llcommon/llfile.cpp1134
-rw-r--r--[-rwxr-xr-x]indra/llcommon/llfile.h488
-rw-r--r--[-rwxr-xr-x]indra/llcrashlogger/llcrashlock.cpp7
-rw-r--r--[-rwxr-xr-x]indra/llfilesystem/lldir.cpp56
-rw-r--r--[-rwxr-xr-x]indra/llfilesystem/lldir_win32.cpp13
-rw-r--r--[-rwxr-xr-x]indra/llfilesystem/llfilesystem.cpp29
-rw-r--r--indra/llfilesystem/llfilesystem.h4
-rw-r--r--[-rwxr-xr-x]indra/llfilesystem/tests/lldir_test.cpp4
-rw-r--r--indra/llmessage/llassetstorage.cpp2
-rw-r--r--[-rwxr-xr-x]indra/llrender/llshadermgr.cpp22
-rw-r--r--[-rwxr-xr-x]indra/llxml/tests/llcontrol_test.cpp2
-rw-r--r--[-rwxr-xr-x]indra/newview/llappviewer.cpp28
-rw-r--r--[-rwxr-xr-x]indra/newview/llfloaterpreference.cpp12
-rw-r--r--[-rwxr-xr-x]indra/newview/llfloateruipreview.cpp37
-rw-r--r--[-rwxr-xr-x]indra/newview/llpreviewnotecard.cpp3
-rw-r--r--[-rwxr-xr-x]indra/newview/llpreviewscript.cpp3
-rw-r--r--[-rwxr-xr-x]indra/newview/lltexturecache.cpp89
-rw-r--r--indra/newview/llviewerassetstorage.cpp2
-rw-r--r--indra/newview/llviewerassetupload.cpp39
-rw-r--r--[-rwxr-xr-x]indra/newview/llviewerdisplay.cpp5
-rw-r--r--[-rwxr-xr-x]indra/newview/llviewermedia.cpp5
-rw-r--r--[-rwxr-xr-x]indra/newview/llvocache.cpp6
-rw-r--r--[-rwxr-xr-x]indra/newview/llvoicevivox.cpp3
-rw-r--r--[-rwxr-xr-x]indra/test/CMakeLists.txt13
-rwxr-xr-xindra/test/llfile_tut.cpp253
-rw-r--r--[-rwxr-xr-x]indra/test/llmessageconfig_tut.cpp2
-rw-r--r--[-rwxr-xr-x]indra/test/message_tut.cpp2
32 files changed, 909 insertions, 1609 deletions
diff --git a/indra/llaudio/llaudiodecodemgr.cpp b/indra/llaudio/llaudiodecodemgr.cpp
index 232b429130..d8a6fffea6 100755
--- a/indra/llaudio/llaudiodecodemgr.cpp
+++ b/indra/llaudio/llaudiodecodemgr.cpp
@@ -199,7 +199,7 @@ bool LLVorbisDecodeState::initDecode()
LL_DEBUGS("AudioEngine") << "Initing decode from vfile: " << mUUID << LL_ENDL;
mInFilep = new LLFileSystem(mUUID, LLAssetType::AT_SOUND);
- if (!mInFilep || mInFilep->getSize() <= 0)
+ if (!mInFilep || !mInFilep->getSize())
{
LL_WARNS("AudioEngine") << "unable to open vorbis source vfile for reading" << LL_ENDL;
delete mInFilep;
diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp
index 5829d2be49..c532620daa 100644
--- a/indra/llcommon/llapp.cpp
+++ b/indra/llcommon/llapp.cpp
@@ -37,7 +37,7 @@
#endif
#include "llcommon.h"
-
+#include "llapr.h"
#include "llerrorcontrol.h"
#include "llframetimer.h"
#include "lllivefile.h"
@@ -53,8 +53,6 @@
//
// Signal handling
#ifndef LL_WINDOWS
-#include "apr_signal.h"
-
# include <signal.h>
# include <unistd.h> // for fork()
void setup_signals();
diff --git a/indra/llcommon/llapr.cpp b/indra/llcommon/llapr.cpp
index 22bed48542..eeff2694a7 100644
--- a/indra/llcommon/llapr.cpp
+++ b/indra/llcommon/llapr.cpp
@@ -526,6 +526,226 @@ S32 LLAPRFile::seek(apr_file_t* file_handle, apr_seek_where_t where, S32 offset)
}
}
+//static
+S32 LLAPRFile::readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool)
+{
+ LL_PROFILE_ZONE_SCOPED;
+ //*****************************************
+ LLAPRFilePoolScope scope(pool);
+ apr_file_t* file_handle = open(filename, scope.getVolatileAPRPool(), APR_READ|APR_BINARY);
+ //*****************************************
+ if (!file_handle)
+ {
+ return 0;
+ }
+
+ llassert(offset >= 0);
+
+ if (offset > 0)
+ offset = LLAPRFile::seek(file_handle, APR_SET, offset);
+
+ apr_size_t bytes_read;
+ if (offset < 0)
+ {
+ bytes_read = 0;
+ }
+ else
+ {
+ bytes_read = nbytes ;
+ apr_status_t s = apr_file_read(file_handle, buf, &bytes_read);
+ if (s != APR_SUCCESS)
+ {
+ LL_WARNS("APR") << " Attempting to read filename: " << filename << LL_ENDL;
+ ll_apr_warn_status(s);
+ bytes_read = 0;
+ }
+ else
+ {
+ llassert_always(bytes_read <= 0x7fffffff);
+ }
+ }
+
+ //*****************************************
+ close(file_handle) ;
+ //*****************************************
+ return (S32)bytes_read;
+}
+
+//static
+S32 LLAPRFile::writeEx(const std::string& filename, const void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool)
+{
+ LL_PROFILE_ZONE_SCOPED;
+ apr_int32_t flags = APR_CREATE|APR_WRITE|APR_BINARY;
+ if (offset < 0)
+ {
+ flags |= APR_APPEND;
+ offset = 0;
+ }
+
+ //*****************************************
+ LLAPRFilePoolScope scope(pool);
+ apr_file_t* file_handle = open(filename, scope.getVolatileAPRPool(), flags);
+ //*****************************************
+ if (!file_handle)
+ {
+ return 0;
+ }
+
+ if (offset > 0)
+ {
+ offset = LLAPRFile::seek(file_handle, APR_SET, offset);
+ }
+
+ apr_size_t bytes_written;
+ if (offset < 0)
+ {
+ bytes_written = 0;
+ }
+ else
+ {
+ bytes_written = nbytes ;
+ apr_status_t s = apr_file_write(file_handle, buf, &bytes_written);
+ if (s != APR_SUCCESS)
+ {
+ LL_WARNS("APR") << "Attempting to write filename: " << filename << LL_ENDL;
+ if (APR_STATUS_IS_ENOSPC(s))
+ {
+ LLApp::notifyOutOfDiskSpace();
+ }
+ ll_apr_warn_status(s);
+ bytes_written = 0;
+ }
+ else
+ {
+ llassert_always(bytes_written <= 0x7fffffff);
+ }
+ }
+
+ //*****************************************
+ LLAPRFile::close(file_handle);
+ //*****************************************
+
+ return (S32)bytes_written;
+}
+
+//static
+bool LLAPRFile::remove(const std::string& filename, LLVolatileAPRPool* pool)
+{
+ apr_status_t s;
+
+ LLAPRFilePoolScope scope(pool);
+ s = apr_file_remove(filename.c_str(), scope.getVolatileAPRPool());
+
+ if (s != APR_SUCCESS)
+ {
+ ll_apr_warn_status(s);
+ LL_WARNS("APR") << " Attempting to remove filename: " << filename << LL_ENDL;
+ return false;
+ }
+ return true;
+}
+
+//static
+bool LLAPRFile::rename(const std::string& filename, const std::string& newname, LLVolatileAPRPool* pool)
+{
+ apr_status_t s;
+
+ LLAPRFilePoolScope scope(pool);
+ s = apr_file_rename(filename.c_str(), newname.c_str(), scope.getVolatileAPRPool());
+
+ if (s != APR_SUCCESS)
+ {
+ ll_apr_warn_status(s);
+ LL_WARNS("APR") << " Attempting to rename filename: " << filename << LL_ENDL;
+ return false;
+ }
+ return true;
+}
+
+//static
+bool LLAPRFile::isExist(const std::string& filename, LLVolatileAPRPool* pool, apr_int32_t flags)
+{
+ apr_file_t* apr_file;
+ apr_status_t s;
+
+ LLAPRFilePoolScope scope(pool);
+ s = apr_file_open(&apr_file, filename.c_str(), flags, APR_OS_DEFAULT, scope.getVolatileAPRPool());
+
+ if (s != APR_SUCCESS || !apr_file)
+ {
+ return false;
+ }
+ else
+ {
+ apr_file_close(apr_file) ;
+ return true;
+ }
+}
+
+//static
+S32 LLAPRFile::size(const std::string& filename, LLVolatileAPRPool* pool)
+{
+ apr_file_t* apr_file;
+ apr_finfo_t info;
+ apr_status_t s;
+
+ LLAPRFilePoolScope scope(pool);
+ s = apr_file_open(&apr_file, filename.c_str(), APR_READ, APR_OS_DEFAULT, scope.getVolatileAPRPool());
+
+ if (s != APR_SUCCESS || !apr_file)
+ {
+ return 0;
+ }
+ else
+ {
+ apr_status_t s = apr_file_info_get(&info, APR_FINFO_SIZE, apr_file);
+
+ apr_file_close(apr_file) ;
+
+ if (s == APR_SUCCESS)
+ {
+ return (S32)info.size;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+}
+
+//static
+bool LLAPRFile::makeDir(const std::string& dirname, LLVolatileAPRPool* pool)
+{
+ apr_status_t s;
+
+ LLAPRFilePoolScope scope(pool);
+ s = apr_dir_make(dirname.c_str(), APR_FPROT_OS_DEFAULT, scope.getVolatileAPRPool());
+
+ if (s != APR_SUCCESS)
+ {
+ ll_apr_warn_status(s);
+ LL_WARNS("APR") << " Attempting to make directory: " << dirname << LL_ENDL;
+ return false;
+ }
+ return true;
+}
+
+//static
+bool LLAPRFile::removeDir(const std::string& dirname, LLVolatileAPRPool* pool)
+{
+ apr_status_t s;
+
+ LLAPRFilePoolScope scope(pool);
+ s = apr_file_remove(dirname.c_str(), scope.getVolatileAPRPool());
+
+ if (s != APR_SUCCESS)
+ {
+ ll_apr_warn_status(s);
+ LL_WARNS("APR") << " Attempting to remove directory: " << dirname << LL_ENDL;
+ return false;
+ }
+ return true;
+}
//
//end of static components of LLAPRFile
//*******************************************************************************************************************************
diff --git a/indra/llcommon/llapr.h b/indra/llcommon/llapr.h
index 72458c6302..11e474b5dd 100644
--- a/indra/llcommon/llapr.h
+++ b/indra/llcommon/llapr.h
@@ -35,6 +35,12 @@
#include "llwin32headers.h"
#include "apr_thread_proc.h"
+#include "apr_getopt.h"
+#include "apr_signal.h"
+
+#include "llstring.h"
+
+#include "mutex.h"
struct apr_dso_handle_t;
/**
@@ -178,7 +184,20 @@ private:
static apr_file_t* open(const std::string& filename, apr_pool_t* apr_pool, apr_int32_t flags);
static apr_status_t close(apr_file_t* file) ;
static S32 seek(apr_file_t* file, apr_seek_where_t where, S32 offset);
+public:
+ // returns false if failure:
+ static bool remove(const std::string& filename, LLVolatileAPRPool* pool = NULL);
+ static bool rename(const std::string& filename, const std::string& newname, LLVolatileAPRPool* pool = NULL);
+ static bool isExist(const std::string& filename, LLVolatileAPRPool* pool = NULL, apr_int32_t flags = APR_READ);
+ static S32 size(const std::string& filename, LLVolatileAPRPool* pool = NULL);
+ static bool makeDir(const std::string& dirname, LLVolatileAPRPool* pool = NULL);
+ static bool removeDir(const std::string& dirname, LLVolatileAPRPool* pool = NULL);
+
+ // Returns bytes read/written, 0 if read/write fails:
+ static S32 readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool = NULL);
+ static S32 writeEx(const std::string& filename, const void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool = NULL); // offset<0 means append
//*******************************************************************************************************************************
};
+
#endif // LL_LLAPR_H
diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp
index bfa8bca224..b14464382b 100755..100644
--- a/indra/llcommon/llerror.cpp
+++ b/indra/llcommon/llerror.cpp
@@ -435,9 +435,13 @@ namespace
std::string file = user_dir + "/logcontrol-dev.xml";
- if (!LLFile::isfile(file))
- {
- file = app_dir + "/logcontrol.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";
}
return * new LogControlFile(file);
// NB: This instance is never freed
diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp
index a1d41cdf73..a539e4fe28 100755..100644
--- a/indra/llcommon/llfile.cpp
+++ b/indra/llcommon/llfile.cpp
@@ -29,17 +29,22 @@
#include "linden_common.h"
#include "llfile.h"
+#include "llstring.h"
#include "llerror.h"
#include "stringize.h"
#if LL_WINDOWS
-#include <fcntl.h>
+#include "llwin32headers.h"
+#include <vector>
#else
#include <errno.h>
-#include <sys/file.h>
#endif
-// Some of the methods below use OS-level functions that mess with errno. Wrap
+using namespace std;
+
+static std::string empty;
+
+// Many of the methods below use OS-level functions that mess with errno. Wrap
// variants of strerror() to report errors.
#if LL_WINDOWS
@@ -74,7 +79,6 @@ 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
@@ -105,25 +109,22 @@ static errentry const errtable[]
{ ERROR_NOT_ENOUGH_QUOTA, ENOMEM } // 1816
};
-static int get_errno_from_oserror(int oserr)
+static int set_errno_from_oserror(unsigned long 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)
{
- return entry.errcode;
+ _set_errno(entry.errcode);
+ return -1;
}
}
- return EINVAL;
-}
-static int set_errno_from_oserror(unsigned long oserr)
-{
- _set_errno(get_errno_from_oserror(oserr));
+ _set_errno(EINVAL);
return -1;
}
@@ -135,8 +136,69 @@ std::string strerr(int errn)
return buffer;
}
-#else
+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);
+}
+
+static unsigned short get_fileattr(const std::wstring& utf16path, bool dontFollowSymLink = false)
+{
+ unsigned long flags = FILE_FLAG_BACKUP_SEMANTICS;
+ if (dontFollowSymLink)
+ {
+ 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])));
+ unsigned short 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 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 st_mode;
+ }
+ }
+ // Retrieve last error and set errno before calling CloseHandle()
+ set_errno_from_oserror(GetLastError());
+
+ if (file_handle != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(file_handle);
+ }
+ return 0;
+}
+
+#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
@@ -183,50 +245,9 @@ 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 suppress_warning = 0)
+static int warnif(const std::string& desc, const std::string& filename, int rc, int accept = 0)
{
if (rc < 0)
{
@@ -235,930 +256,319 @@ static int warnif(const std::string& desc, const std::string& filename, int rc,
// For certain operations, a particular errno value might be
// acceptable -- e.g. stat() could permit ENOENT, mkdir() could permit
- // EEXIST. Don't log a warning if caller explicitly says this errno is okay.
- if (errn != suppress_warning)
+ // 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 PROCESS_LOCKING_CHECK
+#if 0 && LL_WINDOWS // turn on to debug file-locking problems
// 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
{
- find_locking_process(filename);
+ // 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;
}
-#endif
+#endif // LL_WINDOWS hack to identify processes holding file open
}
return rc;
}
-static int warnif(const std::string& desc, const std::string& filename, const std::error_code& ec, int suppress_warning = 0)
+// static
+int LLFile::mkdir(const std::string& dirname, int perms)
{
- if (ec)
- {
- // get Posix errno from the std::error_code so we can compare it to the suppress_warning 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 != suppress_warning)
- {
- 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;
-}
-
+ // 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 LL_WINDOWS
-
-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)
+ // permissions are ignored on Windows
+ int rc = 0;
+ std::wstring utf16dirname = utf8path_to_wstring(dirname);
+ if (!CreateDirectoryW(utf16dirname.c_str(), nullptr))
{
- return CREATE_NEW; // create if it does not exist, otherwise fail
- }
- if (omode & LLFile::trunc)
- {
- if (!(omode & LLFile::out))
+ // Only treat other errors than an already existing file as a real error
+ unsigned long oserr = GetLastError();
+ if (oserr != ERROR_ALREADY_EXISTS)
{
- return TRUNCATE_EXISTING; // open and truncate if it exists, otherwise fail
+ rc = set_errno_from_oserror(oserr);
}
- return CREATE_ALWAYS; // open and truncate if it exists, otherwise create it
- }
- if (!(omode & LLFile::out))
- {
- return OPEN_EXISTING; // open if it 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)
- {
- 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))
+ int rc = ::mkdir(dirname.c_str(), (mode_t)perms);
+ if (rc < 0 && errno == EEXIST)
{
- 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)
- {
- 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;
+ // this is not the error you want, move along
+ return 0;
}
- 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;
+ // anything else might be a problem
+ return warnif("mkdir", dirname, rc);
}
-//----------------------------------------------------------------------------------------
-// class member functions
-//----------------------------------------------------------------------------------------
-int LLFile::open(const std::string& filename, std::ios_base::openmode omode, std::error_code& ec, int perm)
+// static
+int LLFile::rmdir(const std::string& dirname, int suppress_error)
{
- 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);
- // The dwShareMode = share parameter takes care of locking the file for other processes if indicated,
- // no need to do anything else for file locking here
+ std::wstring utf16dirname = utf8path_to_wstring(dirname);
+ int rc = _wrmdir(utf16dirname.c_str());
#else
- int oflags = decode_open_mode(omode);
- int lmode = omode & LLFile::lock_mask;
- mHandle = ::open(filename.c_str(), oflags, perm);
- if (mHandle != InvalidHandle && lmode && lock(lmode | LLFile::noblock, ec) != 0)
- {
- close();
- return -1;
- }
+ int rc = ::rmdir(dirname.c_str());
#endif
- 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);
+ return warnif("rmdir", dirname, rc, suppress_error);
}
-S64 LLFile::size(std::error_code& ec)
+// static
+LLFILE* LLFile::fopen(const std::string& filename, const char* mode)
{
#if LL_WINDOWS
- LARGE_INTEGER value = { 0 };
- if (GetFileSizeEx(mHandle, &value))
- {
- clear_error(ec);
- return value.QuadPart;
- }
+ 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());
#else
- struct stat statval;
- if (fstat(mHandle, &statval) == 0)
- {
- clear_error(ec);
- return statval.st_size;
- }
+ return ::fopen(filename.c_str(),mode);
#endif
- return set_ec_from_system_error(ec);
}
-S64 LLFile::tell(std::error_code& ec)
+// static
+int LLFile::close(LLFILE * file)
{
-#if LL_WINDOWS
- LARGE_INTEGER value = { 0 };
- if (SetFilePointerEx(mHandle, value, &value, FILE_CURRENT))
- {
- clear_error(ec);
- return value.QuadPart;
- }
-#else
- off_t offset = lseek(mHandle, 0, SEEK_CUR);
- if (offset != -1)
+ int ret_value = 0;
+ if (file)
{
- clear_error(ec);
- return offset;
+ ret_value = fclose(file);
}
-#endif
- return set_ec_from_system_error(ec);
-}
-
-int LLFile::seek(S64 pos, std::error_code& ec)
-{
- return seek(pos, LLFile::beg, ec);
+ return ret_value;
}
-int LLFile::seek(S64 offset, std::ios_base::seekdir dir, std::error_code& ec)
+// static
+std::string LLFile::getContents(const std::string& filename)
{
- 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
+ LLFILE* fp = LLFile::fopen(filename, "rb");
+ if (fp)
{
- return clear_error(ec);
- }
- return set_ec_from_system_error(ec);
-}
+ fseek(fp, 0, SEEK_END);
+ U32 length = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
-#if LL_WINDOWS
-inline DWORD next_buffer_size(S64 nbytes)
-{
- return nbytes > 0x80000000 ? 0x80000000 : (DWORD)nbytes;
-}
-#endif
+ std::vector<char> buffer(length);
+ size_t nread = fread(buffer.data(), 1, length, fp);
+ fclose(fp);
-S64 LLFile::read(void* buffer, S64 nbytes, std::error_code& ec)
-{
- if (nbytes == 0)
- {
- // Nothing to do
- return clear_error(ec);
+ return std::string(buffer.data(), nread);
}
- 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);
+ return LLStringUtil::null;
}
-S64 LLFile::write(const void* buffer, S64 nbytes, std::error_code& ec)
+// static
+int LLFile::remove(const std::string& filename, int suppress_error)
{
- if (nbytes == 0)
- {
- // Nothing to do here
- return clear_error(ec);
- }
- 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)
+ // 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
+ int rc = -1;
+ std::wstring utf16filename = utf8path_to_wstring(filename);
+ unsigned short st_mode = get_fileattr(utf16filename);
+ if (S_ISDIR(st_mode))
{
- return -1;
+ rc = _wrmdir(utf16filename.c_str());
}
-
- S64 totalBytes = 0;
- char* ptr = (char*)buffer;
- DWORD bytesWritten, bytesToWrite = next_buffer_size(nbytes);
-
- // Write in chunks to support >4GB which the S64 nbytes value makes possible
- while (WriteFile(mHandle, ptr, bytesToWrite, &bytesWritten, nullptr))
+ else if (S_ISREG(st_mode))
{
- totalBytes += bytesWritten;
- if (nbytes <= totalBytes)
- {
- clear_error(ec);
- return totalBytes;
- }
- ptr += bytesWritten;
- bytesToWrite = next_buffer_size(nbytes - totalBytes);
+ rc = _wunlink(utf16filename.c_str());
}
-#else
- ssize_t bytesWritten = ::write(mHandle, buffer, nbytes);
- if (bytesWritten != -1)
- {
- clear_error(ec);
- return bytesWritten;
- }
-#endif
- return set_ec_from_system_error(ec);
-}
-
-S64 LLFile::printf(const char* fmt, ...)
-{
- va_list args1;
- va_start(args1, fmt);
- va_list args2;
- va_copy(args2, args1);
- int length = vsnprintf(nullptr, 0, fmt, args1);
- va_end(args1);
- if (length < 0)
+ else if (st_mode)
{
- 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;
-}
-
-int LLFile::lock(int mode, std::error_code& ec)
-{
-#if LL_WINDOWS
- if (!(mode & LLFile::lock_mask))
- {
- if (UnlockFile(mHandle, 0, 0, MAXDWORD, MAXDWORD))
- {
- return clear_error(ec);
- }
+ // it is something else than a file or directory
+ // this should not really happen as long as we do not allow for symlink
+ // detection in the optional parameter to get_fileattr()
+ rc = set_errno_from_oserror(ERROR_INVALID_PARAMETER);
}
else
{
- OVERLAPPED overlapped = { 0 };
- DWORD flags = (mode & LLFile::noblock) ? LOCKFILE_FAIL_IMMEDIATELY : 0;
- if (mode & LLFile::exclusive)
- {
- flags |= LOCKFILE_EXCLUSIVE_LOCK;
- }
- // We lock the maximum range, since flock only supports locking the entire file too
- if (LockFileEx(mHandle, flags, 0, MAXDWORD, MAXDWORD, &overlapped))
- {
- return clear_error(ec);
- }
- }
-#else
- if (flock(mHandle, decode_lock_mode(mode)) == 0)
- {
- return clear_error(ec);
+ // get_fileattr() failed and already set errno, preserve it for correct error reporting
}
-#endif
- 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))
+ int rc = ::remove(filename.c_str());
#endif
- {
- return set_ec_from_system_error(ec);
- }
- }
- return clear_error(ec);
+ return warnif("remove", filename, rc, suppress_error);
}
-int LLFile::close()
-{
- std::error_code ec;
- return close(ec);
-}
-
-//----------------------------------------------------------------------------------------
-// static member functions
-//----------------------------------------------------------------------------------------
-
// static
-LLFILE* LLFile::fopen(const std::string& filename, const char* mode, int lmode)
+int LLFile::rename(const std::string& filename, const std::string& newname, int suppress_error)
{
- LLFILE* file;
#if LL_WINDOWS
- int shflag = _SH_DENYNO;
- switch (lmode)
+ // 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);
+ int rc = 0;
+ if (!MoveFileExW(utf16filename.c_str(), utf16newname.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
{
- case LLFile::exclusive:
- shflag = _SH_DENYRW;
- break;
- case LLFile::shared:
- shflag = _SH_DENYWR;
- break;
+ 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
- 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)))
- {
- ::fclose(file);
- file = nullptr;
- }
- }
+ int rc = ::rename(filename.c_str(),newname.c_str());
#endif
- return file;
+ return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, rc, suppress_error);
}
-// static
-int LLFile::close(LLFILE* file)
-{
- int ret_value = 0;
- if (file)
- {
- ret_value = ::fclose(file);
- }
- return ret_value;
-}
-
-// 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)
- {
- S64 length = file.size(ec);
- if (!ec && length > 0)
- {
- buffer = std::string(length, 0);
- file.read(&buffer[0], length, ec);
- if (ec)
- {
- buffer.clear();
- }
- }
- }
- return buffer;
-}
-
-// 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_warning)
-{
- std::error_code ec;
- std::filesystem::path file_path = utf8StringToPath(filename);
- std::filesystem::remove(file_path, ec);
- return warnif("remove", filename, ec, suppress_warning);
-}
-
-// static
-int LLFile::rename(const std::string& filename, const std::string& newname, int suppress_warning)
-{
- 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_warning);
-}
-
-// 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);
-}
+// Make this a define rather than using magic numbers multiple times in the code
+#define LLFILE_COPY_BUFFER_SIZE 16384
// static
-S64 LLFile::read(const std::string& filename, void* buf, S64 offset, S64 nbytes, std::error_code& ec)
+bool LLFile::copy(const std::string& from, const std::string& to)
{
- // if number of bytes is 0 or less there is nothing to do here
- if (nbytes <= 0)
+ bool copied = false;
+ LLFILE* in = LLFile::fopen(from, "rb");
+ if (in)
{
- clear_error(ec);
- return 0;
- }
-
- if (!buf || offset < 0)
- {
- set_ec_to_parameter_error(ec);
- }
- else
- {
- std::ios_base::openmode omode = LLFile::in | LLFile::binary;
-
- LLFile file(filename, omode, ec);
- if (!ec && (bool)file)
+ LLFILE* out = LLFile::fopen(to, "wb");
+ if (out)
{
- if (offset > 0)
- {
- file.seek(offset, ec);
- }
- // else (offset == 0) file was just opened and should already be at 0.
- if (!ec)
+ 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)))
{
- S64 bytes_read = file.read(buf, nbytes, ec);
- if (!ec)
+ if (fwrite(buf, 1, readbytes, out) != readbytes)
{
- return bytes_read;
+ LL_WARNS("LLFile") << "Short write" << LL_ENDL;
+ write_ok = false;
}
}
- }
- }
- return warnif("read from file failed", filename, ec);
-}
-
-// 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
-S64 LLFile::write(const std::string& filename, const void* buf, S64 offset, S64 nbytes, std::error_code& ec)
-{
- // if number of bytes is 0 or less there is nothing to do here
- if (nbytes <= 0)
- {
- clear_error(ec);
- return 0;
- }
-
- if (!buf)
- {
- set_ec_to_parameter_error(ec);
- }
- else
- {
- std::ios_base::openmode omode = LLFile::out | LLFile::binary;
- if (offset < 0)
- {
- omode |= LLFile::app;
- }
-
- LLFile file(filename, omode, ec);
- if (!ec && (bool)file)
- {
- if (offset > 0)
+ if ( write_ok )
{
- file.seek(offset, ec);
- }
- // else (offset == 0) we are not appending, file was just opened and should already be at 0.
- if (!ec)
- {
- S64 bytes_written = file.write(buf, nbytes, ec);
- if (!ec)
- {
- return bytes_written;
- }
+ copied = true;
}
+ fclose(out);
}
- }
- return warnif("write to file failed", filename, ec);
-}
-
-// static
-bool LLFile::copy(const std::string& source, const std::string& target)
-{
- std::error_code ec;
- return copy(source, target, std::filesystem::copy_options::overwrite_existing, ec);
-}
-
-// static
-bool LLFile::copy(const std::string& source, const std::string& target, std::filesystem::copy_options options, std::error_code& ec)
-{
- std::filesystem::path source_path = utf8StringToPath(source);
- std::filesystem::path target_path = utf8StringToPath(target);
- bool copied = std::filesystem::copy_file(source_path, target_path, options, ec);
- if (!copied)
- {
- warnif(STRINGIZE("copy failed, to '" << target << "' from"), source, ec);
+ fclose(in);
}
return copied;
}
// static
-int LLFile::stat(const std::string& filename, llstat* filestatus, const char *fname, int suppress_warning)
+int LLFile::stat(const std::string& filename, llstat* filestatus, int suppress_error)
{
#if LL_WINDOWS
- std::wstring file_path = utf8StringToWstring(filename);
- int rc = _wstat64(file_path.c_str(), filestatus);
+ std::wstring utf16filename = utf8path_to_wstring(filename);
+ int rc = _wstat64(utf16filename.c_str(), filestatus);
#else
int rc = ::stat(filename.c_str(), filestatus);
#endif
- return warnif(fname ? fname : "stat", filename, rc, suppress_warning);
+ return warnif("stat", filename, rc, suppress_error);
}
// static
-std::time_t LLFile::getCreationTime(const std::string& filename, int suppress_warning)
+unsigned short LLFile::getattr(const std::string& filename, bool dontFollowSymLink, int suppress_error)
{
- // As of 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)
+#if LL_WINDOWS
+ // _wstat64() is a bit heavyweight on Windows, use a more lightweight API
+ // to just get the attributes
+ int rc = -1;
+ std::wstring utf16filename = utf8path_to_wstring(filename);
+ unsigned short st_mode = get_fileattr(utf16filename, dontFollowSymLink);
+ if (st_mode)
{
-#if LL_DARWIN
- return filestat.st_birthtime;
-#else
- // 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 st_mode;
}
- 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);
+#else
+ llstat filestatus;
+ int rc = dontFollowSymLink ? ::lstat(filename.c_str(), &filestatus) : ::stat(filename.c_str(), &filestatus);
if (rc == 0)
{
- return filestat.st_mtime;
+ return filestatus.st_mode;
}
+#endif
+ warnif("getattr", filename, rc, suppress_error);
return 0;
}
// static
-S64 LLFile::size(const std::string& filename, int suppress_warning)
-{
- std::error_code ec;
- std::filesystem::path file_path = utf8StringToPath(filename);
- std::intmax_t size = static_cast<std::intmax_t>(std::filesystem::file_size(file_path, ec));
- if (ec)
- {
- return warnif("size", filename, ec, suppress_warning);
- }
- return 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)
- {
- status = std::filesystem::symlink_status(file_path, ec);
- }
- else
- {
- status = std::filesystem::status(file_path, ec);
- }
- warnif("getStatus()", 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)
{
- std::filesystem::file_status status = getStatus(filename);
- return std::filesystem::is_directory(status);
+ return S_ISDIR(getattr(filename));
}
// static
bool LLFile::isfile(const std::string& filename)
{
- std::filesystem::file_status status = getStatus(filename);
- return std::filesystem::is_regular_file(status);
+ return S_ISREG(getattr(filename));
}
// static
bool LLFile::islink(const std::string& filename)
{
- std::filesystem::file_status status = getStatus(filename, true);
- return std::filesystem::is_symlink(status);
+ return S_ISLNK(getattr(filename, true));
}
// static
-const std::string& LLFile::tmpdir()
+const char *LLFile::tmpdir()
{
- static std::string temppath;
- if (temppath.empty())
- {
- temppath = std::filesystem::temp_directory_path().string();
- }
- return temppath;
-}
+ static std::string utf8path;
-// static
-std::filesystem::path LLFile::utf8StringToPath(const std::string& pathname)
-{
-#if LL_WINDOWS
- return ll_convert<std::wstring>(pathname);
-#else
- return pathname;
-#endif
-}
-
-#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)
+ if (utf8path.empty())
{
- // 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);
+ char sep;
+#if LL_WINDOWS
+ sep = '\\';
- // By prepending "\\?\" to a path, Windows widechar file APIs will not fail on long path names
- utf16string.assign(L"\\\\?\\").append(utf16path);
+ std::vector<wchar_t> utf16path(MAX_PATH + 1);
+ GetTempPathW(static_cast<DWORD>(utf16path.size()), &utf16path[0]);
+ utf8path = ll_convert_wide_to_string(&utf16path[0]);
+#else
+ sep = '/';
- /* remove trailing spaces and dots (yes, Windows really does that) */
- size_t last_valid = utf16string.find_last_not_of(L" \t.");
- if (last_valid == std::wstring::npos)
+ utf8path = LLStringUtil::getenv("TMPDIR", "/tmp/");
+#endif
+ if (utf8path[utf8path.size() - 1] != sep)
{
- return std::wstring();
+ utf8path += sep;
}
- return utf16string.substr(0, last_valid + 1);
}
- return utf16string;
+ return utf8path.c_str();
}
+#if LL_WINDOWS
+
/************** input file stream ********************************/
llifstream::llifstream() {}
@@ -1176,8 +586,10 @@ void llifstream::open(const std::string& _Filename, ios_base::openmode _Mode)
_Mode | ios_base::in);
}
+
/************** output file stream ********************************/
+
llofstream::llofstream() {}
// explicit
@@ -1193,4 +605,30 @@ void llofstream::open(const std::string& _Filename, ios_base::openmode _Mode)
_Mode | ios_base::out);
}
+/************** helper functions ********************************/
+
+std::streamsize llifstream_size(llifstream& ifstr)
+{
+ if(!ifstr.is_open()) return 0;
+ std::streampos pos_old = ifstr.tellg();
+ ifstr.seekg(0, ios_base::beg);
+ std::streampos pos_beg = ifstr.tellg();
+ ifstr.seekg(0, ios_base::end);
+ std::streampos pos_end = ifstr.tellg();
+ ifstr.seekg(pos_old, ios_base::beg);
+ return pos_end - pos_beg;
+}
+
+std::streamsize llofstream_size(llofstream& ofstr)
+{
+ if(!ofstr.is_open()) return 0;
+ std::streampos pos_old = ofstr.tellp();
+ ofstr.seekp(0, ios_base::beg);
+ std::streampos pos_beg = ofstr.tellp();
+ ofstr.seekp(0, ios_base::end);
+ std::streampos pos_end = ofstr.tellp();
+ ofstr.seekp(pos_old, ios_base::beg);
+ return pos_end - pos_beg;
+}
+
#endif // LL_WINDOWS
diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h
index 2c74c97d0b..04a2946ac4 100755..100644
--- a/indra/llcommon/llfile.h
+++ b/indra/llcommon/llfile.h
@@ -35,359 +35,118 @@
* 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
+typedef struct stat llstat;
#include <sys/types.h>
-typedef struct stat llstat;
#endif
-typedef FILE LLFILE;
+#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
#include "llstring.h" // safe char* -> std::string conversion
-/// This class provides a selection of functions to operate on files through names and
-/// a class implementation to represent a file for reading and writing to it
+/// LLFile is a class of static functions operating on paths
/// All the functions with a path string input take UTF8 path/filenames
-///
-/// @nosubgrouping
-///
class LL_COMMON_API LLFile
{
public:
- // ================================================================================
- /// @name 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 or failing to open when the file does not exist.
- 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 noreplace File exists File doesn't 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 flags to omode in open() and lmode in fopen() or lock()
- /// to indicate which sort of lock if any to attempt to get
- ///
- /// NOTE: there is a fundamental difference between platforms.
- /// On Windows this lock is mandatory as it is part of the API to open a file handle 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 in another process with that access will fail.
- /// On Mac and Linux it is only an advisory lock implemented through the flock() system call.
- /// This means that any 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.
- static const std::ios_base::openmode exclusive = 1 << 16;
- static const std::ios_base::openmode shared = 1 << 17;
-
- /// Additional lmode 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;
-
- /// The mask value for the lock mask bits
- static const std::ios_base::openmode lock_mask = exclusive | shared;
-
- /// One of 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;
- ///@}
-
- // ================================================================================
- /// @name constructor/deconstructor
- ///
- ///@{
- /// default constructor
- LLFile() : mHandle(InvalidHandle) {}
-
- /// no copy constructor
- LLFile(const LLFile&) = delete;
-
- /// move constructor
- LLFile(LLFile&& other) noexcept
- {
- mHandle = other.mHandle;
- other.mHandle = InvalidHandle;
- }
-
- /// constructor opening the 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);
- }
-
- /// destructor always attempts to close the file
- ~LLFile() { close(); }
- ///@}
-
- // ================================================================================
- /// @name 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); }
- ///@}
-
- /// ================================================================================
- /// @name class member methods
- ///
- /// These methods provide read and write support as well as additional functionality to query the size of
- /// the file, change the position of the current file pointer or query it.
- ///
- /// Most 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
-
- /// Query the position of the current file pointer in 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 the specified 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 the specified 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 and
- /// additional optional parameters as specified in the fmt string
- 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 lmode, std::error_code& ec);
- ///< lmode 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 explicitly
- int close(std::error_code& ec);
- ///< @returns 0 on success, -1 on failure
-
- /// Convenience function to close the file without additional parameters
- int close();
- ///< @returns 0 on success, -1 on failure
- ///@}
-
- /// ================================================================================
- /// @name static member functions
- ///
- /// These functions are static and operate with UTF8 filenames as one of their parameters.
- ///
- ///@{
/// open a file with the specified access mode
- static LLFILE* fopen(const std::string& filename, const char* accessmode, int lmode = 0);
+ static LLFILE* fopen(const std::string& filename, const char* accessmode); /* Flawfinder: ignore */
///< '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 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
- /// try to translate line endings. Windows also allows to append "t" to indicate text mode. If neither
- /// "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', while possible on some platforms (Windows), will usually
- /// cause an error on other platforms as fopen will verify this 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
///
- /// 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
+ /// 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
+ /// try to translate line endings. Windows also allows to append "t" to indicate text mode. If neither
+ /// "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
+ /// @returns a valid LLFILE* pointer on success or NULL on failure
- /// Close a file handle opened with fopen() above
static int close(LLFILE * file);
- ///< @returns 0 on success and -1 on failure.
+
+ /// retrieve the content of a file into a string
+ static std::string getContents(const std::string& filename);
+ ///< @returns the content of the file or an empty string on failure
/// create a directory
- static int mkdir(const std::string& filename);
- ///< mkdir() considers "directory already exists" to be not an error.
+ 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
/// @returns 0 on success and -1 on failure.
/// remove a file or directory
- static int remove(const std::string& filename, int suppress_warning = 0);
- ///< pass an errno value (e.g., ENOENT) in the optional 'suppress_warning' parameter if you want to
- /// suppress a warning in the log when the failure matches that errno (e.g., suppress warning if
- /// the file or directory does not exist)
+ 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
/// @returns 0 on success and -1 on failure.
/// rename a file
- static int rename(const std::string& filename, const std::string& newname, int suppress_warning = 0);
+ static int rename(const std::string& filename, const std::string& newname, int suppress_error = 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
- /// under Windows at this time. It may do it in the same way but the used Windows
- /// APIs do not make such guarantees.
+ /// under Windows at this time. It may do it in the same way but the used Windows API
+ /// does not make such guarantees.
/// @returns 0 on success and -1 on failure.
- /// copy the contents of the file from 'source' to 'target'
- static bool copy(const std::string& source, const std::string& target);
- ///< Copies the contents of the file 'source' to the file 'target', overwriting 'target' if it already
- /// existed.
- /// This is a convenience function that implements the previous behavior of silently overwriting an
- /// already existing target file. Consider using the function below if you desire a different
- /// behavior when the target file already exists
- /// @returns true on success and false on failure.
- /// copy the contents of the file from 'from' to 'to'
- static bool copy(const std::string& source, const std::string& target, std::filesystem::copy_options options, std::error_code& ec);
- ///< Copies the contents of the file 'source' to the file 'target'. The options parameter allows to
- /// specify what should happen if the "target" file already exists:
- /// std::filesystem::copy_options::none - return an error in ec and fail
- /// std::filesystem::copy_options::skip_existing - skip the operation and do not overwrite file
- /// std::filesystem::copy_options::overwrite_existing - overwrite the file
- /// std::filesystem::copy_options::update_existing - overwrite the file only if it is older than the file being copied
- /// @returns true on success and false on failure.
-
- /// 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 entire content of the file as std::string or an empty string on failure
-
- /// read nBytes from the file into the buffer, starting at offset in the file
- 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 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);
- ///< If a negative offset is provided, the file is opened in append mode and the
- /// write will be appended to the end of the file.
- /// @returns bytes written on success, or -1 on failure
+ /// copy the contents of file from 'from' to 'to' filename
+ static bool copy(const std::string& from, const std::string& to);
+ ///< @returns true on success and false on failure.
/// return the file stat structure for filename
- static int stat(const std::string& filename, llstat* file_status, const char *operation = nullptr, int suppress_warning = ENOENT);
+ static int stat(const std::string& filename, llstat* file_status, int suppress_error = ENOENT);
///< for compatibility with existing uses of LL_File::stat() we use ENOENT as default in the
- /// optional 'suppress_warning' parameter to avoid spamming the log with warnings when the API
+ /// optional 'suppress_error' 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);
- ///< Different systems have different support for this. Under Windows this is supposedly
- /// the actual time the file was created, on the Mac this is the actual birth date of
- /// the file which is in fact the creation time. The according ctime entry in the stat
- /// structure under Linux (and any other *nix really) is however contrary to what one
- /// might expect based on the c in ctime not the creation time but the time the last
- /// change to the inode entry was made. Changing access rights to a file will update
- /// this value too. In order to have a true creation time under Linux, we would have
- /// to use the statx() call which is available since kernel 4.19, but that will require
- /// considerable changes to the implementation of above stat() function.
- /// @returns the creation time (last status change under Linux) 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 std::filesystem::file_status for filename
- static std::filesystem::file_status getStatus(const std::string& filename, bool dontFollowSymLink = false, int suppress_warning = ENOENT);
- ///< dontFollowSymLinks set to true returns the std::filesystem::file_status 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 a std::filesystem::file_status value that can be passed to the appropriate 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_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
+ /// 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
+ /// warnings when the file or directory does not exist
+ /// @returns 0 on failure and a st_mode value with either S_IFDIR or S_IFREG set otherwise
+ /// together with the three access bits which under Windows only the write bit is relevant.
/// check if filename is an existing directory
static bool isdir(const std::string& filename);
@@ -402,44 +161,70 @@ public:
///< @returns true if the path is pointing at a symlink
/// return a path to the temporary directory on the system
- static const std::string& tmpdir();
+ static const char * tmpdir();
+};
- /// 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
- ///@}
+/// 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();
+ }
-private:
-#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
+ // 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;
+ }
- /// ================================================================================
- /// @name private 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; // The file handle/descriptor
- std::ios_base::openmode mOpen; // Used to emulate std::ios_base::app under Windows
+ // 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; }
+
+private:
+ LLFILE* mFileHandle;
};
#if LL_WINDOWS
@@ -530,6 +315,17 @@ class LL_COMMON_API llofstream : public std::ofstream
ios_base::openmode _Mode = ios_base::out|ios_base::trunc);
};
+
+/**
+ * @brief filesize helpers.
+ *
+ * The file size helpers are not considered particularly efficient,
+ * and should only be used for config files and the like -- not in a
+ * loop.
+ */
+std::streamsize LL_COMMON_API llifstream_size(llifstream& fstr);
+std::streamsize LL_COMMON_API llofstream_size(llofstream& fstr);
+
#else // ! LL_WINDOWS
// on non-windows, llifstream and llofstream are just mapped directly to the std:: equivalents
diff --git a/indra/llcrashlogger/llcrashlock.cpp b/indra/llcrashlogger/llcrashlock.cpp
index 731b53b7e9..bc34f6798f 100755..100644
--- a/indra/llcrashlogger/llcrashlock.cpp
+++ b/indra/llcrashlogger/llcrashlock.cpp
@@ -188,7 +188,12 @@ LLSD LLCrashLock::getProcessList()
//static
bool LLCrashLock::fileExists(std::string filename)
{
- return LLFile::exists(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);
}
void LLCrashLock::cleanupProcess(std::string proc_dir)
diff --git a/indra/llfilesystem/lldir.cpp b/indra/llfilesystem/lldir.cpp
index eb3c2d9909..190539cea5 100755..100644
--- a/indra/llfilesystem/lldir.cpp
+++ b/indra/llfilesystem/lldir.cpp
@@ -30,6 +30,8 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
+#else
+#include <direct.h>
#endif
#include "lldir.h"
@@ -41,6 +43,7 @@
#include "lldiriterator.h"
#include "stringize.h"
#include "llstring.h"
+#include <boost/filesystem.hpp>
#include <boost/bind.hpp>
#include <algorithm>
@@ -90,20 +93,30 @@ LLDir::~LLDir()
std::vector<std::string> LLDir::getFilesInDir(const std::string &dirname)
{
- // Returns a vector of filenames in the directory.
- std::filesystem::path p = LLFile::utf8StringToPath(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::vector<std::string> v;
- std::error_code ec;
- if (std::filesystem::is_directory(p, ec))
+
+ boost::system::error_code ec;
+ if (exists(p, ec) && !ec.failed())
{
- std::filesystem::directory_iterator end_iter;
- for (std::filesystem::directory_iterator dir_itr(p);
- dir_itr != end_iter;
- ++dir_itr)
+ if (is_directory(p, ec) && !ec.failed())
{
- if (std::filesystem::is_regular_file(dir_itr->status()))
+ boost::filesystem::directory_iterator end_iter;
+ for (boost::filesystem::directory_iterator dir_itr(p);
+ dir_itr != end_iter;
+ ++dir_itr)
{
- v.push_back(dir_itr->path().filename().string());
+ if (boost::filesystem::is_regular_file(dir_itr->status()))
+ {
+ v.push_back(dir_itr->path().filename().string());
+ }
}
}
}
@@ -173,23 +186,28 @@ 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
{
- if (std::filesystem::is_directory(dir_path))
+#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_empty(dir_path))
+ if (!boost::filesystem::is_empty(dir_path))
{ // Directory has content
- num_deleted = (U32)std::filesystem::remove_all(dir_path);
+ num_deleted = (U32)boost::filesystem::remove_all(dir_path);
}
else
{ // Directory is empty
- std::filesystem::remove(dir_path);
+ boost::filesystem::remove(dir_path);
}
}
}
- catch (std::filesystem::filesystem_error &er)
+ catch (boost::filesystem::filesystem_error &er)
{
LL_WARNS() << "Failed to delete " << dir_name << " with error " << er.code().message() << LL_ENDL;
}
@@ -1087,15 +1105,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);
+ LLFile::mkdir(dir_name, 0700);
#else
- llstat dir_stat;
+ struct stat dir_stat;
if(0 != LLFile::stat(dir_name, &dir_stat))
{
S32 stat_rv = errno;
if(ENOENT == stat_rv)
{
- if(0 != LLFile::mkdir(dir_name))
+ if(0 != LLFile::mkdir(dir_name, 0700)) // octal
{
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 2b478e5dce..58c080c982 100755..100644
--- a/indra/llfilesystem/lldir_win32.cpp
+++ b/indra/llfilesystem/lldir_win32.cpp
@@ -376,7 +376,18 @@ std::string LLDir_Win32::getCurPath()
bool LLDir_Win32::fileExists(const std::string &filename) const
{
- return LLFile::exists(filename);
+ 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;
+ }
}
diff --git a/indra/llfilesystem/llfilesystem.cpp b/indra/llfilesystem/llfilesystem.cpp
index 0c220fe7cf..541266af4f 100755..100644
--- a/indra/llfilesystem/llfilesystem.cpp
+++ b/indra/llfilesystem/llfilesystem.cpp
@@ -77,17 +77,21 @@ 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);
- // not only test for existence but for the file to be not empty
- S64 size = LLFile::size(filename);
- return size > 0;
+ llifstream file(filename, std::ios::binary);
+ if (file.is_open())
+ {
+ file.seekg(0, std::ios::end);
+ return file.tellg() > 0;
+ }
+ return false;
}
// static
-bool LLFileSystem::removeFile(const LLUUID& file_id, const LLAssetType::EType file_type, int suppress_warning /*= 0*/)
+bool LLFileSystem::removeFile(const LLUUID& file_id, const LLAssetType::EType file_type, int suppress_error /*= 0*/)
{
const std::string filename = LLDiskCache::metaDataToFilepath(file_id, file_type);
- LLFile::remove(filename.c_str(), suppress_warning);
+ LLFile::remove(filename.c_str(), suppress_error);
return true;
}
@@ -112,10 +116,19 @@ bool LLFileSystem::renameFile(const LLUUID& old_file_id, const LLAssetType::ETyp
}
// static
-S64 LLFileSystem::getFileSize(const LLUUID& file_id, const LLAssetType::EType file_type)
+S32 LLFileSystem::getFileSize(const LLUUID& file_id, const LLAssetType::EType file_type)
{
const std::string filename = LLDiskCache::metaDataToFilepath(file_id, file_type);
- return LLFile::size(filename);
+
+ 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;
}
bool LLFileSystem::read(U8* buffer, S32 bytes)
@@ -256,7 +269,7 @@ S32 LLFileSystem::tell() const
S32 LLFileSystem::getSize() const
{
- return (S32)LLFileSystem::getFileSize(mFileID, mFileType);
+ return LLFileSystem::getFileSize(mFileID, mFileType);
}
S32 LLFileSystem::getMaxSize() const
diff --git a/indra/llfilesystem/llfilesystem.h b/indra/llfilesystem/llfilesystem.h
index 7188683e7f..10649b6920 100644
--- a/indra/llfilesystem/llfilesystem.h
+++ b/indra/llfilesystem/llfilesystem.h
@@ -61,10 +61,10 @@ class LLFileSystem
void updateFileAccessTime(const std::string& file_path);
static bool getExists(const LLUUID& file_id, const LLAssetType::EType file_type);
- static bool removeFile(const LLUUID& file_id, const LLAssetType::EType file_type, int suppress_warning = 0);
+ static bool removeFile(const LLUUID& file_id, const LLAssetType::EType file_type, int suppress_error = 0);
static bool renameFile(const LLUUID& old_file_id, const LLAssetType::EType old_file_type,
const LLUUID& new_file_id, const LLAssetType::EType new_file_type);
- static S64 getFileSize(const LLUUID& file_id, const LLAssetType::EType file_type);
+ static S32 getFileSize(const LLUUID& file_id, const LLAssetType::EType file_type);
public:
static const S32 READ;
diff --git a/indra/llfilesystem/tests/lldir_test.cpp b/indra/llfilesystem/tests/lldir_test.cpp
index 17e4ffecf1..13db6fad80 100755..100644
--- 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::remove(dir1);
- LLFile::remove(dir2);
+ LLFile::rmdir(dir1);
+ LLFile::rmdir(dir2);
}
template<> template<>
diff --git a/indra/llmessage/llassetstorage.cpp b/indra/llmessage/llassetstorage.cpp
index 34eeacb273..10fd56a68e 100644
--- a/indra/llmessage/llassetstorage.cpp
+++ b/indra/llmessage/llassetstorage.cpp
@@ -467,7 +467,7 @@ bool LLAssetStorage::findInCacheAndInvokeCallback(const LLUUID& uuid, LLAssetTyp
else
{
LL_WARNS("AssetStorage") << "Asset vfile " << uuid << ":" << type
- << " found in static cache with bad size " << size << ", ignoring" << LL_ENDL;
+ << " found in static cache with bad size " << file.getSize() << ", ignoring" << LL_ENDL;
}
}
return false;
diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp
index 0522307661..2c35a6acae 100755..100644
--- a/indra/llrender/llshadermgr.cpp
+++ b/indra/llrender/llshadermgr.cpp
@@ -1014,6 +1014,7 @@ void LLShaderMgr::initShaderCache(bool enabled, const LLUUID& old_cache_version,
mShaderCacheDir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "shader_cache");
LLFile::mkdir(mShaderCacheDir);
+
{
std::string meta_out_path = gDirUtilp->add(mShaderCacheDir, "shaderdata.llsd");
if (gDirUtilp->fileExists(meta_out_path))
@@ -1058,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::remove(shader_cache);
+ LLFile::rmdir(shader_cache);
mShaderBinaryCache.clear();
}
@@ -1077,7 +1078,7 @@ void LLShaderMgr::persistShaderCacheMetadata()
// Settings and shader cache get saved at different time, thus making
// RenderShaderCacheVersion unreliable when running multiple viewer
// instances, or for cases where viewer crashes before saving settings.
- // Duplicate version to the cache itself.
+ // Dupplicate version to the cache itself.
out["version"] = mShaderCacheVersion;
out["shaders"] = LLSD::emptyMap();
LLSD &shaders = out["shaders"];
@@ -1130,11 +1131,11 @@ bool LLShaderMgr::loadCachedProgramBinary(LLGLSLShader* shader)
{
std::vector<U8> in_data;
in_data.resize(shader_info.mBinaryLength);
- std::error_code ec;
- LLFile filep = LLFile(in_path, LLFile::in | LLFile::binary, ec);
- if (!ec && (bool)filep)
+
+ LLUniqueFile filep = LLFile::fopen(in_path, "rb");
+ if (filep)
{
- size_t result = filep.read(in_data.data(), in_data.size(), ec);
+ size_t result = fread(in_data.data(), sizeof(U8), in_data.size(), filep);
filep.close();
if (result == in_data.size())
@@ -1179,12 +1180,11 @@ bool LLShaderMgr::saveCachedProgramBinary(LLGLSLShader* shader)
if (error == GL_NO_ERROR)
{
std::string out_path = gDirUtilp->add(mShaderCacheDir, shader->mShaderHash.asString() + ".shaderbin");
- std::error_code ec;
- LLFile filep = LLFile(out_path, LLFile::out | LLFile::binary, ec);
- if (filep)
+ LLUniqueFile outfile = LLFile::fopen(out_path, "wb");
+ if (outfile)
{
- filep.write(program_binary.data(), program_binary.size(), ec);
- filep.close();
+ fwrite(program_binary.data(), sizeof(U8), program_binary.size(), outfile);
+ outfile.close();
binary_info.mLastUsedTime = (F32)LLTimer::getTotalSeconds();
diff --git a/indra/llxml/tests/llcontrol_test.cpp b/indra/llxml/tests/llcontrol_test.cpp
index e15117fc29..4192e029c5 100755..100644
--- 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::remove(mTestConfigDir);
+ LLFile::rmdir(mTestConfigDir);
}
void writeSettingsFile(const LLSD& config)
{
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 962a91bb73..569fd30b21 100755..100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -2268,7 +2268,7 @@ void errorCallback(LLError::ELevel level, const std::string &error_string)
LLAppViewer::instance()->writeDebugInfo();
std::string error_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ERROR_MARKER_FILE_NAME);
- if (!LLFile::isfile(error_marker_file))
+ if (!LLAPRFile::isExist(error_marker_file, NULL, LL_APR_RB))
{
// If marker doesn't exist, create a marker with llerror code for next launch
// otherwise don't override existing file
@@ -3031,11 +3031,13 @@ void LLAppViewer::initStrings()
}
else
{
- if (!LLFile::exists(strings_path_full))
+ llstat st;
+ int rc = LLFile::stat(strings_path_full, &st);
+ if (rc != 0)
{
- crash_reason = "The file '" + strings_path_full + "' doesn't seem to exist";
+ crash_reason = "The file '" + strings_path_full + "' failed to get status. Error code: " + std::to_string(rc);
}
- else if (LLFile::isdir(strings_path_full))
+ else if (S_ISDIR(st.st_mode))
{
crash_reason = "The filename '" + strings_path_full + "' is a directory name";
}
@@ -3898,7 +3900,7 @@ void LLAppViewer::processMarkerFiles()
bool marker_is_same_version = true;
// first, look for the marker created at startup and deleted on a clean exit
mMarkerFileName = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,MARKER_FILE_NAME);
- if (LLFile::isfile(mMarkerFileName))
+ if (LLAPRFile::isExist(mMarkerFileName, NULL, LL_APR_RB))
{
// File exists...
// first, read it to see if it was created by the same version (we need this later)
@@ -3990,7 +3992,7 @@ void LLAppViewer::processMarkerFiles()
// check for any last exec event report based on whether or not it happened during logout
// (the logout marker is created when logout begins)
std::string logout_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, LOGOUT_MARKER_FILE_NAME);
- if(LLFile::isfile(logout_marker_file))
+ if(LLAPRFile::isExist(logout_marker_file, NULL, LL_APR_RB))
{
if (markerIsSameVersion(logout_marker_file))
{
@@ -4002,11 +4004,11 @@ void LLAppViewer::processMarkerFiles()
{
LL_INFOS("MarkerFile") << "Logout crash marker '"<< logout_marker_file << "' found, but versions did not match" << LL_ENDL;
}
- LLFile::remove(logout_marker_file);
+ LLAPRFile::remove(logout_marker_file);
}
// and last refine based on whether or not a marker created during a non-llerr crash is found
std::string error_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ERROR_MARKER_FILE_NAME);
- if(LLFile::isfile(error_marker_file))
+ if(LLAPRFile::isExist(error_marker_file, NULL, LL_APR_RB))
{
S32 marker_code = getMarkerErrorCode(error_marker_file);
if (marker_code >= 0)
@@ -4031,7 +4033,7 @@ void LLAppViewer::processMarkerFiles()
{
LL_INFOS("MarkerFile") << "Error marker '"<< error_marker_file << "' marker found, but versions did not match" << LL_ENDL;
}
- LLFile::remove(error_marker_file);
+ LLAPRFile::remove(error_marker_file);
}
#if LL_DARWIN
@@ -4058,7 +4060,7 @@ void LLAppViewer::removeMarkerFiles()
if (mMarkerFile.getFileHandle())
{
mMarkerFile.close() ;
- LLFile::remove( mMarkerFileName );
+ LLAPRFile::remove( mMarkerFileName );
LL_DEBUGS("MarkerFile") << "removed exec marker '"<<mMarkerFileName<<"'"<< LL_ENDL;
}
else
@@ -4069,7 +4071,7 @@ void LLAppViewer::removeMarkerFiles()
if (mLogoutMarkerFile.getFileHandle())
{
mLogoutMarkerFile.close();
- LLFile::remove( mLogoutMarkerFileName );
+ LLAPRFile::remove( mLogoutMarkerFileName );
LL_DEBUGS("MarkerFile") << "removed logout marker '"<<mLogoutMarkerFileName<<"'"<< LL_ENDL;
}
else
@@ -4291,7 +4293,7 @@ void LLAppViewer::migrateCacheDirectory()
LLFile::remove(ds_store);
}
#endif
- if (LLFile::remove(old_cache_dir) != 0)
+ if (LLFile::rmdir(old_cache_dir) != 0)
{
LL_WARNS() << "could not delete old cache directory " << old_cache_dir << LL_ENDL;
}
@@ -5437,7 +5439,7 @@ void LLAppViewer::createErrorMarker(eLastExecEvent error_code) const
bool LLAppViewer::errorMarkerExists() const
{
std::string error_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ERROR_MARKER_FILE_NAME);
- return LLFile::isfile(error_marker_file);
+ return LLAPRFile::isExist(error_marker_file, NULL, LL_APR_RB);
}
void LLAppViewer::outOfMemorySoftQuit()
diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp
index 66066a45b2..c5c1e01538 100755..100644
--- 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::remove(chatLogPath);
+ LLFile::rmdir(chatLogPath);
}
return false;
}
@@ -1526,7 +1526,7 @@ bool LLFloaterPreference::moveTranscriptsAndLog()
if(madeDirectory)
{
- LLFile::remove(chatLogPath);
+ LLFile::rmdir(chatLogPath);
}
return false;
@@ -2031,15 +2031,17 @@ 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::isfile(LLConversationLog::instance().getFileName())
- && LLFile::size(LLConversationLog::instance().getFileName()) > 0;
+ && LLFile::stat(LLConversationLog::instance().getFileName(), &st) == 0
+ && S_ISREG(st.st_mode)
+ && st.st_size > 0;
getChild<LLButton>("clear_log")->setEnabled(has_logs);
}
diff --git a/indra/newview/llfloateruipreview.cpp b/indra/newview/llfloateruipreview.cpp
index 0bf0946c42..c3bc24c6b9 100755..100644
--- a/indra/newview/llfloateruipreview.cpp
+++ b/indra/newview/llfloateruipreview.cpp
@@ -484,47 +484,47 @@ bool LLFloaterUIPreview::postBuild()
bool found_en_us = false;
std::string language_directory;
std::string xui_dir = get_xui_dir(); // directory containing localizations -- don't forget trailing delim
- mLanguageSelection->removeall(); // clear out anything temporarily in list from XML
+ mLanguageSelection->removeall(); // clear out anything temporarily in list from XML
LLDirIterator iter(xui_dir, "*");
- while (found) // for every directory
+ while(found) // for every directory
{
- if ((found = iter.next(language_directory))) // get next directory
+ if((found = iter.next(language_directory))) // get next directory
{
std::string full_path = gDirUtilp->add(xui_dir, language_directory);
- if (!LLFile::isdir(full_path.c_str())) // if it's not a directory, skip it
+ if(LLFile::isfile(full_path.c_str())) // if it's not a directory, skip it
{
continue;
}
- if (strncmp("template",language_directory.c_str(), 8) && -1 == language_directory.find(".")) // if it's not the template directory or a hidden directory
+ if(strncmp("template",language_directory.c_str(),8) && -1 == language_directory.find(".")) // if it's not the template directory or a hidden directory
{
- if (!strncmp("en",language_directory.c_str(), 5)) // remember if we've seen en, so we can make it default
+ if(!strncmp("en",language_directory.c_str(),5)) // remember if we've seen en, so we can make it default
{
found_en_us = true;
}
else
{
- mLanguageSelection->add(std::string(language_directory)); // add it to the language selection dropdown menu
+ mLanguageSelection->add(std::string(language_directory)); // add it to the language selection dropdown menu
mLanguageSelection_2->add(std::string(language_directory));
}
}
}
}
- if (found_en_us)
+ if(found_en_us)
{
- mLanguageSelection->add(std::string("en"), ADD_TOP); // make en first item if we found it
- mLanguageSelection_2->add(std::string("en"), ADD_TOP);
+ mLanguageSelection->add(std::string("en"),ADD_TOP); // make en first item if we found it
+ mLanguageSelection_2->add(std::string("en"),ADD_TOP);
}
else
{
std::string warning = std::string("No EN localization found; check your XUI directories!");
popupAndPrintWarning(warning);
}
- mLanguageSelection->selectFirstItem(); // select the first item
+ mLanguageSelection->selectFirstItem(); // select the first item
mLanguageSelection_2->selectFirstItem();
- refreshList(); // refresh the list of available floaters
+ refreshList(); // refresh the list of available floaters
return true;
}
@@ -892,7 +892,8 @@ 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";
- if (LLFile::isfile(full_path.c_str())) // use localized language if the file exists
+ llstat dummy;
+ if(!LLFile::stat(full_path.c_str(), &dummy)) // if the file does not exist
{
floater_lang = getLocStr(ID);
}
@@ -965,8 +966,9 @@ void LLFloaterUIPreview::onClickEditFloater()
}
file_path = getLocalizedDirectory() + file_name;
- // Does it exist? (Some localized versions may not have it when there are no diffs, and then we try to open a nonexistent file)
- if (!LLFile::isfile(file_path.c_str())) // if the file does not exist
+ // 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
{
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
@@ -1115,14 +1117,15 @@ void LLFloaterUIPreview::onClickToggleDiffHighlighting()
std::string path_in_textfield = mDiffPathTextBox->getText(); // get file path
bool error = false;
- if(std::string("") == path_in_textfield) // check for blank file
+ if(std::string("") == path_in_textfield) // check for blank file
{
std::string warning = "Unable to highlight differences because no file was provided; fill in the relevant text field";
popupAndPrintWarning(warning);
error = true;
}
- if (!LLFile::isfile(path_in_textfield.c_str()) && !error) // check if the file exists (empty check is redundant but useful for the informative error message)
+ 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)
{
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 4fe661b055..9a991727b2 100755..100644
--- a/indra/newview/llpreviewnotecard.cpp
+++ b/indra/newview/llpreviewnotecard.cpp
@@ -575,7 +575,8 @@ void LLPreviewNotecard::syncExternal()
{
// Sync with external editor.
std::string tmp_file = getTmpFileName();
- if (LLFile::isfile(tmp_file)) // file exists
+ llstat s;
+ if (LLFile::stat(tmp_file, &s) == 0) // file exists
{
if (mLiveFile) mLiveFile->ignoreNextUpdate();
writeToFile(tmp_file);
diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp
index 2c436198e3..c2aa4925bd 100755..100644
--- a/indra/newview/llpreviewscript.cpp
+++ b/indra/newview/llpreviewscript.cpp
@@ -694,7 +694,8 @@ void LLScriptEdCore::sync()
if (mLiveFile)
{
std::string tmp_file = mLiveFile->filename();
- if (LLFile::isfile(tmp_file)) // file exists
+ llstat s;
+ if (LLFile::stat(tmp_file, &s) == 0) // file exists
{
mLiveFile->ignoreNextUpdate();
writeToFile(tmp_file);
diff --git a/indra/newview/lltexturecache.cpp b/indra/newview/lltexturecache.cpp
index a6d81816ce..1a7ce74ccc 100755..100644
--- a/indra/newview/lltexturecache.cpp
+++ b/indra/newview/lltexturecache.cpp
@@ -180,7 +180,8 @@ private:
bool LLTextureCacheLocalFileWorker::doRead()
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
- S32 local_size = (S32)LLFile::size(mFileName);
+ S32 local_size = LLAPRFile::size(mFileName, mCache->getLocalAPRFilePool());
+
if (local_size > 0 && mFileName.size() > 4)
{
mDataSize = local_size; // Only a complete file is valid
@@ -209,7 +210,8 @@ bool LLTextureCacheLocalFileWorker::doRead()
}
mReadData = (U8*)ll_aligned_malloc_16(mDataSize);
- S32 bytes_read = (S32)LLFile::read(mFileName, mReadData, mOffset, mDataSize);
+ S32 bytes_read = LLAPRFile::readEx(mFileName, mReadData, mOffset, mDataSize, mCache->getLocalAPRFilePool());
+
if (bytes_read != mDataSize)
{
// LL_WARNS() << "Error reading file from local cache: " << mFileName
@@ -294,7 +296,7 @@ bool LLTextureCacheRemoteWorker::doRead()
// Is it a JPEG2000 file?
{
local_filename = filename + ".j2c";
- local_size = (S32)LLFile::size(local_filename);
+ local_size = LLAPRFile::size(local_filename, mCache->getLocalAPRFilePool());
if (local_size > 0)
{
mImageFormat = IMG_CODEC_J2C;
@@ -304,7 +306,7 @@ bool LLTextureCacheRemoteWorker::doRead()
if (local_size == 0)
{
local_filename = filename + ".jpg";
- local_size = (S32)LLFile::size(local_filename);
+ local_size = LLAPRFile::size(local_filename, mCache->getLocalAPRFilePool());
if (local_size > 0)
{
mImageFormat = IMG_CODEC_JPEG;
@@ -315,7 +317,7 @@ bool LLTextureCacheRemoteWorker::doRead()
if (local_size == 0)
{
local_filename = filename + ".tga";
- local_size = (S32)LLFile::size(local_filename);
+ local_size = LLAPRFile::size(local_filename, mCache->getLocalAPRFilePool());
if (local_size > 0)
{
mImageFormat = IMG_CODEC_TGA;
@@ -344,10 +346,12 @@ bool LLTextureCacheRemoteWorker::doRead()
if (mReadData)
{
- S32 bytes_read = (S32)LLFile::read(local_filename,
- mReadData,
- mOffset,
- mDataSize);
+ S32 bytes_read = LLAPRFile::readEx( local_filename,
+ mReadData,
+ mOffset,
+ mDataSize,
+ mCache->getLocalAPRFilePool());
+
if (bytes_read != mDataSize)
{
LL_WARNS() << "Error reading file from local cache: " << local_filename
@@ -406,7 +410,8 @@ bool LLTextureCacheRemoteWorker::doRead()
mReadData = (U8*)ll_aligned_malloc_16(size);
if (mReadData)
{
- S32 bytes_read = (S32)LLFile::read(mCache->mHeaderDataFileName, mReadData, offset, size);
+ S32 bytes_read = LLAPRFile::readEx(mCache->mHeaderDataFileName,
+ mReadData, offset, size, mCache->getLocalAPRFilePool());
if (bytes_read != size)
{
LL_WARNS() << "LLTextureCacheWorker: " << mID
@@ -441,9 +446,9 @@ bool LLTextureCacheRemoteWorker::doRead()
if (!done && (mState == BODY))
{
std::string filename = mCache->getTextureFileName(mID);
- S32 filesize = (S32)LLFile::size(filename);
+ S32 filesize = LLAPRFile::size(filename, mCache->getLocalAPRFilePool());
- if (filesize > 0 && (filesize + TEXTURE_CACHE_ENTRY_SIZE) > mOffset)
+ if (filesize && (filesize + TEXTURE_CACHE_ENTRY_SIZE) > mOffset)
{
S32 max_datasize = TEXTURE_CACHE_ENTRY_SIZE + filesize - mOffset;
mDataSize = llmin(max_datasize, mDataSize);
@@ -482,9 +487,10 @@ bool LLTextureCacheRemoteWorker::doRead()
mReadData = data;
// Read the data at last
- S32 bytes_read = (S32)LLFile::read(filename,
- mReadData + data_offset,
- file_offset, file_size);
+ S32 bytes_read = LLAPRFile::readEx(filename,
+ mReadData + data_offset,
+ file_offset, file_size,
+ mCache->getLocalAPRFilePool());
if (bytes_read != file_size)
{
LL_WARNS() << "LLTextureCacheWorker: " << mID
@@ -630,13 +636,13 @@ bool LLTextureCacheRemoteWorker::doWrite()
U8* padBuffer = (U8*)ll_aligned_malloc_16(TEXTURE_CACHE_ENTRY_SIZE);
memset(padBuffer, 0, TEXTURE_CACHE_ENTRY_SIZE); // Init with zeros
memcpy(padBuffer, mWriteData, mDataSize); // Copy the write buffer
- bytes_written = (S32)LLFile::write(mCache->mHeaderDataFileName, padBuffer, offset, size);
+ bytes_written = LLAPRFile::writeEx(mCache->mHeaderDataFileName, padBuffer, offset, size, mCache->getLocalAPRFilePool());
ll_aligned_free_16(padBuffer);
}
else
{
// Write the header record (== first TEXTURE_CACHE_ENTRY_SIZE bytes of the raw file) in the header file
- bytes_written = (S32)LLFile::write(mCache->mHeaderDataFileName, mWriteData, offset, size);
+ bytes_written = LLAPRFile::writeEx(mCache->mHeaderDataFileName, mWriteData, offset, size, mCache->getLocalAPRFilePool());
}
if (bytes_written <= 0)
@@ -672,11 +678,15 @@ bool LLTextureCacheRemoteWorker::doWrite()
else
{
S32 file_size = mDataSize - TEXTURE_CACHE_ENTRY_SIZE;
+
{
// build the cache file name from the UUID
std::string filename = mCache->getTextureFileName(mID);
// LL_INFOS() << "Writing Body: " << filename << " Bytes: " << file_offset+file_size << LL_ENDL;
- S32 bytes_written = (S32)LLFile::write(filename, mWriteData + TEXTURE_CACHE_ENTRY_SIZE, 0, file_size);
+ S32 bytes_written = LLAPRFile::writeEx(filename,
+ mWriteData + TEXTURE_CACHE_ENTRY_SIZE,
+ 0, file_size,
+ mCache->getLocalAPRFilePool());
if (bytes_written <= 0)
{
LL_WARNS() << "LLTextureCacheWorker: " << mID
@@ -881,7 +891,7 @@ bool LLTextureCache::isInLocal(const LLUUID& id)
// Is it a JPEG2000 file?
{
local_filename = filename + ".j2c";
- local_size = (S32)LLFile::size(local_filename);
+ local_size = LLAPRFile::size(local_filename, getLocalAPRFilePool());
if (local_size > 0)
{
return true ;
@@ -891,7 +901,7 @@ bool LLTextureCache::isInLocal(const LLUUID& id)
// If not, is it a jpeg file?
{
local_filename = filename + ".jpg";
- local_size = (S32)LLFile::size(local_filename);
+ local_size = LLAPRFile::size(local_filename, getLocalAPRFilePool());
if (local_size > 0)
{
return true ;
@@ -901,7 +911,7 @@ bool LLTextureCache::isInLocal(const LLUUID& id)
// Hmm... What about a targa file? (used for UI texture mostly)
{
local_filename = filename + ".tga";
- local_size = (S32)LLFile::size(local_filename);
+ local_size = LLAPRFile::size(local_filename, getLocalAPRFilePool());
if (local_size > 0)
{
return true ;
@@ -933,6 +943,8 @@ const char* fast_cache_filename = "FastCache.cache";
void LLTextureCache::setDirNames(ELLPath location)
{
+ std::string delem = gDirUtilp->getDirDelimiter();
+
mHeaderEntriesFileName = gDirUtilp->getExpandedFilename(location, textures_dirname, entries_filename);
mHeaderDataFileName = gDirUtilp->getExpandedFilename(location, textures_dirname, cache_filename);
mTexturesDirName = gDirUtilp->getExpandedFilename(location, textures_dirname);
@@ -954,10 +966,11 @@ void LLTextureCache::purgeCache(ELLPath location, bool remove_dir)
if(LLFile::isdir(mTexturesDirName))
{
std::string file_name = gDirUtilp->getExpandedFilename(location, entries_filename);
- LLFile::remove(file_name);
+ // mHeaderAPRFilePoolp because we are under header mutex, and can be in main thread
+ LLAPRFile::remove(file_name, mHeaderAPRFilePoolp);
file_name = gDirUtilp->getExpandedFilename(location, cache_filename);
- LLFile::remove(file_name);
+ LLAPRFile::remove(file_name, mHeaderAPRFilePoolp);
purgeAllTextures(true);
}
@@ -1058,9 +1071,10 @@ void LLTextureCache::readEntriesHeader()
{
// mHeaderEntriesInfo initializes to default values so safe not to read it
llassert_always(mHeaderAPRFile == NULL);
- if (LLFile::isfile(mHeaderEntriesFileName))
+ if (LLAPRFile::isExist(mHeaderEntriesFileName, mHeaderAPRFilePoolp))
{
- LLFile::read(mHeaderEntriesFileName, (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo));
+ LLAPRFile::readEx(mHeaderEntriesFileName, (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo),
+ mHeaderAPRFilePoolp);
}
else //create an empty entries header.
{
@@ -1076,7 +1090,7 @@ void LLTextureCache::setEntriesHeader()
// For simplicity we use predefined size of header, so if version string
// doesn't fit, either getEngineInfo() returned malformed string or
// sHeaderEncoderStringSize need to be increased.
- // Also take into account that c_str() returns additional null character
+ // Also take into accout that c_str() returns additional null character
LL_ERRS() << "Version string doesn't fit in header" << LL_ENDL;
}
@@ -1091,7 +1105,8 @@ void LLTextureCache::writeEntriesHeader()
llassert_always(mHeaderAPRFile == NULL);
if (!mReadOnly)
{
- LLFile::write(mHeaderEntriesFileName, (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo));
+ LLAPRFile::writeEx(mHeaderEntriesFileName, (U8*)&mHeaderEntriesInfo, 0, sizeof(EntriesInfo),
+ mHeaderAPRFilePoolp);
}
}
@@ -1195,7 +1210,8 @@ void LLTextureCache::writeEntryToHeaderImmediately(S32& idx, Entry& entry, bool
idx = -1 ;//mark the idx invalid.
return ;
}
- aprfile->seek(APR_SET, offset);
+
+ mHeaderAPRFile->seek(APR_SET, offset);
}
else
{
@@ -1599,7 +1615,7 @@ void LLTextureCache::purgeAllTextures(bool purge_directories)
gDirUtilp->deleteFilesInDir(mTexturesDirName, mask); // headers, fast cache
if (purge_directories)
{
- LLFile::remove(mTexturesDirName);
+ LLFile::rmdir(mTexturesDirName);
}
}
mHeaderIDMap.clear();
@@ -1779,7 +1795,8 @@ void LLTextureCache::purgeTextures(bool validate)
{
std::string filename = getTextureFileName(entries[idx].mID);
LL_DEBUGS("TextureCache") << "Validating: " << filename << "Size: " << entries[idx].mBodySize << LL_ENDL;
- S32 bodysize = (S32)LLFile::size(filename);
+ // mHeaderAPRFilePoolp because this is under header mutex in main thread
+ S32 bodysize = LLAPRFile::size(filename, mHeaderAPRFilePoolp);
if (bodysize != entries[idx].mBodySize)
{
LL_WARNS("TextureCache") << "TEXTURE CACHE BODY HAS BAD SIZE: " << bodysize << " != " << entries[idx].mBodySize << filename << LL_ENDL;
@@ -2130,7 +2147,7 @@ void LLTextureCache::openFastCache(bool first_time)
mFastCachePadBuffer = (U8*)ll_aligned_malloc_16(TEXTURE_FAST_CACHE_ENTRY_SIZE);
}
mFastCachePoolp = new LLVolatileAPRPool(); // is_local= true by default, so not thread safe by default
- if (LLFile::isfile(mFastCacheFileName))
+ if (LLAPRFile::isExist(mFastCacheFileName, mFastCachePoolp))
{
mFastCachep = new LLAPRFile(mFastCacheFileName, APR_READ|APR_WRITE|APR_BINARY, mFastCachePoolp) ;
}
@@ -2213,7 +2230,9 @@ void LLTextureCache::removeCachedTexture(const LLUUID& id)
mTexturesSizeMap.erase(id);
}
mHeaderIDMap.erase(id);
- LLFile::remove(getTextureFileName(id));
+ // We are inside header's mutex so mHeaderAPRFilePoolp is safe to use,
+ // but getLocalAPRFilePool() is not safe, it might be in use by worker
+ LLAPRFile::remove(getTextureFileName(id), mHeaderAPRFilePoolp);
}
//called after mHeaderMutex is locked.
@@ -2226,7 +2245,9 @@ void LLTextureCache::removeEntry(S32 idx, Entry& entry, std::string& filename)
if (entry.mBodySize == 0) // Always attempt to remove when mBodySize > 0.
{
// Sanity check. Shouldn't exist when body size is 0.
- if (LLFile::isfile(filename))
+ // We are inside header's mutex so mHeaderAPRFilePoolp is safe to use,
+ // but getLocalAPRFilePool() is not safe, it might be in use by worker
+ if (LLAPRFile::isExist(filename, mHeaderAPRFilePoolp))
{
LL_WARNS("TextureCache") << "Entry has body size of zero but file " << filename << " exists. Deleting this file, too." << LL_ENDL;
}
@@ -2246,7 +2267,7 @@ void LLTextureCache::removeEntry(S32 idx, Entry& entry, std::string& filename)
if (file_maybe_exists)
{
- LLFile::remove(filename);
+ LLAPRFile::remove(filename, mHeaderAPRFilePoolp);
}
}
diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp
index e76d340eda..141f370ecb 100644
--- a/indra/newview/llviewerassetstorage.cpp
+++ b/indra/newview/llviewerassetstorage.cpp
@@ -182,7 +182,7 @@ void LLViewerAssetStorage::storeAssetData(
else
{
// LLAssetStorage metric: Successful Request
- S32 size = (S32)LLFileSystem::getFileSize(asset_id, asset_type);
+ S32 size = LLFileSystem::getFileSize(asset_id, asset_type);
const char *message = "Added to upload queue";
reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, size, MR_OKAY, __FILE__, __LINE__, message );
diff --git a/indra/newview/llviewerassetupload.cpp b/indra/newview/llviewerassetupload.cpp
index 5ac7d6b1fe..65a69acc88 100644
--- a/indra/newview/llviewerassetupload.cpp
+++ b/indra/newview/llviewerassetupload.cpp
@@ -45,7 +45,11 @@
#include "llappviewer.h"
#include "llviewerstats.h"
#include "llfilesystem.h"
+#include "llgesturemgr.h"
+#include "llpreviewnotecard.h"
+#include "llpreviewgesture.h"
#include "llcoproceduremanager.h"
+#include "llthread.h"
#include "llkeyframemotion.h"
#include "lldatapacker.h"
#include "llvoavatarself.h"
@@ -401,7 +405,6 @@ LLSD LLNewFileResourceUploadInfo::exportTempFile()
std::string errorMessage;
std::string errorLabel;
- std::error_code ec;
bool error = false;
@@ -472,38 +475,30 @@ LLSD LLNewFileResourceUploadInfo::exportTempFile()
error = true;
// read from getFileName()
- LLFile infile(getFileName(), LLFile::in | LLFile::binary, ec);
- if (ec || !infile)
+ LLAPRFile infile;
+ infile.open(getFileName(),LL_APR_RB);
+ if (!infile.getFileHandle())
{
LL_WARNS() << "Couldn't open file for reading: " << getFileName() << LL_ENDL;
errorMessage = llformat("Failed to open animation file %s\n", getFileName().c_str());
}
else
{
- S64 size = infile.size(ec);
- if (ec || size <= 0)
- {
- LLError::LLUserWarningMsg::showMissingFiles();
- LL_ERRS() << "Invalid file" << LL_ENDL;
- }
- else if (size > INT_MAX)
- {
- LL_ERRS() << "File is too big, size: " << size << LL_ENDL;
- }
+ S32 size = LLAPRFile::size(getFileName());
U8* buffer = new(std::nothrow) U8[size];
if (!buffer)
{
LLError::LLUserWarningMsg::showOutOfMemory();
LL_ERRS() << "Bad memory allocation for buffer, size: " << size << LL_ENDL;
}
- S64 size_read = infile.read(buffer, size, ec);
- if (ec || size_read != size)
+ S32 size_read = infile.read(buffer,size);
+ if (size_read != size)
{
errorMessage = llformat("Failed to read animation file %s: wanted %d bytes, got %d\n", getFileName().c_str(), size, size_read);
}
else
{
- LLDataPackerBinaryBuffer dp(buffer, (S32)size);
+ LLDataPackerBinaryBuffer dp(buffer, size);
LLKeyframeMotion *motionp = new LLKeyframeMotion(getAssetId());
motionp->setCharacter(gAgentAvatarp);
if (motionp->deserialize(dp, getAssetId(), false))
@@ -549,17 +544,18 @@ LLSD LLNewFileResourceUploadInfo::exportTempFile()
setAssetType(assetType);
// copy this file into the cache for upload
- LLFile infile(filename, LLFile::in | LLFile::binary, ec);
- if (!ec && infile.size(ec) > 0)
+ S32 file_size;
+ LLAPRFile infile;
+ infile.open(filename, LL_APR_RB, NULL, &file_size);
+ if (infile.getFileHandle())
{
LLFileSystem file(getAssetId(), assetType, LLFileSystem::APPEND);
- S64 read_bytes;
const S32 buf_size = 65536;
U8 copy_buf[buf_size];
- while (((read_bytes = infile.read(copy_buf, buf_size, ec))) > 0)
+ while ((file_size = infile.read(copy_buf, buf_size)))
{
- file.write(copy_buf, (S32)read_bytes);
+ file.write(copy_buf, file_size);
}
}
else
@@ -573,6 +569,7 @@ LLSD LLNewFileResourceUploadInfo::exportTempFile()
}
return LLSD();
+
}
//=========================================================================
diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp
index 7534d778a5..35ac7919ac 100755..100644
--- a/indra/newview/llviewerdisplay.cpp
+++ b/indra/newview/llviewerdisplay.cpp
@@ -1140,12 +1140,15 @@ 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 (LLFile::exists(fsyspath(name)));
+ } 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?
return name;
}
diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp
index 5cb05460bc..a77b9f6103 100755..100644
--- a/indra/newview/llviewermedia.cpp
+++ b/indra/newview/llviewermedia.cpp
@@ -1827,11 +1827,12 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_
user_data_path_cache += gDirUtilp->getDirDelimiter();
// See if the plugin executable exists
- if (!LLFile::isfile(launcher_name))
+ llstat s;
+ if(LLFile::stat(launcher_name, &s))
{
LL_WARNS_ONCE("Media") << "Couldn't find launcher at " << launcher_name << LL_ENDL;
}
- else if (!LLFile::isfile(plugin_name))
+ else if(LLFile::stat(plugin_name, &s))
{
#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 cdc41baa88..5d456b1a19 100755..100644
--- 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::remove(cache_dir);
+ LLFile::rmdir(cache_dir);
clearCacheInMemory();
mInitialized = false;
@@ -1370,7 +1370,7 @@ void LLVOCache::removeFromCache(HeaderEntryInfo* entry)
std::string filename;
getObjectCacheFilename(entry->mHandle, filename);
LL_WARNS("GLTF", "VOCache") << "Removing object cache for handle " << entry->mHandle << "Filename: " << filename << LL_ENDL;
- LLFile::remove(filename);
+ LLAPRFile::remove(filename, mLocalAPRFilePoolp);
// Note: `removeFromCache` should take responsibility for cleaning up all cache artefacts specfic to the handle/entry.
// as such this now includes the generic extras
@@ -1394,7 +1394,7 @@ void LLVOCache::readCacheHeader()
clearCacheInMemory();
bool success = true ;
- if (LLFile::isfile(mHeaderFileName))
+ if (LLAPRFile::isExist(mHeaderFileName, mLocalAPRFilePoolp))
{
LLAPRFile apr_file(mHeaderFileName, APR_READ|APR_BINARY, mLocalAPRFilePoolp);
diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp
index e58a6577f1..d132cbfa36 100755..100644
--- a/indra/newview/llvoicevivox.cpp
+++ b/indra/newview/llvoicevivox.cpp
@@ -943,7 +943,8 @@ bool LLVivoxVoiceClient::startAndLaunchDaemon()
gDirUtilp->append(exe_path, "SLVoice");
#endif
// See if the vivox executable exists
- if (LLFile::isfile(exe_path))
+ llstat s;
+ if (!LLFile::stat(exe_path, &s))
{
// vivox executable exists. Build the command line and launch the daemon.
LLProcess::Params params;
diff --git a/indra/test/CMakeLists.txt b/indra/test/CMakeLists.txt
index 69c9660575..246fc5e6f8 100755..100644
--- a/indra/test/CMakeLists.txt
+++ b/indra/test/CMakeLists.txt
@@ -16,7 +16,6 @@ set(test_SOURCE_FILES
llbuffer_tut.cpp
lldoubledispatch_tut.cpp
llevents_tut.cpp
- llfile_tut.cpp
llhttpdate_tut.cpp
llhttpnode_tut.cpp
lliohttpserver_tut.cpp
@@ -68,7 +67,7 @@ target_link_libraries(lltest
if (WINDOWS)
set_target_properties(lltest
- PROPERTIES
+ PROPERTIES
LINK_FLAGS "/NODEFAULTLIB:LIBCMT"
LINK_FLAGS_DEBUG "/NODEFAULTLIB:\"LIBCMT;LIBCMTD;MSVCRT\""
RUNTIME_OUTPUT_DIRECTORY "${EXE_STAGING_DIR}"
@@ -86,10 +85,10 @@ set(TEST_EXE $<TARGET_FILE:lltest>)
SET_TEST_PATH(LD_LIBRARY_PATH)
-LL_TEST_COMMAND(command
+LL_TEST_COMMAND(command
"${LD_LIBRARY_PATH}"
"${TEST_EXE}"
- "--output=${CMAKE_CURRENT_BINARY_DIR}/cpp_test_results.txt"
+ "--output=${CMAKE_CURRENT_BINARY_DIR}/cpp_test_results.txt"
"--touch=${CMAKE_CURRENT_BINARY_DIR}/cpp_tests_ok.txt")
ADD_CUSTOM_COMMAND(
@@ -102,11 +101,11 @@ ADD_CUSTOM_COMMAND(
set(test_results ${CMAKE_CURRENT_BINARY_DIR}/cpp_tests_ok.txt)
-# This should cause the test executable to be built, but not
+# This should cause the test executable to be built, but not
# run if LL_TESTS is disabled. This will hopefully keep the
-# tests up to date with any code changes changes even if
+# tests up to date with any code changes changes even if
# developers choose to disable LL_TESTS.
-if (LL_TESTS)
+if (LL_TESTS)
add_custom_target(tests_ok ALL DEPENDS ${test_results})
if(DARWIN)
# Support our "@executable_path/../Resources" load path for our test
diff --git a/indra/test/llfile_tut.cpp b/indra/test/llfile_tut.cpp
deleted file mode 100755
index 9f2a9b2988..0000000000
--- a/indra/test/llfile_tut.cpp
+++ /dev/null
@@ -1,253 +0,0 @@
-/**
- * @file llfile_tut.cpp
- * @author Frederick Martian
- * @date 2025-11
- * @brief LLFile test cases.
- *
- * $LicenseInfo:firstyear=2025&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2025, Linden Research, Inc.
- *
- * 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.
- *
- * 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.
- *
- * 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
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include <tut/tut.hpp>
-#include "lltut.h"
-#include "linden_common.h"
-#include "llfile.h"
-
-namespace tut
-{
- static void clear_entire_dir(std::filesystem::path &dir)
- {
- std::error_code ec;
- std::filesystem::remove_all(dir, ec);
- }
-
- static std::filesystem::path append_filename(const std::filesystem::path& dir, const std::string& element)
- {
- std::filesystem::path path = dir;
- return path.append(element);
- }
-
- static std::filesystem::path get_testdir(const std::filesystem::path& tempdir)
- {
- return append_filename(tempdir, std::string("test_dir"));
- }
-
- struct llfile_test
- {
- std::filesystem::path tempdir = LLFile::tmpdir();
- std::filesystem::path testdir = get_testdir(tempdir);
- };
- typedef test_group<llfile_test> llfile_test_t;
- typedef llfile_test_t::object llfile_test_object_t;
- tut::llfile_test_t tut_llfile_test("llfile_test");
-
- template<> template<>
- void llfile_test_object_t::test<1>()
- {
- // Test creating directories and files and deleting them and checking if the
- // relevant status functions work as expected
- ensure("LLFile::tmpdir() empty", !tempdir.empty());
- ensure("LLFile::tmpdir() doesn't exist", LLFile::exists(tempdir.string()));
- ensure("LLFile::tmpdir() is not a directory", LLFile::isdir(tempdir.string()));
- ensure("LLFile::tmpdir() should not be a file", !LLFile::isfile(tempdir.string()));
-
- // Make sure there is nothing left from a previous test run
- clear_entire_dir(testdir);
- ensure("llfile_test should not exist anymore", !LLFile::exists(testdir.string()));
-
- int rc = LLFile::mkdir(testdir.string());
- ensure("LLFile::mkdir() failed", rc == 0);
- ensure("llfile_test should be a directory", LLFile::isdir(testdir.string()));
- rc = LLFile::mkdir(testdir.string());
- ensure("LLFile::mkdir() should not fail when the directory already exists", rc == 0);
-
- std::filesystem::path testfile1 = testdir;
- testfile1.append("llfile_test.dat");
- ensure("llfile_test1.dat should not yet exist", !LLFile::exists(testfile1.string()));
-
- const char* testdata = "testdata";
- S64 bytes = LLFile::write(testfile1.string(), testdata, 0, sizeof(testdata));
- ensure("LLFile::write() did not write correctly", bytes == sizeof(testdata));
-
- rc = LLFile::remove(testfile1.string());
- ensure("LLFile::remove() for file test_file.dat", rc == 0);
- ensure("llfile_test.dat should not exist anymore", !LLFile::exists(testfile1.string()));
- ensure("llfile_test.dat should not be a file", !LLFile::isfile(testfile1.string()));
- ensure("llfile_test.dat should not be a directory", !LLFile::isdir(testfile1.string()));
- ensure("llfile_test.dat should not be a symlink", !LLFile::islink(testfile1.string()));
-
- rc = LLFile::remove(testdir.string());
- ensure("LLFile::remove() for directory llfile_test failed", rc == 0);
- ensure("llfile_test should not exist anymore", !LLFile::exists(testdir.string()));
- }
-
- template<> template<>
- void llfile_test_object_t::test<2>()
- {
- // High level static file IO functions to read and write data files
- LLFile::mkdir(testdir.string());
- ensure("llfile_test should exist", LLFile::isdir(testdir.string()));
-
- std::filesystem::path testfile1 = testdir;
- testfile1.append("llfile_test.dat");
-
- std::string testdata1("testdata");
- std::string testdata2("datateststuff");
- std::time_t current = time(nullptr);
- S64 bytes = LLFile::write(testfile1.string(), testdata1.c_str(), 0, testdata1.length());
- ensure("LLFile::write() did not write correctly", bytes == testdata1.length());
- ensure("llfile_test.dat should exist", LLFile::exists(testfile1.string()));
- ensure("llfile_test.dat should be a file", LLFile::isfile(testfile1.string()));
- ensure("llfile_test.dat should not be a directory", !LLFile::isdir(testfile1.string()));
-
- bytes = LLFile::size(testfile1.string());
- ensure("LLFile::size() did not return the correct size", bytes == testdata1.length());
-
- std::string data = LLFile::getContents(testfile1.string());
- ensure("LLFile::getContents() did not return the correct size data", data.length() == testdata1.length());
- ensure_memory_matches("LLFile::getContents() did not read correct data", testdata1.c_str(), (U32)testdata1.length(), data.c_str(), (U32)data.length());
-
- std::time_t ctime = LLFile::getCreationTime(testfile1.string());
- ensure_approximately_equals_range("LLFile::getCreationTime() did not return correct time", (F32)(ctime - current), 0.f, 1);
-
- std::time_t mtime = LLFile::getModificationTime(testfile1.string());
- ensure_approximately_equals_range("LLFile::getModificationTime() did not return correct time", (F32)(mtime - current), 0.f, 1);
-
- char buffer[1024];
- bytes = LLFile::read(testfile1.string(), buffer, 0, testdata1.length());
- ensure("LLFile:read() did not return the correct size", bytes == testdata1.length());
- ensure_memory_matches("LLFile::read() did not read correct data", testdata1.c_str(), (U32)bytes, buffer, (U32)bytes);
-
- // What if we try to read more data than there is in the file?
- bytes = LLFile::read(testfile1.string(), buffer, 0, bytes + 10);
- ensure("LLFile:read() did not correctly stop on eof", bytes == testdata1.length());
- ensure_memory_matches("LLFile::read() did not read correct data", testdata1.c_str(), (U32)bytes, buffer, (U32)bytes);
-
- // Let's append more data
- bytes = LLFile::write(testfile1.string(), testdata2.c_str(), -1, testdata2.length());
- ensure("LLFile::write() did not write correctly", bytes == testdata2.length());
-
- bytes = LLFile::size(testfile1.string());
- ensure("LLFile::size() did not return the correct size", bytes == testdata1.length() + testdata2.length());
- bytes = LLFile::read(testfile1.string(), buffer, 0, bytes);
- ensure("LLFile:read() did not read correct number of bytes", bytes == testdata1.length() + testdata2.length());
- ensure_memory_matches("LLFile:read() did not read correct testdata1", testdata1.c_str(), (U32)testdata1.length(), buffer, (U32)testdata1.length());
- ensure_memory_matches("LLFile:read() did not read correct testdata2", testdata2.c_str(), (U32)testdata2.length(), buffer + testdata1.length(), (U32)testdata2.length());
- }
-
- template<> template<>
- void llfile_test_object_t::test<3>()
- {
- const size_t numints = 1024;
-
- // Testing the LLFile class implementation
- std::filesystem::path testfile = testdir;
- testfile.append("llfile_test.bin");
-
- int data[numints];
- for (int &t : data)
- {
- t = rand();
- }
-
- std::error_code ec;
- LLFile fileout(testfile.string(), LLFile::out, ec);
- ensure("LLFile constructor did not open correctly", (bool)fileout);
- ensure("error_code from LLFile constructor should not indicate an error", !ec);
- if (fileout)
- {
- S64 length = fileout.size(ec);
- ensure("freshly created file should be empty", length == 0);
- ensure("error_code from LLFile::size() should not indicate an error", !ec);
- S64 bytes = fileout.write(data, sizeof(data), ec);
- ensure("LLFile::write() did not write correctly", bytes == sizeof(data));
- ensure("error_code from LLFile::write() should not indicate an error", !ec);
- bytes = fileout.write(data, sizeof(data), ec);
- ensure("LLFile::write() did not write correctly", bytes == sizeof(data));
- ensure("error_code from LLFile::write() should not indicate an error", !ec);
- bytes = fileout.size(ec);
- ensure("LLFile::size() returned wrong size", bytes == 2 * sizeof(data));
- ensure("error_code from LLFile::size() should not indicate an error", !ec);
- fileout.close();
- }
-
- LLFile filein(testfile.string(), LLFile::in, ec);
- ensure("LLFile constructor did not open correctly", (bool)filein);
- ensure("error_code from LLFile constructor should not indicate an error", !ec);
- if (filein)
- {
- S64 length = filein.size(ec);
- ensure("LLFile::size() returned wrong size", length == 2 * sizeof(data));
- ensure("error_code from LLFile::size() should not indicate an error", !ec);
- char* buffer = (char*)malloc(length);
- S64 bytes = filein.read(buffer, length, ec);
- ensure("LLFile::read() did not read correctly", bytes == length);
- ensure("error_code from LLFile::read() should not indicate an error", !ec);
- ensure_memory_matches("LLFile:read() did not read correct data1", data, (U32)sizeof(data), buffer, (U32)sizeof(data));
- ensure_memory_matches("LLFile:read() did not read correct data2", data, (U32)sizeof(data), buffer + sizeof(data), (U32)sizeof(data));
- S64 offset = filein.tell(ec);
- ensure("LLFile::tell() returned a bad offset", offset == length);
- ensure("error_code from LLFile::read() should not indicate an error", !ec);
- offset = sizeof(data) / 2;
- int rc = filein.seek(offset, ec);
- ensure("LLFile::seek() indicated an error", rc == 0);
- ensure("error_code from LLFile::seek() should not indicate an error", !ec);
- bytes = filein.read(buffer, 2 * sizeof(data), ec);
- ensure("LLFile::read() did not read correctly", bytes == sizeof(data) + offset);
- ensure("error_code from LLFile::read() should not indicate an error", !ec);
- ensure_memory_matches("LLFile:read() did not read correct data3", (char*)data + offset, (U32)offset, buffer, (U32)offset);
- ensure_memory_matches("LLFile:read() did not read correct data4", (char*)data, (U32)sizeof(data), buffer + offset, (U32)sizeof(data));
- filein.close();
-
- free(buffer);
- }
- }
-
- template<> template<>
- void llfile_test_object_t::test<4>()
- {
- // Testing the LLFile class implementation with wrong paths and parameters
- std::filesystem::path testfile = testdir;
- testfile.append("llfile_test.bin");
-
- std::error_code ec;
- LLFile file(testfile.string(), LLFile::out | LLFile::noreplace, ec);
- ensure("LLFile constructor should not have opened the already existing file", !file);
- ensure("error_code from LLFile constructor should indicate an error", (bool)ec);
-
- LLFile::remove(testfile.string());
- file = LLFile(testfile.string(), LLFile::out | LLFile::app | LLFile::trunc, ec);
- ensure("LLFile constructor should not have opened the file with conflicting flags", !file);
- ensure("error_code from LLFile constructor should indicate an error", (bool)ec);
-
- file = LLFile(testfile.string(), LLFile::out | LLFile::app | LLFile::noreplace, ec);
- ensure("LLFile constructor should not have opened the file with conflicting flags", !file);
- ensure("error_code from LLFile constructor should indicate an error", (bool)ec);
-
- testfile = testdir;
- testfile.append("llfile_test");
- testfile.append("llfile_test.bin");
-
- file = LLFile(testfile.string(), LLFile::in, ec);
- ensure("LLFile constructor should not have been able to open the file in the non-existing directory", !file);
- ensure("error_code from LLFile constructor should indicate an error", (bool)ec);
- }
-} // namespace tut
diff --git a/indra/test/llmessageconfig_tut.cpp b/indra/test/llmessageconfig_tut.cpp
index b8942e99b3..93443467a2 100755..100644
--- 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::remove(mTestConfigDir);
+ int rmdir = LLFile::rmdir(mTestConfigDir);
ensure_equals("rmdir value", rmdir, 0);
}
diff --git a/indra/test/message_tut.cpp b/indra/test/message_tut.cpp
index 6fb2b92803..11cd710ef6 100755..100644
--- 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::remove(mTestConfigDir);
+ int rmdir = LLFile::rmdir(mTestConfigDir);
ensure_equals("rmdir value", rmdir, 0);
}