Files
RedHotRoast-ios/HybridCLRData/LocalIl2CppData-OSXEditor/il2cpp/libil2cpp/os/Posix/File.cpp
T
2026-07-17 14:03:00 +08:00

1210 lines
32 KiB
C++

#include "il2cpp-config.h"
#if IL2CPP_TARGET_POSIX
#include "FileHandle.h"
#if IL2CPP_USE_POSIX_FILE_PLATFORM_CONFIG
#include "FilePlatformConfig.h"
#endif
#include "os/ConsoleExtension.h"
#include "os/ErrorCodes.h"
#include "os/File.h"
#include "os/Mutex.h"
#include "os/Posix/Error.h"
#include "utils/Expected.h"
#include "utils/Il2CppError.h"
#include "utils/PathUtils.h"
#if IL2CPP_SUPPORT_THREADS
#include "Baselib.h"
#include "Cpp/ReentrantLock.h"
#endif
#include <fcntl.h>
#include <stdint.h>
#include <unistd.h>
#include <utime.h>
#if IL2CPP_TARGET_QNX
#include <errno.h>
#else
#include <sys/errno.h>
#endif
#include <sys/stat.h>
#include <sys/types.h>
#include <string>
#define INVALID_FILE_HANDLE (FileHandle*)-1
#define INVALID_FILE_ATTRIBUTES (UnityPalFileAttributes)((uint32_t)-1)
#define TIME_ZERO 116444736000000000ULL
namespace il2cpp
{
namespace os
{
// Head and tail of linked list.
static FileHandle* s_fileHandleHead = NULL;
static FileHandle* s_fileHandleTail = NULL;
#if IL2CPP_SUPPORT_THREADS
static baselib::ReentrantLock s_fileHandleMutex;
#endif
static void AddFileHandle(FileHandle *fileHandle)
{
#if IL2CPP_SUPPORT_THREADS
FastAutoLock autoLock(&s_fileHandleMutex);
#endif
if (s_fileHandleHead == NULL)
{
IL2CPP_ASSERT(s_fileHandleTail == NULL);
s_fileHandleHead = fileHandle;
s_fileHandleTail = fileHandle;
}
else
{
IL2CPP_ASSERT(s_fileHandleTail != NULL);
IL2CPP_ASSERT(s_fileHandleTail->next == NULL);
s_fileHandleTail->next = fileHandle;
fileHandle->prev = s_fileHandleTail;
s_fileHandleTail = fileHandle;
}
}
static void RemoveFileHandle(il2cpp::os::FileHandle *fileHandle)
{
#if IL2CPP_SUPPORT_THREADS
FastAutoLock autoLock(&s_fileHandleMutex);
#endif
if (s_fileHandleHead == fileHandle)
s_fileHandleHead = fileHandle->next;
if (s_fileHandleTail == fileHandle)
s_fileHandleTail = fileHandle->prev;
if (fileHandle->prev)
fileHandle->prev->next = fileHandle->next;
if (fileHandle->next)
fileHandle->next->prev = fileHandle->prev;
}
static const FileHandle* FindFileHandle(const struct stat& statBuf)
{
#if IL2CPP_SUPPORT_THREADS
FastAutoLock autoLock(&s_fileHandleMutex);
#endif
const dev_t device = statBuf.st_dev;
const ino_t inode = statBuf.st_ino;
for (FileHandle *handle = s_fileHandleHead; handle != NULL; handle = handle->next)
{
if (handle->device == device && handle->inode == inode)
return handle;
}
return NULL;
}
bool File::IsHandleOpenFileHandle(intptr_t lookup)
{
#if IL2CPP_SUPPORT_THREADS
FastAutoLock autoLock(&s_fileHandleMutex);
#endif
for (FileHandle *handle = s_fileHandleHead; handle != NULL; handle = handle->next)
{
if (reinterpret_cast<intptr_t>(handle) == lookup)
return true;
}
return false;
}
// NOTE:
// Checking for file sharing violations only works for the current process.
//
// Mono implements this feature across processes by storing the file handles as
// a look up table in a shared file.
static bool ShareAllowOpen(const struct stat& statBuf, int shareMode, int accessMode)
{
const FileHandle *fileHandle = FindFileHandle(statBuf);
if (fileHandle == NULL) // File is not open
return true;
if (fileHandle->shareMode == kFileShareNone || shareMode == kFileShareNone)
return false;
if (((fileHandle->shareMode == kFileShareRead) && (accessMode != kFileAccessRead)) ||
((fileHandle->shareMode == kFileShareWrite) && (accessMode != kFileAccessWrite)))
{
return false;
}
return true;
}
static UnityPalFileAttributes StatToFileAttribute(const std::string& path, struct stat& pathStat, struct stat* linkStat)
{
uint32_t fileAttributes = 0;
if (S_ISSOCK(pathStat.st_mode))
pathStat.st_mode &= ~S_IFSOCK; // don't consider socket protection
#if defined(__APPLE__) && defined(__MACH__)
if ((pathStat.st_flags & UF_IMMUTABLE) || (pathStat.st_flags & SF_IMMUTABLE))
fileAttributes |= kFileAttributeReadOnly;
#endif
const std::string filename(il2cpp::utils::PathUtils::Basename(path));
if (S_ISDIR(pathStat.st_mode))
{
fileAttributes = kFileAttributeDirectory;
if (!(pathStat.st_mode & S_IWUSR) && !(pathStat.st_mode & S_IWGRP) && !(pathStat.st_mode & S_IWOTH))
fileAttributes |= kFileAttributeReadOnly;
if (filename[0] == '.')
fileAttributes |= kFileAttributeHidden;
}
else
{
if (!(pathStat.st_mode & S_IWUSR) && !(pathStat.st_mode & S_IWGRP) && !(pathStat.st_mode & S_IWOTH))
{
fileAttributes = kFileAttributeReadOnly;
if (filename[0] == '.')
fileAttributes |= kFileAttributeHidden;
}
else if (filename[0] == '.')
fileAttributes = kFileAttributeHidden;
else
fileAttributes = kFileAttributeNormal;
}
if (linkStat != NULL && S_ISLNK(linkStat->st_mode))
fileAttributes |= kFileAttributeReparse_point;
return (UnityPalFileAttributes)fileAttributes;
}
static int GetStatAndLinkStat(const std::string& path, struct stat& pathStat, struct stat& linkStat)
{
const int statResult = stat(path.c_str(), &pathStat);
if (statResult == -1 && errno == ENOENT && lstat(path.c_str(), &pathStat) != 0) // Might be a dangling symlink...
return PathErrnoToErrorCode(path, errno);
if (lstat(path.c_str(), &linkStat) != 0)
return PathErrnoToErrorCode(path, errno);
return kErrorCodeSuccess;
}
static uint64_t TimeToTicks(time_t timeval)
{
return ((uint64_t)timeval * 10000000) + TIME_ZERO;
}
static time_t TicksToTime(uint64_t ticks)
{
return (ticks - TIME_ZERO) / 10000000;
}
static bool InternalCopyFile(int srcFd, int destFd, const struct stat& srcStat, int *error)
{
const blksize_t preferedBlockSize = srcStat.st_blksize;
const blksize_t bufferSize = preferedBlockSize < 8192 ? 8192 : (preferedBlockSize > 65536 ? 65536 : preferedBlockSize);
char *buffer = new char[bufferSize];
ssize_t readBytes;
while ((readBytes = read(srcFd, buffer, bufferSize)) > 0)
{
char* writeBuffer = buffer;
ssize_t writeBytes = readBytes;
while (writeBytes > 0)
{
const ssize_t writtenBytes = write(destFd, writeBuffer, writeBytes);
if (writtenBytes < 0)
{
if (errno == EINTR)
continue;
delete[] buffer;
*error = FileErrnoToErrorCode(errno);
return false;
}
writeBytes -= writtenBytes;
writeBuffer += writtenBytes;
}
}
delete[] buffer;
if (readBytes < 0)
{
*error = FileErrnoToErrorCode(errno);
return false;
}
IL2CPP_ASSERT(readBytes == 0);
return true;
}
utils::Expected<bool> File::Isatty(FileHandle* fileHandle)
{
return isatty(fileHandle->fd) == 1;
}
#if !IL2CPP_PLATFORM_OVERRIDES_STD_FILE_HANDLES
FileHandle* File::GetStdError()
{
static FileHandle* s_handle = NULL;
if (s_handle)
return s_handle;
s_handle = new FileHandle();
s_handle->fd = 2;
s_handle->type = kFileTypeChar;
s_handle->options = 0;
s_handle->accessMode = kFileAccessReadWrite;
s_handle->shareMode = -1; // Only used for files
return s_handle;
}
FileHandle* File::GetStdInput()
{
static FileHandle* s_handle = NULL;
if (s_handle)
return s_handle;
s_handle = new FileHandle();
s_handle->fd = 0;
s_handle->type = kFileTypeChar;
s_handle->options = 0;
s_handle->accessMode = kFileAccessRead;
s_handle->shareMode = -1; // Only used for files
return s_handle;
}
FileHandle* File::GetStdOutput()
{
static FileHandle* s_handle = NULL;
if (s_handle)
return s_handle;
s_handle = new FileHandle();
s_handle->fd = 1;
s_handle->type = kFileTypeChar;
s_handle->options = 0;
s_handle->accessMode = kFileAccessReadWrite;
s_handle->shareMode = -1; // Only used for files
return s_handle;
}
#endif
utils::Expected<bool> File::CreatePipe(FileHandle** read_handle, FileHandle** write_handle)
{
int error;
return File::CreatePipe(read_handle, write_handle, &error);
}
utils::Expected<bool> File::CreatePipe(FileHandle** read_handle, FileHandle** write_handle, int* error)
{
int fds[2];
const int ret = pipe(fds);
if (ret == -1)
{
*error = FileErrnoToErrorCode(errno);
return false;
}
FileHandle *input = new FileHandle();
input->fd = fds[0];
input->type = kFileTypePipe;
input->options = 0;
input->accessMode = kFileAccessRead;
input->shareMode = -1; // Only used for files
FileHandle *output = new FileHandle();
output->fd = fds[1];
output->type = kFileTypePipe;
output->options = 0;
output->accessMode = kFileAccessReadWrite;
output->shareMode = -1; // Only used for files
*read_handle = input;
*write_handle = output;
return true;
}
UnityPalFileAttributes File::GetFileAttributes(const std::string& path, int *error)
{
struct stat pathStat, linkStat;
*error = GetStatAndLinkStat(path, pathStat, linkStat);
if (*error != kErrorCodeSuccess)
return INVALID_FILE_ATTRIBUTES;
return StatToFileAttribute(path, pathStat, &linkStat);
}
bool File::SetFileAttributes(const std::string& path, UnityPalFileAttributes attributes, int* error)
{
struct stat pathStat;
int ret = stat(path.c_str(), &pathStat);
if (ret != 0)
{
*error = PathErrnoToErrorCode(path, errno);
return false;
}
if (attributes & kFileAttributeReadOnly)
ret = chmod(path.c_str(), pathStat.st_mode & ~(S_IWUSR | S_IWOTH | S_IWGRP));
else
ret = chmod(path.c_str(), pathStat.st_mode | S_IWUSR);
if (ret != 0)
{
*error = PathErrnoToErrorCode(path, errno);
return false;
}
// Mono ignores all other attributes
if (attributes & kFileAttributeInternalMonoExecutable)
{
mode_t exec_mask = 0;
if ((pathStat.st_mode & S_IRUSR) != 0)
exec_mask |= S_IXUSR;
if ((pathStat.st_mode & S_IRGRP) != 0)
exec_mask |= S_IXGRP;
if ((pathStat.st_mode & S_IROTH) != 0)
exec_mask |= S_IXOTH;
ret = chmod(path.c_str(), pathStat.st_mode | exec_mask);
if (ret != 0)
{
*error = PathErrnoToErrorCode(path, errno);
return false;
}
}
return true;
}
bool File::GetFileStat(const std::string& path, il2cpp::os::FileStat * stat, int* error)
{
struct stat pathStat, linkStat;
*error = GetStatAndLinkStat(path, pathStat, linkStat);
if (*error != kErrorCodeSuccess)
return false;
const std::string filename(il2cpp::utils::PathUtils::Basename(path));
const time_t creationTime = pathStat.st_mtime < pathStat.st_ctime ? pathStat.st_mtime : pathStat.st_ctime;
stat->name = filename;
stat->attributes = StatToFileAttribute(path, pathStat, &linkStat);
stat->length = (stat->attributes & kFileAttributeDirectory) > 0 ? 0 : pathStat.st_size;
stat->creation_time = TimeToTicks(creationTime);
stat->last_access_time = TimeToTicks(pathStat.st_atime);
stat->last_write_time = TimeToTicks(pathStat.st_mtime);
return true;
}
FileType File::GetFileType(FileHandle* handle)
{
return ((FileHandle*)handle)->type;
}
bool File::DeleteFile(const std::string& path, int *error)
{
const UnityPalFileAttributes attributes = GetFileAttributes(path, error);
if (*error != kErrorCodeSuccess)
{
return false;
}
if (attributes & kFileAttributeReadOnly)
{
*error = kErrorCodeAccessDenied;
return false;
}
const int ret = unlink(path.c_str());
if (ret == -1)
{
*error = PathErrnoToErrorCode(path, errno);
return false;
}
*error = kErrorCodeSuccess;
return true;
}
bool File::CopyFile(const std::string& src, const std::string& dest, bool overwrite, int* error)
{
const int srcFd = open(src.c_str(), O_RDONLY, 0);
if (srcFd < 0)
{
*error = PathErrnoToErrorCode(src, errno);
return false;
}
struct stat srcStat;
if (fstat(srcFd, &srcStat) < 0)
{
*error = FileErrnoToErrorCode(errno);
close(srcFd);
return false;
}
int destFd;
if (!overwrite)
{
destFd = open(dest.c_str(), O_WRONLY | O_CREAT | O_EXCL, srcStat.st_mode);
}
else
{
destFd = open(dest.c_str(), O_WRONLY | O_TRUNC, srcStat.st_mode);
if (destFd < 0)
destFd = open(dest.c_str(), O_WRONLY | O_CREAT | O_TRUNC, srcStat.st_mode);
else
*error = kErrorCodeAlreadyExists; // Apparently this error is set if we overwrite the dest file
}
if (destFd < 0)
{
*error = FileErrnoToErrorCode(errno);
close(srcFd);
return false;
}
const bool ret = InternalCopyFile(srcFd, destFd, srcStat, error);
close(srcFd);
close(destFd);
return ret;
}
bool File::MoveFile(const std::string& src, const std::string& dest, int* error)
{
struct stat srcStat, destStat;
if (stat(src.c_str(), &srcStat) < 0)
{
*error = PathErrnoToErrorCode(src.c_str(), errno);
return false;
}
// In C# land we check for the existence of src, but not for dest.
// We check it here and return the failure if dest exists and is not
// the same file as src.
if (stat(dest.c_str(), &destStat) == 0) // dest exists
{
if (destStat.st_dev != srcStat.st_dev || destStat.st_ino != srcStat.st_ino)
{
*error = kErrorCodeAlreadyExists;
return false;
}
}
if (!ShareAllowOpen(srcStat, kFileShareNone, kFileAccessWrite))
{
*error = kErrorCodeSuccess;
return false;
}
const int ret = rename(src.c_str(), dest.c_str());
if (ret == -1)
{
if (errno == EEXIST)
{
*error = kErrorCodeAlreadyExists;
return false;
}
else if (errno == EXDEV)
{
if (S_ISDIR(srcStat.st_mode))
{
*error = kErrorCodeNotSameDevice;
return false;
}
if (!CopyFile(src, dest, true, error))
{
// CopyFile sets the error
return false;
}
return DeleteFile(src, error); // DeleteFile sets the error
}
else
{
*error = PathErrnoToErrorCode(src.c_str(), errno);
return false;
}
}
*error = kErrorCodeSuccess;
return true;
}
bool File::ReplaceFile(const std::string& sourceFileName, const std::string& destinationFileName, const std::string& destinationBackupFileName, bool ignoreMetadataErrors, int* error)
{
const bool backupFile = !destinationBackupFileName.empty();
// Open the backup file for read so we can restore the file if an error occurs.
const int backupFd = backupFile ? open(destinationBackupFileName.c_str(), O_RDONLY, 0) : -1;
// dest -> backup
if (backupFile)
{
const int retDest = rename(destinationFileName.c_str(), destinationBackupFileName.c_str());
if (retDest == -1)
{
if (backupFd != -1)
close(backupFd);
*error = PathErrnoToErrorCode(destinationFileName.c_str(), errno);
return false;
}
}
// source -> dest
const int restSource = rename(sourceFileName.c_str(), destinationFileName.c_str());
if (restSource == -1)
{
// backup -> dest
if (backupFile)
rename(destinationBackupFileName.c_str(), destinationFileName.c_str());
// Copy backup data -> dest
struct stat backupStat;
if (backupFd != -1 && fstat(backupFd, &backupStat) == 0)
{
const int destFd = open(destinationBackupFileName.c_str(), O_WRONLY | O_CREAT | O_TRUNC, backupStat.st_mode);
if (destFd != -1)
{
int unusedCopyFileError;
InternalCopyFile(backupFd, destFd, backupStat, &unusedCopyFileError);
close(destFd);
}
}
if (backupFd != -1)
close(backupFd);
*error = PathErrnoToErrorCode(sourceFileName.c_str(), errno);
return false;
}
if (backupFd != -1)
close(backupFd);
*error = kErrorCodeSuccess;
return true;
}
static int ConvertFlags(int fileaccess, int createmode)
{
int flags;
switch (fileaccess)
{
case kFileAccessRead:
flags = O_RDONLY;
break;
case kFileAccessWrite:
flags = O_WRONLY;
break;
case kFileAccessReadWrite:
flags = O_RDWR;
break;
default:
flags = 0;
break;
}
switch (createmode)
{
case kFileModeCreateNew:
flags |= O_CREAT | O_EXCL;
break;
case kFileModeCreate:
flags |= O_CREAT | O_TRUNC;
break;
case kFileModeOpen:
break;
case kFileModeOpenOrCreate:
case kFileModeAppend:
flags |= O_CREAT;
break;
case kFileModeTruncate:
flags |= O_TRUNC;
break;
default:
flags = 0;
break;
}
return flags;
}
#ifndef S_ISFIFO
#define S_ISFIFO(m) ((m & S_IFIFO) != 0)
#endif
FileHandle* File::Open(const std::string& path, int mode, int accessMode, int shareMode, int options, int *error)
{
const int flags = ConvertFlags(accessMode, mode);
/* we don't use sharemode, because that relates to sharing of
* the file when the file is open and is already handled by
* other code, perms instead are the on-disk permissions and
* this is a sane default.
*/
const mode_t perms = options & kFileOptionsTemporary ? 0600 : 0666;
int fd = open(path.c_str(), flags, perms);
/* If we were trying to open a directory with write permissions
* (e.g. O_WRONLY or O_RDWR), this call will fail with
* EISDIR. However, this is a bit bogus because calls to
* manipulate the directory (e.g. SetFileTime) will still work on
* the directory because they use other API calls
* (e.g. utime()). Hence, if we failed with the EISDIR error, try
* to open the directory again without write permission.
*/
// Try again but don't try to make it writable
if (fd == -1)
{
if (errno == EISDIR)
{
fd = open(path.c_str(), flags & ~(O_RDWR | O_WRONLY), perms);
if (fd == -1)
{
*error = PathErrnoToErrorCode(path, errno);
return INVALID_FILE_HANDLE;
}
}
else
{
*error = PathErrnoToErrorCode(path, errno);
return INVALID_FILE_HANDLE;
}
}
struct stat statbuf;
const int ret = fstat(fd, &statbuf);
if (ret == -1)
{
*error = FileErrnoToErrorCode(errno);
close(fd);
return INVALID_FILE_HANDLE;
}
if (!ShareAllowOpen(statbuf, shareMode, accessMode))
{
*error = kErrorCodeSharingViolation;
close(fd);
return INVALID_FILE_HANDLE;
}
FileHandle* fileHandle = new FileHandle();
fileHandle->fd = fd;
fileHandle->path = path;
fileHandle->options = options;
fileHandle->accessMode = accessMode;
fileHandle->shareMode = shareMode;
fileHandle->device = statbuf.st_dev;
fileHandle->inode = statbuf.st_ino;
// Add to linked list
AddFileHandle(fileHandle);
#ifdef HAVE_POSIX_FADVISE
if (options & kFileOptionsSequentialScan)
posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);
if (options & kFileOptionsRandomAccess)
posix_fadvise(fd, 0, 0, POSIX_FADV_RANDOM);
#endif
if (S_ISFIFO(statbuf.st_mode))
fileHandle->type = kFileTypePipe;
else if (S_ISCHR(statbuf.st_mode))
fileHandle->type = kFileTypeChar;
else
fileHandle->type = kFileTypeDisk;
*error = kErrorCodeSuccess;
return fileHandle;
}
bool File::Close(FileHandle* handle, int *error)
{
if (handle->type == kFileTypeDisk && handle->options & kFileOptionsDeleteOnClose)
unlink(handle->path.c_str());
close(handle->fd);
// Remove from linked list
RemoveFileHandle(handle);
delete handle;
*error = kErrorCodeSuccess;
return true;
}
bool File::SetFileTime(FileHandle* handle, int64_t creation_time, int64_t last_access_time, int64_t last_write_time, int* error)
{
if ((handle->accessMode & kFileAccessWrite) == 0)
{
*error = kErrorCodeAccessDenied;
return false;
}
struct stat statbuf;
const int ret = fstat(handle->fd, &statbuf);
if (ret == -1)
{
*error = kErrorCodeInvalidParameter;
return false;
}
struct utimbuf utbuf;
// Setting creation time is not implemented in Mono and not supported by utime.
if (last_access_time >= 0)
{
if (last_access_time < TIME_ZERO)
{
*error = kErrorCodeInvalidParameter;
return false;
}
utbuf.actime = TicksToTime(last_access_time);
}
else
{
utbuf.actime = statbuf.st_atime;
}
if (last_write_time >= 0)
{
if (last_write_time < TIME_ZERO)
{
*error = kErrorCodeInvalidParameter;
return false;
}
utbuf.modtime = TicksToTime(last_write_time);
}
else
{
utbuf.modtime = statbuf.st_mtime;
}
const int utimeRet = utime(handle->path.c_str(), &utbuf);
if (utimeRet == -1)
{
*error = kErrorCodeInvalidParameter;
return false;
}
*error = kErrorCodeSuccess;
return true;
}
int64_t File::GetLength(FileHandle* handle, int *error)
{
if (handle->type != kFileTypeDisk)
{
*error = kErrorCodeInvalidHandle;
return false;
}
struct stat statbuf;
const int ret = fstat(handle->fd, &statbuf);
if (ret == -1)
{
*error = FileErrnoToErrorCode(errno);
return -1;
}
*error = kErrorCodeSuccess;
return statbuf.st_size;
}
bool File::SetLength(FileHandle* handle, int64_t length, int *error)
{
if (handle->type != kFileTypeDisk)
{
*error = kErrorCodeInvalidHandle;
return false;
}
// Save current position
const off_t currentPosition = lseek(handle->fd, 0, SEEK_CUR);
if (currentPosition == -1)
{
*error = FileErrnoToErrorCode(errno);
return false;
}
const off_t setLength = lseek(handle->fd, length, SEEK_SET);
if (setLength == -1)
{
*error = FileErrnoToErrorCode(errno);
return false;
}
int ret = 0;
do
{
ret = ftruncate(handle->fd, length);
}
while (ret == -1 && errno == EINTR);
if (ret == -1)
{
*error = FileErrnoToErrorCode(errno);
return false;
}
const off_t oldPosition = lseek(handle->fd, currentPosition, SEEK_SET);
if (oldPosition == -1)
{
*error = FileErrnoToErrorCode(errno);
return false;
}
*error = kErrorCodeSuccess;
return true;
}
#if !IL2CPP_USE_GENERIC_FILE
bool File::Truncate(FileHandle* handle, int *error)
{
off_t currentPosition = lseek(handle->fd, (off_t)0, SEEK_CUR);
int32_t ret = 0;
*error = kErrorCodeSuccess;
if (currentPosition == -1)
{
*error = FileErrnoToErrorCode(errno);
return false;
}
do
{
ret = ftruncate(handle->fd, currentPosition);
}
while (ret == -1 && errno == EINTR);
if (ret == -1)
{
*error = FileErrnoToErrorCode(errno);
return false;
}
return true;
}
#endif // IL2CPP_USE_GENERIC_FILE
int64_t File::Seek(FileHandle* handle, int64_t offset, int origin, int *error)
{
if (handle->type != kFileTypeDisk)
{
*error = kErrorCodeInvalidHandle;
return false;
}
int whence;
switch (origin)
{
case kFileSeekOriginBegin:
whence = SEEK_SET;
break;
case kFileSeekOriginCurrent:
whence = SEEK_CUR;
break;
case kFileSeekOriginEnd:
whence = SEEK_END;
break;
default:
{
*error = kErrorCodeInvalidParameter;
return -1;
}
}
const off_t position = lseek(handle->fd, offset, whence);
if (position == -1)
{
*error = FileErrnoToErrorCode(errno);
return -1;
}
*error = kErrorCodeSuccess;
return position;
}
int File::Read(FileHandle* handle, char *dest, int count, int *error)
{
if (handle == NULL || handle == INVALID_FILE_HANDLE)
{
*error = kErrorCodeInvalidHandle;
return 0;
}
if ((handle->accessMode & kFileAccessRead) == 0)
{
*error = kErrorCodeAccessDenied;
return 0;
}
int ret;
do
{
ret = (int)read(handle->fd, dest, count);
}
while (ret == -1 && errno == EINTR);
if (ret == -1)
{
*error = FileErrnoToErrorCode(errno);
return 0;
}
return ret;
}
int32_t File::Write(FileHandle* handle, const char* buffer, int count, int *error)
{
if ((handle->accessMode & kFileAccessWrite) == 0)
{
*error = kErrorCodeAccessDenied;
return -1;
}
int ret;
do
{
ret = (int32_t)write(handle->fd, buffer, count);
}
while (ret == -1 && errno == EINTR);
if (ret == -1)
{
*error = FileErrnoToErrorCode(errno);
return -1;
}
#if IL2CPP_SUPPORTS_CONSOLE_EXTENSION
if (handle == GetStdOutput() || handle == GetStdError())
os::ConsoleExtension::Write(buffer);
#endif
return ret;
}
bool File::Flush(FileHandle* handle, int* error)
{
if (handle->type != kFileTypeDisk)
{
*error = kErrorCodeInvalidHandle;
return false;
}
const int ret = fsync(handle->fd);
if (ret == -1)
{
*error = FileErrnoToErrorCode(errno);
return false;
}
*error = kErrorCodeSuccess;
return true;
}
void File::Lock(FileHandle* handle, int64_t position, int64_t length, int* error)
{
struct flock lock_data;
int ret;
lock_data.l_type = F_WRLCK;
lock_data.l_whence = SEEK_SET;
lock_data.l_start = position;
lock_data.l_len = length;
do
{
ret = fcntl(handle->fd, F_SETLK, &lock_data);
}
while (ret == -1 && errno == EINTR);
if (ret == -1)
{
/*
* if locks are not available (NFS for example),
* ignore the error
*/
if (errno == ENOLCK
#ifdef EOPNOTSUPP
|| errno == EOPNOTSUPP
#endif
#ifdef ENOTSUP
|| errno == ENOTSUP
#endif
)
{
*error = kErrorCodeSuccess;
return;
}
*error = FileErrnoToErrorCode(errno);
return;
}
*error = kErrorCodeSuccess;
}
void File::Unlock(FileHandle* handle, int64_t position, int64_t length, int* error)
{
struct flock lock_data;
int ret;
lock_data.l_type = F_UNLCK;
lock_data.l_whence = SEEK_SET;
lock_data.l_start = position;
lock_data.l_len = length;
do
{
ret = fcntl(handle->fd, F_SETLK, &lock_data);
}
while (ret == -1 && errno == EINTR);
if (ret == -1)
{
/*
* if locks are not available (NFS for example),
* ignore the error
*/
if (errno == ENOLCK
#ifdef EOPNOTSUPP
|| errno == EOPNOTSUPP
#endif
#ifdef ENOTSUP
|| errno == ENOTSUP
#endif
)
{
*error = kErrorCodeSuccess;
return;
}
*error = FileErrnoToErrorCode(errno);
return;
}
*error = kErrorCodeSuccess;
}
utils::Expected<bool> File::DuplicateHandle(FileHandle* source_process_handle, FileHandle* source_handle, FileHandle* target_process_handle,
FileHandle** target_handle, int access, int inhert, int options, int* error)
{
return utils::Il2CppError(utils::NotSupported, "This platform does not support file handle duplication.");
}
utils::Expected<bool> File::IsExecutable(const std::string& path)
{
#if IL2CPP_CAN_CHECK_EXECUTABLE
return access(path.c_str(), X_OK) == 0;
#else
return utils::Il2CppError(utils::NotSupported, "This platform cannot check for executable permissions.");
#endif
}
bool File::Cancel(FileHandle* handle)
{
return false;
}
}
}
#endif