/*** Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: FileAdvisory.Unix.cpp Date: 2022-1-29 Author: Reece ***/ #include #include "FS.hpp" #include "FileAdvisory.hpp" #include "FileAdvisory.Unix.hpp" #include #if defined(AURORA_COMPILER_CLANG) // warning: enumeration values 'kEnumCount' and 'kEnumInvalid' not handled in switch [-Wswitch #pragma clang diagnostic ignored "-Wswitch" // Yea, I don't give a shit. #endif namespace Aurora::IO::FS { bool ApplyDumbAdvisoryLock(int fd, EFileAdvisoryLockLevel level) { #if !defined(AURORA_IS_LINUX_DERIVED) int operation = LOCK_NB; switch (level) { case EFileAdvisoryLockLevel::eEnumCount: case EFileAdvisoryLockLevel::eNoSafety: return true; case EFileAdvisoryLockLevel::eBlockWrite: operation |= LOCK_SH; break; case EFileAdvisoryLockLevel::eBlockReadWrite: operation |= LOCK_EX; break; } // Assume: // "Furthermore, the lock is release either by an explicit LOCK_UN // operation on any of these duplicate file descriptors, or when // all such file descriptors have been closed" return ::flock(fd, operation) == 0; #else // Linux int lease {}; switch (level) { case EFileAdvisoryLockLevel::eEnumCount: case EFileAdvisoryLockLevel::eNoSafety: return true; case EFileAdvisoryLockLevel::eBlockWrite: lease = F_RDLCK; break; case EFileAdvisoryLockLevel::eBlockReadWrite: lease = F_WRLCK; break; } // Ignore warning bc idc // I assume c++ compilers under posix land allow for struct init the c way struct flock lck = { .l_whence = SEEK_SET, .l_start = 0, .l_len = 0, .l_type = lease }; // Assume this is true: // * If a process closes any file descriptor referring to a file, // then all of the process's locks on that file are released, // regardless of the file descriptor(s) on which the locks were // obtained. This is bad: it means that a process can lose its // locks on a file such as /etc/passwd or /etc/mtab when for some // reason a library function decides to open, read, and close the // same file. // [...] // // Open file description locks solve both of these problems. // // OFD's description implies it inherits non-ofd behaviour // I wanted to confirm a l_len zero would work to lock all contents, // even on expansion of the file. The docs didn't mention anything; // however, I don't really see any real difference len processing in // the kernel. Furthermore, I saw the kernels implementation checking // for MAX_OFFSET of int type (-1) before overloading l_len with zero. // // I assume this will work. Linux locking is trash. return ::fcntl(fd, F_OFD_SETLK, &lck) == 0; #endif } bool ApplyFileSectionLock(AuSPtr pHandle, EFileAdvisoryLockLevel level, AuUInt64 uOffset, AuUInt64 uLength) { int lease {}; auto opt = pHandle->GetOSHandleSafe(); if (!opt) { SysPushErrorIOResourceRejected(); return false; } int fd = (int)opt.ValueOr((int)-1); if (fd == -1) { SysPushErrorIOResourceRejected(); return false; } switch (level) { case EFileAdvisoryLockLevel::eEnumCount: case EFileAdvisoryLockLevel::eNoSafety: return true; case EFileAdvisoryLockLevel::eBlockWrite: lease = F_RDLCK; break; case EFileAdvisoryLockLevel::eBlockReadWrite: lease = F_WRLCK; break; } struct flock lck = { .l_whence = SEEK_SET, .l_start = uLength, .l_len = uOffset, .l_type = lease }; #if defined(AURORA_IS_LINUX_DERIVED) return ::fcntl(fd, F_OFD_SETLK, &lck) == 0; #else return ::fcntl(fd, F_SETLK, &lck) == 0; #endif } bool UnapplyFileSectionLock(AuSPtr pHandle, AuUInt64 uOffset, AuUInt64 uLength) { auto opt = pHandle->GetOSHandleSafe(); if (!opt) { SysPushErrorIOResourceRejected(); return false; } int fd = (int)opt.ValueOr((int)-1); if (fd == -1) { SysPushErrorIOResourceRejected(); return false; } struct flock lck = { .l_whence = SEEK_SET, .l_start = uLength, .l_len = uOffset, .l_type = F_UNLCK }; #if defined(AURORA_IS_LINUX_DERIVED) return ::fcntl(fd, F_OFD_SETLK, &lck) == 0; #else return ::fcntl(fd, F_SETLK, &lck) == 0; #endif } }