summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrederick Martian <fredmartian@gmail.com>2025-10-26 19:59:02 +0100
committerAndrey Kleshchev <117672381+akleshchev@users.noreply.github.com>2025-12-10 20:33:58 +0200
commit9756b4b70b3cd24c03142d7e52eb2acae52002ef (patch)
tree0f99eafe4bcd01661b508cf538206cfcb382346b
parent3ca3ea75c333078013914e174564340f894573e2 (diff)
Clarify some documentation and add an LLFile:read() and LLFile::write() static function. These functions will be used to replace LLAPRFile::readEx() and LLAPRFile::writeEx() in an upcoming patch.
-rw-r--r--indra/llcommon/llfile.cpp157
-rw-r--r--indra/llcommon/llfile.h33
2 files changed, 144 insertions, 46 deletions
diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp
index 4ef39674c2..9fff614efb 100644
--- a/indra/llcommon/llfile.cpp
+++ b/indra/llcommon/llfile.cpp
@@ -155,12 +155,15 @@ static std::wstring utf8path_to_wstring(const std::string& utf8path)
}
return ll_convert<std::wstring>(utf8path);
}
-
-static unsigned short get_fileattr(const std::wstring& utf16path, bool dontFollowSymLink = false)
+// 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;
+ 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,
@@ -175,27 +178,27 @@ static unsigned short get_fileattr(const std::wstring& utf16path, bool dontFollo
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 = 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
+ // 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 st_mode;
+ return 0;
}
}
// Retrieve last error and set errno before calling CloseHandle()
- set_errno_from_oserror(GetLastError());
+ int rc = set_errno_from_oserror(GetLastError());
if (file_handle != INVALID_HANDLE_VALUE)
{
CloseHandle(file_handle);
}
- return 0;
+ return rc;
}
#else
@@ -343,7 +346,11 @@ int LLFile::rmdir(const std::string& dirname, int suppress_error)
{
#if LL_WINDOWS
std::wstring utf16dirname = utf8path_to_wstring(dirname);
- int rc = _wrmdir(utf16dirname.c_str());
+ if (RemoveDirectoryW(utf16dirname.c_str()))
+ {
+ return 0;
+ }
+ int rc = set_errno_from_oserror(GetLastError());
#else
int rc = ::rmdir(dirname.c_str());
#endif
@@ -402,27 +409,31 @@ int LLFile::remove(const std::string& filename, int suppress_error)
// 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))
- {
- rc = _wrmdir(utf16filename.c_str());
- }
- else if (S_ISREG(st_mode))
- {
- rc = _wunlink(utf16filename.c_str());
- }
- else if (st_mode)
- {
- // 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
+ unsigned short st_mode;
+ int rc = get_fileattr(utf16filename, st_mode);
+ if (rc == 0)
{
- // get_fileattr() failed and already set errno, preserve it for correct error reporting
+ if (S_ISDIR(st_mode))
+ {
+ if (RemoveDirectoryW(utf16filename.c_str()))
+ {
+ return 0;
+ }
+ }
+ else if (S_ISREG(st_mode))
+ {
+ if (DeleteFileW(utf16filename.c_str()))
+ {
+ return 0;
+ }
+ }
+ else
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ }
+ // Deleting the file or directory failed
+ rc = set_errno_from_oserror(GetLastError());
}
#else
int rc = ::remove(filename.c_str());
@@ -439,17 +450,91 @@ int LLFile::rename(const std::string& filename, const std::string& newname, int
// 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))
+ if (MoveFileExW(utf16filename.c_str(), utf16newname.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
{
- rc = set_errno_from_oserror(GetLastError());
+ return 0;
}
+ int rc = set_errno_from_oserror(GetLastError());
#else
int rc = ::rename(filename.c_str(),newname.c_str());
#endif
return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, rc, suppress_error);
}
+// static
+U64 LLFile::read(const std::string& filename, void* buf, U64 offset, U64 nbytes)
+{
+ LLFILE* file_handle = LLFile::fopen(filename, "rb");
+ if (file_handle == nullptr)
+ {
+ warnif("read, opening file", filename, -1);
+ return 0;
+ }
+
+ if (offset > 0)
+ {
+#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)
+ {
+ warnif("read, setting file offset", filename, rc);
+ fclose(file_handle);
+ return 0;
+ }
+ }
+
+ // 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
+U64 LLFile::write(const std::string& filename, const void* buf, U64 offset, U64 nbytes)
+{
+ LLFILE* file_handle = LLFile::fopen(filename, "wb");
+ if (file_handle == nullptr)
+ {
+ warnif("write, opening file", filename, -1);
+ return 0;
+ }
+
+ if (offset > 0)
+ {
+#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)
+ {
+ warnif("write, setting file offset", filename, rc);
+ fclose(file_handle);
+ return 0;
+ }
+ }
+
+ // 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;
+}
+
// Make this a define rather than using magic numbers multiple times in the code
#define LLFILE_COPY_BUFFER_SIZE 16384
@@ -523,7 +608,7 @@ S64 LLFile::size(const std::string& filename, int suppress_error)
int rc = ::stat(filename.c_str(), &filestatus);
if (rc == 0)
{
- if (S_ISREG(filestatus.st_mode)
+ if (S_ISREG(filestatus.st_mode))
{
return filestatus.st_size;
}
@@ -542,10 +627,10 @@ unsigned short LLFile::getattr(const std::string& filename, bool dontFollowSymLi
#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)
+ unsigned short st_mode;
+ int rc = get_fileattr(utf16filename, st_mode, dontFollowSymLink);
+ if (rc == 0)
{
return st_mode;
}
diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h
index 61ec8a5e4e..a856f34be6 100644
--- a/indra/llcommon/llfile.h
+++ b/indra/llcommon/llfile.h
@@ -45,8 +45,8 @@ typedef FILE LLFILE;
// We use _stat64 here to support 64-bit st_size and time_t values
typedef struct _stat64 llstat;
#else
-typedef struct stat llstat;
#include <sys/types.h>
+typedef struct stat llstat;
#endif
#ifndef S_ISREG
@@ -74,8 +74,11 @@ typedef struct stat llstat;
class LL_COMMON_API LLFile
{
public:
+//----------------------------------------------------------------------------------------
+// Static member functions
+//----------------------------------------------------------------------------------------
/// open a file with the specified access mode
- static LLFILE* fopen(const std::string& filename, const char* accessmode); /* Flawfinder: ignore */
+ static LLFILE* fopen(const std::string& filename, const char* accessmode);
///< '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
@@ -91,14 +94,13 @@ public:
/// 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
+ /// @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);
- /// 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, int perms = 0700);
///< perms is a permissions mask like 0777 or 0700. In most cases it will be
@@ -127,11 +129,22 @@ public:
/// does not make such guarantees.
/// @returns 0 on success and -1 on failure.
-
- /// copy the contents of file from 'from' to 'to' filename
+ /// copy the contents of the 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.
+ /// 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
+
+ /// 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
+
+ /// 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
+
/// return the file stat structure for filename
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
@@ -145,7 +158,7 @@ public:
/// 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
+ /// @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.
/// get the size of a file in bytes