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