Merge pull request 'Merge features/fs' (#6) from features/fs into master
Reviewed-on: PallasDev/DrangPlatform#6
This commit is contained in:
commit
27190311c0
16 changed files with 2078 additions and 7 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -11,4 +11,6 @@ build-*/
|
|||
CMakeLists.txt.user
|
||||
|
||||
# CMake
|
||||
CMakeUserPresets.json
|
||||
CMakeUserPresets.json
|
||||
|
||||
Playground/
|
||||
|
|
@ -33,3 +33,7 @@ set(MI_BUILD_OBJECT OFF CACHE BOOL "" FORCE)
|
|||
FetchContent_MakeAvailable(mimalloc)
|
||||
|
||||
add_subdirectory(Source/DrangPlatform)
|
||||
|
||||
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/Playground)
|
||||
add_subdirectory(Playground)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -147,6 +147,25 @@ DRANG_PLATFORM_API void drang_free(void *ptr);
|
|||
*/
|
||||
#define DRANG_ALLOC_T_N(_Type_, _Num_) ((_Type_ *) drang_alloc_aligned(sizeof(_Type_) * (_Num_), alignof(_Type_)))
|
||||
|
||||
/**
|
||||
* @brief Allocates zero-initialized memory for a single object of the specified type with proper alignment.
|
||||
*
|
||||
* @param _Type_ The type to allocate memory for.
|
||||
* @return Pointer to allocated zero-initialized memory cast to the appropriate type.
|
||||
* @remarks Automatically calculates total size and uses proper alignment for the type.
|
||||
*/
|
||||
#define DRANG_CALLOC_T(_Type_) ((_Type_ *) drang_calloc_aligned(1, sizeof(_Type_), alignof(_Type_)))
|
||||
|
||||
/**
|
||||
* @brief Allocates zero-initialized memory for an array of objects of the specified type with proper alignment.
|
||||
*
|
||||
* @param _Type_ The type to allocate memory for.
|
||||
* @param _Num_ Number of objects to allocate.
|
||||
* @return Pointer to allocated zero-initialized memory cast to the appropriate type.
|
||||
* @remarks Automatically calculates total size and uses proper alignment for the type.
|
||||
*/
|
||||
#define DRANG_CALLOC_T_N(_Type_, _Num_) ((_Type_ *) drang_calloc_aligned((_Num_), sizeof(_Type_), alignof(_Type_)))
|
||||
|
||||
/**
|
||||
* @brief Reallocates memory for an array of objects of the specified type with proper alignment.
|
||||
*
|
||||
|
|
@ -266,6 +285,24 @@ DRANG_PLATFORM_API void drang_free(void *ptr);
|
|||
*/
|
||||
#define DRANG_TRY_ALLOC_T_N(_Type_, _Num_) DRANG_ALLOC_TRY_IMPL(DRANG_ALLOC_T_N(_Type_, (_Num_)))
|
||||
|
||||
/**
|
||||
* @brief Allocates zero-initialized memory for a typed object with automatic error handling.
|
||||
*
|
||||
* @param _Type_ The type to allocate memory for.
|
||||
* @return Pointer to allocated zero-initialized memory cast to the appropriate type.
|
||||
* @remarks Combines type-safe allocation with automatic error handling. Preferred for single object allocation.
|
||||
*/
|
||||
#define DRANG_TRY_CALLOC_T(_Type_) DRANG_ALLOC_TRY_IMPL(DRANG_CALLOC_T(_Type_))
|
||||
|
||||
/**
|
||||
* @brief Allocates zero-initialized memory for a typed array with automatic error handling.
|
||||
*
|
||||
* @param _Type_ The type to allocate memory for.
|
||||
* @param _Num_ Number of objects to allocate.
|
||||
* @return Pointer to allocated zero-initialized memory cast to the appropriate type.
|
||||
*/
|
||||
#define DRANG_TRY_CALLOC_T_N(_Type_, _Num_) DRANG_ALLOC_TRY_IMPL(DRANG_CALLOC_T_N(_Type_, (_Num_)))
|
||||
|
||||
/**
|
||||
* @brief Reallocates memory for a typed array with automatic error handling.
|
||||
*
|
||||
|
|
|
|||
139
Source/DrangPlatform/Include/drang/endianness.h
Normal file
139
Source/DrangPlatform/Include/drang/endianness.h
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
#pragma once
|
||||
#include "platform.h"
|
||||
#include <stdint.h>
|
||||
#if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)
|
||||
# ifdef __has_include
|
||||
# if __has_include(<endian.h>)
|
||||
# include <endian.h> // gnu libc normally provides, linux
|
||||
# elif __has_include(<machine/endian.h>)
|
||||
# include <machine/endian.h> //open bsd, macos
|
||||
# elif __has_include(<sys/param.h>)
|
||||
# include <sys/param.h> // mingw, some bsd (not open/macos)
|
||||
# elif __has_include(<sys/isadefs.h>)
|
||||
# include <sys/isadefs.h> // solaris
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)
|
||||
# if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) \
|
||||
|| (defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN) \
|
||||
|| (defined(_BYTE_ORDER) && _BYTE_ORDER == _BIG_ENDIAN) || (defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN) \
|
||||
|| (defined(__sun) && defined(__SVR4) && defined(_BIG_ENDIAN)) || defined(__ARMEB__) || defined(__THUMBEB__) \
|
||||
|| defined(__AARCH64EB__) || defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__) || defined(_M_PPC)
|
||||
# define __BIG_ENDIAN__
|
||||
# elif (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || /* gcc */ \
|
||||
(defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN) /* linux header */ \
|
||||
|| (defined(_BYTE_ORDER) && _BYTE_ORDER == _LITTLE_ENDIAN) \
|
||||
|| (defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN) /* mingw header */ \
|
||||
|| (defined(__sun) && defined(__SVR4) && defined(_LITTLE_ENDIAN)) || /* solaris */ \
|
||||
defined(__ARMEL__) || defined(__THUMBEL__) || defined(__AARCH64EL__) || defined(_MIPSEL) || defined(__MIPSEL) \
|
||||
|| defined(__MIPSEL__) || defined(_M_IX86) || defined(_M_X64) || defined(_M_IA64) \
|
||||
|| /* msvc for intel processors */ \
|
||||
defined(_M_ARM) /* msvc code on arm executes in little endian mode */
|
||||
# define __LITTLE_ENDIAN__
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)
|
||||
# error "Unknown endianness"
|
||||
#endif
|
||||
|
||||
#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || defined(__clang__)
|
||||
# define DRANG_BSWAP8(_X_) (_X_)
|
||||
# define DRANG_BSWAP16(_X_) (__builtin_bswap16(_X_))
|
||||
# define DRANG_BSWAP32(_X_) (__builtin_bswap32(_X_))
|
||||
# define DRANG_BSWAP64(_X_) (__builtin_bswap64(_X_))
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
# include <stdlib.h>
|
||||
# define DRANG_BSWAP8(_X_) (_X_)
|
||||
# define DRANG_BSWAP16(_X_) (_byteswap_ushort(_X_))
|
||||
# define DRANG_BSWAP32(_X_) (_byteswap_ulong(_X_))
|
||||
# define DRANG_BSWAP64(_X_) (_byteswap_uint64(_X_))
|
||||
#else
|
||||
static inline uint16 drang_bswap16_impl(const uint16_t x)
|
||||
{
|
||||
return (((x >> 8) & 0xFFu) | ((x & 0xFFu) << 8));
|
||||
}
|
||||
static inline uint32_t drang_bswap32_impl(const uint32_t x)
|
||||
{
|
||||
return (((x & 0xFF000000u) >> 24) | ((x & 0x00FF0000u) >> 8) | ((x & 0x0000FF00u) << 8) | ((x & 0x000000FFu) << 24));
|
||||
}
|
||||
|
||||
static inline uint64_t drang_bswap64_impl(const uint64_t x)
|
||||
{
|
||||
return (((x & 0xFF00000000000000ull) >> 56) | ((x & 0x00FF000000000000ull) >> 40)
|
||||
| ((x & 0x0000FF0000000000ull) >> 24) | ((x & 0x000000FF00000000ull) >> 8)
|
||||
| ((x & 0x00000000FF000000ull) << 8) | ((x & 0x0000000000FF0000ull) << 24)
|
||||
| ((x & 0x000000000000FF00ull) << 40) | ((x & 0x00000000000000FFull) << 56));
|
||||
}
|
||||
# define DRANG_BSWAP16(_X_) (drang_bswap16_impl(_X_))
|
||||
# define DRANG_BSWAP32(_X_) (drang_bswap32_impl(_X_))
|
||||
# define DRANG_BSWAP64(_X_) (drang_bswap64_impl(_X_))
|
||||
#endif
|
||||
|
||||
#define DRANG_BSWAPF(_X_) DRANG_BIT_CAST(DRANG_BSWAP32(DRANG_BIT_CAST(_X_, float, uint32_t)), uint32_t, float)
|
||||
#define DRANG_BSWAPD(_X_) DRANG_BIT_CAST(DRANG_BSWAP64(DRANG_BIT_CAST(_X_, double, uint64_t)), uint64_t, double)
|
||||
|
||||
#if defined(__LITTLE_ENDIAN__)
|
||||
# define DRANG_NE_TO_LE8(_X_) (_X_)
|
||||
# define DRANG_NE_TO_LE16(_X_) (_X_)
|
||||
# define DRANG_NE_TO_LE32(_X_) (_X_)
|
||||
# define DRANG_NE_TO_LE64(_X_) (_X_)
|
||||
# define DRANG_NE_TO_LEF(_X_) (_X_)
|
||||
# define DRANG_NE_TO_LED(_X_) (_X_)
|
||||
|
||||
# define DRANG_LE_TO_NE8(_X_) (_X_)
|
||||
# define DRANG_LE_TO_NE16(_X_) (_X_)
|
||||
# define DRANG_LE_TO_NE32(_X_) (_X_)
|
||||
# define DRANG_LE_TO_NE64(_X_) (_X_)
|
||||
# define DRANG_LE_TO_NEF(_X_) (_X_)
|
||||
# define DRANG_LE_TO_NED(_X_) (_X_)
|
||||
|
||||
# define DRANG_NE_TO_BE8(_X_) DRANG_BSWAP8(_X_)
|
||||
# define DRANG_NE_TO_BE16(_X_) DRANG_BSWAP16(_X_)
|
||||
# define DRANG_NE_TO_BE32(_X_) DRANG_BSWAP32(_X_)
|
||||
# define DRANG_NE_TO_BE64(_X_) DRANG_BSWAP64(_X_)
|
||||
# define DRANG_NE_TO_BEF(_X_) DRANG_BSWAPF(_X_)
|
||||
# define DRANG_NE_TO_BED(_X_) DRANG_BSWAPD(_X_
|
||||
|
||||
# define DRANG_BE_TO_NE8(_X_) DRANG_BSWAP8(_X_)
|
||||
# define DRANG_BE_TO_NE16(_X_) DRANG_BSWAP16(_X_)
|
||||
# define DRANG_BE_TO_NE32(_X_) DRANG_BSWAP32(_X_)
|
||||
# define DRANG_BE_TO_NE64(_X_) DRANG_BSWAP64(_X_)
|
||||
# define DRANG_BE_TO_NEF(_X_) DRANG_BSWAPF(_X_)
|
||||
# define DRANG_BE_TO_NED(_X_) DRANG_BSWAPD(_X_
|
||||
|
||||
#elif defined(__BIG_ENDIAN__)
|
||||
# define DRANG_NE_TO_LE8(_X_) DRANG_BSWAP8(_X_)
|
||||
# define DRANG_NE_TO_LE16(_X_) DRANG_BSWAP16(_X_)
|
||||
# define DRANG_NE_TO_LE32(_X_) DRANG_BSWAP32(_X_)
|
||||
# define DRANG_NE_TO_LE64(_X_) DRANG_BSWAP64(_X_)
|
||||
# define DRANG_NE_TO_LEF(_X_) DRANG_BSWAPF(_X_)
|
||||
# define DRANG_NE_TO_LED(_X_) DRANG_BSWAPD(_X_)
|
||||
|
||||
# define DRANG_LE_TO_NE8(_X_) DRANG_BSWAP8(_X_)
|
||||
# define DRANG_LE_TO_NE16(_X_) DRANG_BSWAP16(_X_)
|
||||
# define DRANG_LE_TO_NE32(_X_) DRANG_BSWAP32(_X_)
|
||||
# define DRANG_LE_TO_NE64(_X_) DRANG_BSWAP64(_X_)
|
||||
# define DRANG_LE_TO_NEF(_X_) DRANG_BSWAPF(_X_)
|
||||
# define DRANG_LE_TO_NED(_X_) DRANG_BSWAPD(_X_
|
||||
|
||||
# define DRANG_NE_TO_BE8(_X_) (_X_)
|
||||
# define DRANG_NE_TO_BE16(_X_) (_X_)
|
||||
# define DRANG_NE_TO_BE32(_X_) (_X_)
|
||||
# define DRANG_NE_TO_BE64(_X_) (_X_)
|
||||
# define DRANG_NE_TO_BEF(_X_) (_X_)
|
||||
# define DRANG_NE_TO_BED(_X_) (_X_)
|
||||
|
||||
# define DRANG_BE_TO_NE8(_X_) (_X_)
|
||||
# define DRANG_BE_TO_NE16(_X_) (_X_)
|
||||
# define DRANG_BE_TO_NE32(_X_) (_X_)
|
||||
# define DRANG_BE_TO_NE64(_X_) (_X_)
|
||||
# define DRANG_BE_TO_NEF(_X_) (_X_)
|
||||
# define DRANG_BE_TO_NED(_X_) (_X_)
|
||||
|
||||
#else
|
||||
# error "Unknown endianness"
|
||||
#endif
|
||||
|
|
@ -21,6 +21,9 @@ DRANG_PLATFORM_API const char *drang_error_str(int err);
|
|||
#define DRANG_EPERM (-8) // Operation not permitted
|
||||
#define DRANG_ETIMEDOUT (-9) // Connection timed out
|
||||
#define DRANG_EBUSY (-10) // Device or resource busy
|
||||
#define DRANG_EOVERFLOW (-11) // Value too large to be stored in data type
|
||||
#define DRANG_ENOSPC (-12) // No space left on device
|
||||
#define DRANG_EROFS (-13) // Read-only file system
|
||||
#define DRANG_EPLATFORM (-1000) // An unknown platform-specific error occurred
|
||||
|
||||
/**
|
||||
|
|
|
|||
543
Source/DrangPlatform/Include/drang/fs.h
Normal file
543
Source/DrangPlatform/Include/drang/fs.h
Normal file
|
|
@ -0,0 +1,543 @@
|
|||
#pragma once
|
||||
#include "platform.h"
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
DRANG_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* @brief File open mode flags for controlling file access behavior.
|
||||
*
|
||||
* These flags can be combined using bitwise OR operations to specify
|
||||
* the desired file access mode and creation behavior.
|
||||
*
|
||||
* @remarks Flags can be combined, for example:
|
||||
* drang_fs_mode_read | drang_fs_mode_write for read-write access.
|
||||
* Some combinations may not be valid on all platforms.
|
||||
*/
|
||||
typedef enum {
|
||||
drang_fs_mode_read = 0x01, /**< Open file for reading */
|
||||
drang_fs_mode_write = 0x02, /**< Open file for writing */
|
||||
drang_fs_mode_append = 0x04, /**< Open file for appending (writes go to end) */
|
||||
drang_fs_mode_create = 0x08, /**< Create file if it doesn't exist */
|
||||
drang_fs_mode_truncate = 0x10, /**< Truncate file to zero length if it exists */
|
||||
drang_fs_mode_exclusive = 0x20 /**< Fail if file already exists (used with create) */
|
||||
} drang_fs_mode_t;
|
||||
|
||||
/**
|
||||
* @brief Origin points for seek operations.
|
||||
*
|
||||
* Specifies the reference point for file positioning operations.
|
||||
*/
|
||||
typedef enum {
|
||||
drang_seek_origin_begin = 0, /**< Seek from beginning of file */
|
||||
drang_seek_origin_current = 1, /**< Seek from current file position */
|
||||
drang_seek_origin_end = 2 /**< Seek from end of file */
|
||||
} drang_seek_origin_t;
|
||||
|
||||
/**
|
||||
* @brief File system entry type classification.
|
||||
*
|
||||
* Used to identify the type of file system objects when querying
|
||||
* file information or reading directory entries.
|
||||
*/
|
||||
typedef enum {
|
||||
drang_fs_type_unknown = 0, /**< Unknown or unsupported file type */
|
||||
drang_fs_type_file = 1, /**< Regular file */
|
||||
drang_fs_type_directory = 2, /**< Directory */
|
||||
drang_fs_type_symlink = 3 /**< Symbolic link */
|
||||
} drang_fs_type_t;
|
||||
|
||||
/**
|
||||
* @brief File system permissions type.
|
||||
*
|
||||
* Represents file permissions using Unix-style permission bits.
|
||||
* Use the DRANG_FS_PERM_* constants to construct permission values.
|
||||
*/
|
||||
typedef uint32_t drang_fs_permissions_t;
|
||||
|
||||
/**
|
||||
* @brief File system timestamp type.
|
||||
*
|
||||
* Represents 64-bits of time in seconds since the Unix epoch (1970-01-01 00:00:00 UTC).
|
||||
*/
|
||||
typedef uint64_t drang_fs_time_t;
|
||||
|
||||
/**
|
||||
* @name File Permission Constants
|
||||
* @brief Unix-style file permission bit flags.
|
||||
*
|
||||
* These constants can be combined using bitwise OR to create
|
||||
* permission specifications for files and directories.
|
||||
* @{
|
||||
*/
|
||||
#define DRANG_FS_PERM_USER_READ 0x0400 /**< User read permission */
|
||||
#define DRANG_FS_PERM_USER_WRITE 0x0200 /**< User write permission */
|
||||
#define DRANG_FS_PERM_USER_EXEC 0x0100 /**< User execute permission */
|
||||
#define DRANG_FS_PERM_GROUP_READ 0x0040 /**< Group read permission */
|
||||
#define DRANG_FS_PERM_GROUP_WRITE 0x0020 /**< Group write permission */
|
||||
#define DRANG_FS_PERM_GROUP_EXEC 0x0010 /**< Group execute permission */
|
||||
#define DRANG_FS_PERM_OTHER_READ 0x0004 /**< Other read permission */
|
||||
#define DRANG_FS_PERM_OTHER_WRITE 0x0002 /**< Other write permission */
|
||||
#define DRANG_FS_PERM_OTHER_EXEC 0x0001 /**< Other execute permission */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Opaque structure representing an open file handle.
|
||||
*
|
||||
* This structure contains platform-specific file handle information
|
||||
* and should only be manipulated through the provided API functions.
|
||||
*/
|
||||
typedef struct drang_fs_file drang_fs_file_t;
|
||||
|
||||
/**
|
||||
* @brief Opaque structure representing an open directory handle.
|
||||
*
|
||||
* This structure contains platform-specific directory handle information
|
||||
* and should only be manipulated through the provided API functions.
|
||||
*/
|
||||
typedef struct drang_fs_directory drang_fs_directory_t;
|
||||
|
||||
/**
|
||||
* @brief File system metadata structure.
|
||||
*
|
||||
* Contains comprehensive information about a file system entry
|
||||
* including type, size, permissions, and timestamps.
|
||||
*/
|
||||
struct drang_fs_stat
|
||||
{
|
||||
drang_fs_type_t type; /**< Type of file system entry */
|
||||
uint64_t size; /**< Size in bytes (0 for directories) */
|
||||
drang_fs_permissions_t permissions; /**< File permissions */
|
||||
drang_fs_time_t created_time; /**< File creation time */
|
||||
drang_fs_time_t modified_time; /**< Last modification time */
|
||||
drang_fs_time_t accessed_time; /**< Last access time */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Maximum size for relative path names in directory entries.
|
||||
*/
|
||||
#define DRANG_FS_MAX_REL_PATH_SIZE (512)
|
||||
|
||||
/**
|
||||
* @brief Directory entry structure.
|
||||
*
|
||||
* Represents a single entry within a directory, containing the
|
||||
* entry name and type information.
|
||||
*/
|
||||
typedef struct fs_dir_entry
|
||||
{
|
||||
char name[DRANG_FS_MAX_REL_PATH_SIZE]; /**< Entry name (null-terminated) */
|
||||
drang_fs_type_t type; /**< Entry type */
|
||||
} drang_fs_dir_entry_t;
|
||||
|
||||
/**
|
||||
* @brief Gets the platform-specific path separator character.
|
||||
*
|
||||
* Returns the character used to separate path components on the current platform.
|
||||
* This is typically '/' on Unix-like systems (Linux, macOS) and '\' on Windows.
|
||||
*
|
||||
* @return Platform-specific path separator character.
|
||||
* @retval '/' On Unix-like systems (Linux, macOS, BSD, etc.).
|
||||
* @retval '\' On Windows systems.
|
||||
* @remarks This function is useful for constructing portable file paths.
|
||||
* The returned character should be used when manually building paths
|
||||
* or when parsing path strings in a platform-aware manner.
|
||||
* For portable path construction, consider using this function instead
|
||||
* of hardcoding separator characters.
|
||||
*/
|
||||
DRANG_PLATFORM_API char drang_fs_path_separator(void);
|
||||
|
||||
/**
|
||||
* @brief Opens a file with specified access mode.
|
||||
*
|
||||
* Opens or creates a file at the specified path with the given access mode.
|
||||
* The file handle must be closed with drang_fs_close() when no longer needed.
|
||||
*
|
||||
* @param[in] path Path to the file to open.
|
||||
* @param[in] mode File access mode flags (combination of drang_fs_mode_t values).
|
||||
* @param[out] out_file Pointer to store the opened file handle.
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval DRANG_EOK Success - file opened successfully.
|
||||
* @retval DRANG_EINVAL Invalid parameter (path or out_file is NULL).
|
||||
* @retval DRANG_ENOENT File not found and create flag not set.
|
||||
* @retval DRANG_EPERM Permission denied.
|
||||
* @retval DRANG_ENOMEM Insufficient memory to allocate file handle.
|
||||
* @retval DRANG_EIO I/O error occurred during file opening.
|
||||
* @retval DRANG_EBUSY File is locked or in use by another process.
|
||||
* @remarks The returned file handle must be freed with drang_fs_close().
|
||||
* File path should use platform-appropriate path separators.
|
||||
*/
|
||||
DRANG_PLATFORM_API int drang_fs_open(const char *path, drang_fs_mode_t mode, drang_fs_file_t **out_file);
|
||||
|
||||
/**
|
||||
* @brief Closes an open file handle.
|
||||
*
|
||||
* Closes the file handle and releases associated resources. Any pending
|
||||
* writes are flushed before closing.
|
||||
*
|
||||
* @param[in] file File handle to close, or NULL.
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval DRANG_EOK Success - file closed successfully.
|
||||
* @retval DRANG_EINVAL Invalid file handle.
|
||||
* @retval DRANG_EIO I/O error occurred during file closing.
|
||||
* @remarks Safe to call with NULL pointer. File handle becomes invalid after closing.
|
||||
* Pending writes are automatically flushed before closing.
|
||||
*/
|
||||
DRANG_PLATFORM_API int drang_fs_close(drang_fs_file_t *file);
|
||||
|
||||
/**
|
||||
* @brief Reads data from an open file.
|
||||
*
|
||||
* Reads up to count*size bytes from the file into the provided buffer.
|
||||
* The actual number of bytes read is returned through bytes_read.
|
||||
*
|
||||
* @param[in] file Open file handle.
|
||||
* @param[out] buffer Buffer to receive read data.
|
||||
* @param[in] count Number of elements to read.
|
||||
* @param[in] size Size of each element in bytes.
|
||||
* @param[out] bytes_read Pointer to store actual bytes read (may be NULL).
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval DRANG_EOK Success - data read successfully.
|
||||
* @retval DRANG_EINVAL Invalid parameter (file or buffer is NULL).
|
||||
* @retval DRANG_EIO I/O error occurred during reading.
|
||||
* @retval DRANG_EPERM File not opened for reading.
|
||||
* @remarks Reading beyond end of file is not an error - bytes_read will indicate
|
||||
* actual bytes read. File position advances to bytes read.
|
||||
*/
|
||||
DRANG_PLATFORM_API int drang_fs_read(drang_fs_file_t *file, void *buffer, size_t count, size_t size, size_t *bytes_read);
|
||||
|
||||
/**
|
||||
* @brief Writes data to an open file.
|
||||
*
|
||||
* Writes up to count*size bytes from the buffer to the file.
|
||||
* The actual number of bytes written is returned through bytes_written.
|
||||
*
|
||||
* @param[in] file Open file handle.
|
||||
* @param[in] buffer Buffer containing data to write.
|
||||
* @param[in] count Number of elements to write.
|
||||
* @param[in] size Size of each element in bytes.
|
||||
* @param[out] bytes_written Pointer to store actual bytes written (may be NULL).
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval DRANG_EOK Success - data written successfully.
|
||||
* @retval DRANG_EINVAL Invalid parameter (file or buffer is NULL).
|
||||
* @retval DRANG_EIO I/O error occurred during writing.
|
||||
* @retval DRANG_EPERM File not opened for writing.
|
||||
* @retval DRANG_ENOMEM Insufficient disk space.
|
||||
* @remarks Data may be buffered - use drang_fs_flush() to ensure data is written.
|
||||
* File position advances by bytes written.
|
||||
*/
|
||||
DRANG_PLATFORM_API int drang_fs_write(
|
||||
drang_fs_file_t *file, const void *buffer, size_t count, size_t size, size_t *bytes_written);
|
||||
|
||||
/**
|
||||
* @brief Sets the file position indicator.
|
||||
*
|
||||
* Changes the current file position to the specified offset relative
|
||||
* to the given origin point.
|
||||
*
|
||||
* @param[in] file Open file handle.
|
||||
* @param[in] offset Byte offset from the origin.
|
||||
* @param[in] origin Reference point for the offset.
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval DRANG_EOK Success - file position set successfully.
|
||||
* @retval DRANG_EINVAL Invalid parameter (file is NULL or invalid origin).
|
||||
* @retval DRANG_EIO I/O error occurred during seek operation.
|
||||
* @remarks Seeking beyond end of file is allowed and may extend the file on write.
|
||||
* Use drang_fs_tell() to get the current position after seeking.
|
||||
*/
|
||||
DRANG_PLATFORM_API int drang_fs_seek(drang_fs_file_t *file, int64_t offset, drang_seek_origin_t origin);
|
||||
|
||||
/**
|
||||
* @brief Gets the current file position.
|
||||
*
|
||||
* Returns the current byte offset of the file position indicator
|
||||
* from the beginning of the file.
|
||||
*
|
||||
* @param[in] file Open file handle.
|
||||
* @param[out] position Pointer to store the current file position.
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval DRANG_EOK Success - position retrieved successfully.
|
||||
* @retval DRANG_EINVAL Invalid parameter (file or position is NULL).
|
||||
* @retval DRANG_EIO I/O error occurred during position query.
|
||||
* @remarks Position is always relative to the beginning of the file.
|
||||
*/
|
||||
DRANG_PLATFORM_API int drang_fs_tell(drang_fs_file_t *file, uint64_t *position);
|
||||
|
||||
/**
|
||||
* @brief Flushes pending writes to storage.
|
||||
*
|
||||
* Forces any buffered write operations to be written to the underlying
|
||||
* storage device. Does not guarantee data persistence across system crashes.
|
||||
*
|
||||
* @param[in] file Open file handle.
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval DRANG_EOK Success - data flushed successfully.
|
||||
* @retval DRANG_EINVAL Invalid file handle.
|
||||
* @retval DRANG_EIO I/O error occurred during flush operation.
|
||||
* @remarks Does not guarantee data persistence - use platform-specific sync
|
||||
* operations for crash-safe persistence.
|
||||
*/
|
||||
DRANG_PLATFORM_API int drang_fs_flush(drang_fs_file_t *file);
|
||||
|
||||
/**
|
||||
* @brief Truncates or extends a file to specified size.
|
||||
*
|
||||
* Changes the size of the file to the specified length. If the file
|
||||
* is extended, the new area is filled with zeros.
|
||||
*
|
||||
* @param[in] file Open file handle.
|
||||
* @param[in] size New file size in bytes.
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval DRANG_EOK Success - file size changed successfully.
|
||||
* @retval DRANG_EINVAL Invalid file handle.
|
||||
* @retval DRANG_EIO I/O error occurred during truncation.
|
||||
* @retval DRANG_EPERM File not opened for writing.
|
||||
* @retval DRANG_ENOMEM Insufficient disk space for extension.
|
||||
* @remarks If extending the file, new bytes are zero-filled.
|
||||
* File position may be adjusted if beyond new file size.
|
||||
*/
|
||||
DRANG_PLATFORM_API int drang_fs_truncate(drang_fs_file_t *file, uint64_t size);
|
||||
|
||||
/**
|
||||
* @brief Creates a new empty file.
|
||||
*
|
||||
* Creates a new file at the specified path with the given permissions.
|
||||
* Fails if the file already exists.
|
||||
*
|
||||
* @param[in] path Path where the file should be created.
|
||||
* @param[in] permissions Initial file permissions.
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval DRANG_EOK Success - file created successfully.
|
||||
* @retval DRANG_EINVAL Invalid parameter (path is NULL).
|
||||
* @retval DRANG_ENOENT Parent directory does not exist.
|
||||
* @retval DRANG_EPERM Permission denied.
|
||||
* @retval DRANG_EBUSY File already exists.
|
||||
* @retval DRANG_ENOMEM Insufficient disk space.
|
||||
* @retval DRANG_EIO I/O error occurred during creation.
|
||||
* @remarks Use drang_fs_open() with create mode for more control over file creation.
|
||||
* Created file is initially empty.
|
||||
*/
|
||||
DRANG_PLATFORM_API int drang_fs_create(const char *path, drang_fs_permissions_t permissions);
|
||||
|
||||
/**
|
||||
* @brief Removes a file from the file system.
|
||||
*
|
||||
* Deletes the specified file from the file system. The file must not
|
||||
* be currently open by any process.
|
||||
*
|
||||
* @param[in] path Path to the file to remove.
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval DRANG_EOK Success - file removed successfully.
|
||||
* @retval DRANG_EINVAL Invalid parameter (path is NULL).
|
||||
* @retval DRANG_ENOENT File does not exist.
|
||||
* @retval DRANG_EPERM Permission denied or file is in use.
|
||||
* @retval DRANG_EIO I/O error occurred during removal.
|
||||
* @remarks File must not be open by any process. Use drang_fs_rmdir() for directories.
|
||||
* This operation is typically not reversible.
|
||||
*/
|
||||
DRANG_PLATFORM_API int drang_fs_remove(const char *path);
|
||||
|
||||
/**
|
||||
* @brief Moves or renames a file.
|
||||
*
|
||||
* Moves a file from the old path to the new path. This can be used
|
||||
* for renaming or moving files within or across directories.
|
||||
*
|
||||
* @param[in] old_path Current path of the file.
|
||||
* @param[in] new_path Destination path for the file.
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval DRANG_EOK Success - file moved successfully.
|
||||
* @retval DRANG_EINVAL Invalid parameter (old_path or new_path is NULL).
|
||||
* @retval DRANG_ENOENT Source file does not exist.
|
||||
* @retval DRANG_EPERM Permission denied.
|
||||
* @retval DRANG_EBUSY File is in use or destination exists.
|
||||
* @retval DRANG_EIO I/O error occurred during move operation.
|
||||
* @remarks Moving across different file systems may require copy+delete.
|
||||
* Destination file is overwritten if it exists and permissions allow.
|
||||
*/
|
||||
DRANG_PLATFORM_API int drang_fs_move(const char *old_path, const char *new_path);
|
||||
|
||||
/**
|
||||
* @brief Checks if a file or directory exists.
|
||||
*
|
||||
* Tests whether a file system entry exists at the specified path.
|
||||
* This function does not distinguish between files and directories.
|
||||
*
|
||||
* @param[in] path Path to check for existence.
|
||||
* @return true if the path exists, false otherwise.
|
||||
* @remarks This function does not set error codes. Use drang_fs_stat()
|
||||
* for more detailed information about file system entries.
|
||||
* Result may be outdated immediately after return due to concurrent access.
|
||||
*/
|
||||
DRANG_PLATFORM_API bool drang_fs_exists(const char *path);
|
||||
|
||||
/**
|
||||
* @brief Retrieves file system metadata.
|
||||
*
|
||||
* Gets comprehensive information about a file system entry including
|
||||
* type, size, permissions, and timestamps.
|
||||
*
|
||||
* @param[in] path Path to the file system entry.
|
||||
* @param[out] out_stat Pointer to structure to receive file information.
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval DRANG_EOK Success - file information retrieved successfully.
|
||||
* @retval DRANG_EINVAL Invalid parameter (path or out_stat is NULL).
|
||||
* @retval DRANG_ENOENT File does not exist.
|
||||
* @retval DRANG_EPERM Permission denied.
|
||||
* @retval DRANG_EIO I/O error occurred during stat operation.
|
||||
* @remarks Provides more detailed information than drang_fs_exists().
|
||||
* Timestamp format and precision may be platform-dependent.
|
||||
*/
|
||||
DRANG_PLATFORM_API int drang_fs_stat(const char *path, struct drang_fs_stat *out_stat);
|
||||
|
||||
/**
|
||||
* @brief Sets file permissions.
|
||||
*
|
||||
* Changes the access permissions of the specified file or directory.
|
||||
* The exact behavior may vary between platforms.
|
||||
*
|
||||
* @param[in] path Path to the file or directory.
|
||||
* @param[in] permissions New permission flags.
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval DRANG_EOK Success - permissions set successfully.
|
||||
* @retval DRANG_EINVAL Invalid parameter (path is NULL).
|
||||
* @retval DRANG_ENOENT File does not exist.
|
||||
* @retval DRANG_EPERM Permission denied.
|
||||
* @retval DRANG_NOTSUP Operation not supported on this platform.
|
||||
* @retval DRANG_EIO I/O error occurred during permission change.
|
||||
* @remarks Permission semantics may vary between platforms (Unix vs Windows).
|
||||
* Some permission combinations may not be supported on all platforms.
|
||||
*/
|
||||
DRANG_PLATFORM_API int drang_fs_set_permissions(const char *path, drang_fs_permissions_t permissions);
|
||||
|
||||
/**
|
||||
* @brief Creates a new directory.
|
||||
*
|
||||
* Creates a directory at the specified path with the given permissions.
|
||||
* Parent directories must already exist.
|
||||
*
|
||||
* @param[in] path Path where the directory should be created.
|
||||
* @param[in] permissions Initial directory permissions.
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval DRANG_EOK Success - directory created successfully.
|
||||
* @retval DRANG_EINVAL Invalid parameter (path is NULL).
|
||||
* @retval DRANG_ENOENT Parent directory does not exist.
|
||||
* @retval DRANG_EPERM Permission denied.
|
||||
* @retval DRANG_EBUSY Directory already exists.
|
||||
* @retval DRANG_ENOMEM Insufficient disk space.
|
||||
* @retval DRANG_EIO I/O error occurred during creation.
|
||||
* @remarks Parent directories must exist. Use recursive creation for nested paths.
|
||||
* Directory permissions may be modified by system umask.
|
||||
*/
|
||||
DRANG_PLATFORM_API int drang_fs_mkdir(const char *path, drang_fs_permissions_t permissions);
|
||||
|
||||
/**
|
||||
* @brief Removes an empty directory.
|
||||
*
|
||||
* Deletes the specified directory from the file system. The directory
|
||||
* must be empty (contain no files or subdirectories).
|
||||
*
|
||||
* @param[in] path Path to the directory to remove.
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval DRANG_EOK Success - directory removed successfully.
|
||||
* @retval DRANG_EINVAL Invalid parameter (path is NULL).
|
||||
* @retval DRANG_ENOENT Directory does not exist.
|
||||
* @retval DRANG_EPERM Permission denied.
|
||||
* @retval DRANG_EBUSY Directory is not empty or in use.
|
||||
* @retval DRANG_EIO I/O error occurred during removal.
|
||||
* @remarks Directory must be empty before removal. Use drang_fs_remove() for files.
|
||||
*/
|
||||
DRANG_PLATFORM_API int drang_fs_rmdir(const char *path);
|
||||
|
||||
/**
|
||||
* @brief Opens a directory for reading.
|
||||
*
|
||||
* Opens the specified directory for reading its entries. The directory
|
||||
* handle must be closed with drang_fs_closedir() when no longer needed.
|
||||
*
|
||||
* @param[in] path Path to the directory to open.
|
||||
* @param[out] out_directory Pointer to store the opened directory handle.
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval DRANG_EOK Success - directory opened successfully.
|
||||
* @retval DRANG_EINVAL Invalid parameter (path or out_directory is NULL).
|
||||
* @retval DRANG_ENOENT Directory does not exist.
|
||||
* @retval DRANG_EPERM Permission denied.
|
||||
* @retval DRANG_ENOMEM Insufficient memory to allocate directory handle.
|
||||
* @retval DRANG_EIO I/O error occurred during directory opening.
|
||||
* @remarks The returned directory handle must be freed with drang_fs_closedir().
|
||||
* Use drang_fs_readdir() to iterate through directory entries.
|
||||
*/
|
||||
DRANG_PLATFORM_API int drang_fs_opendir(const char *path, drang_fs_directory_t **out_directory);
|
||||
|
||||
/**
|
||||
* @brief Reads the next directory entry.
|
||||
*
|
||||
* Reads the next entry from an open directory. Returns information
|
||||
* about the entry including name and type.
|
||||
*
|
||||
* @param[in] directory Open directory handle.
|
||||
* @param[out] out_entry Pointer to structure to receive entry information.
|
||||
* @return 0 on success, negative error code on failure or end of directory.
|
||||
* @retval DRANG_EOK Success - entry read successfully.
|
||||
* @retval DRANG_EINVAL Invalid parameter (directory or out_entry is NULL).
|
||||
* @retval DRANG_ENOENT No more entries (end of directory reached).
|
||||
* @retval DRANG_EIO I/O error occurred during reading.
|
||||
* @remarks Returns DRANG_ENOENT when no more entries are available.
|
||||
* Entry names "." and ".." will not be included.
|
||||
*/
|
||||
DRANG_PLATFORM_API int drang_fs_readdir(drang_fs_directory_t *directory, drang_fs_dir_entry_t *out_entry);
|
||||
|
||||
/**
|
||||
* @brief Closes an open directory handle.
|
||||
*
|
||||
* Closes the directory handle and releases associated resources.
|
||||
*
|
||||
* @param[in] directory Directory handle to close, or NULL.
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval DRANG_EOK Success - directory closed successfully.
|
||||
* @retval DRANG_EINVAL Invalid directory handle.
|
||||
* @retval DRANG_EIO I/O error occurred during directory closing.
|
||||
* @remarks Safe to call with NULL pointer. Directory handle becomes invalid after closing.
|
||||
*/
|
||||
DRANG_PLATFORM_API int drang_fs_closedir(drang_fs_directory_t *directory);
|
||||
|
||||
/**
|
||||
* @brief Gets the current working directory.
|
||||
*
|
||||
* Retrieves the absolute path of the current working directory.
|
||||
* The path is null-terminated and stored in the provided buffer.
|
||||
*
|
||||
* @param[out] buffer Buffer to receive the current working directory path.
|
||||
* @param[in] size Size of the buffer in bytes.
|
||||
* @param[out] out_size Pointer to store the actual path length (may be NULL).
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval DRANG_EOK Success - current directory retrieved successfully.
|
||||
* @retval DRANG_EINVAL Invalid parameter (buffer is NULL or size is 0).
|
||||
* @retval DRANG_ENOMEM Buffer too small to hold the path.
|
||||
* @retval DRANG_EIO I/O error occurred during path retrieval.
|
||||
* @remarks If out_size is provided, it contains the required buffer size on DRANG_ENOMEM.
|
||||
* The returned path is always absolute and null-terminated.
|
||||
*/
|
||||
DRANG_PLATFORM_API int drang_fs_get_cwd(char *buffer, size_t size, size_t *out_size);
|
||||
|
||||
/**
|
||||
* @brief Sets the current working directory.
|
||||
*
|
||||
* Changes the current working directory to the specified path.
|
||||
* Affects the process-wide current directory.
|
||||
*
|
||||
* @param[in] path Path to the new working directory.
|
||||
* @return 0 on success, negative error code on failure.
|
||||
* @retval DRANG_EOK Success - working directory changed successfully.
|
||||
* @retval DRANG_EINVAL Invalid parameter (path is NULL).
|
||||
* @retval DRANG_ENOENT Directory does not exist.
|
||||
* @retval DRANG_EPERM Permission denied.
|
||||
* @retval DRANG_EIO I/O error occurred during directory change.
|
||||
* @remarks Changes the process-wide current working directory.
|
||||
* Relative paths in subsequent operations will be relative to this directory.
|
||||
*/
|
||||
DRANG_PLATFORM_API int drang_fs_set_cwd(const char *path);
|
||||
|
||||
DRANG_END_DECLS
|
||||
|
|
@ -36,11 +36,14 @@
|
|||
# define DRANG_ALLOC_ALIGN_ATTR(idx) __attribute__((alloc_align(idx)))
|
||||
# define DRANG_MALLOC_ATTR __attribute__((malloc))
|
||||
|
||||
# define DRANG_BIT_CAST(_Expr_, _From_, _To_) __builtin_bit_cast(_To_, _Expr_)
|
||||
|
||||
# define DRANG_MUL_OVERFLOW(_A_, _B_, _Result_) (__builtin_mul_overflow(_A_, _B_, _Result_) != 0)
|
||||
# define DRANG_ADD_OVERFLOW(_A_, _B_, _Result_) (__builtin_add_overflow(_A_, _B_, _Result_) != 0)
|
||||
# define DRANG_SUB_OVERFLOW(_A_, _B_, _Result_) (__builtin_sub_overflow(_A_, _B_, _Result_) != 0)
|
||||
|
||||
#else
|
||||
# define DRANG_PRINTF_ATTR(fmt_idx, arg_idx)
|
||||
# define DRANG_VPRINTF_ATTR(fmt_idx)
|
||||
# define DRANG_ALLOC_SIZE_COUNT_ATTR(idx, count)
|
||||
# define DRANG_ALLOC_SIZE_ATTR(idx)
|
||||
# define DRANG_ALLOC_ALIGN_ATTR(idx)
|
||||
# define DRANG_MALLOC_ATTR
|
||||
# error "Unsupported compiler, please implement attribute macros"
|
||||
#endif
|
||||
|
||||
#define DRANG_UNUSED(x) ((void) (x))
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ int drang_error_to_errno(int error)
|
|||
return ETIMEDOUT;
|
||||
case DRANG_EBUSY:
|
||||
return EBUSY;
|
||||
case DRANG_EOVERFLOW:
|
||||
return EOVERFLOW;
|
||||
case DRANG_EPLATFORM:
|
||||
default:
|
||||
return EIO; // Generic I/O error for unknown platform errors
|
||||
|
|
@ -50,6 +52,8 @@ int drang_errno_to_error(const int errnum)
|
|||
return DRANG_ETIMEDOUT;
|
||||
case EBUSY:
|
||||
return DRANG_EBUSY;
|
||||
case EOVERFLOW:
|
||||
return DRANG_EOVERFLOW;
|
||||
default:
|
||||
return DRANG_EPLATFORM;
|
||||
}
|
||||
|
|
|
|||
158
Source/DrangPlatform/Source/linux/fs/dir.c
Normal file
158
Source/DrangPlatform/Source/linux/fs/dir.c
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
#include "drang/alloc.h"
|
||||
#include "errno_convert.h"
|
||||
#include "internal.h"
|
||||
#include <drang/error.h>
|
||||
#include <drang/fs.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
int drang_fs_mkdir(const char *path, drang_fs_permissions_t permissions)
|
||||
{
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(path, DRANG_EINVAL);
|
||||
|
||||
int mode = 0;
|
||||
if (permissions & DRANG_FS_PERM_USER_READ)
|
||||
mode |= S_IRUSR;
|
||||
if (permissions & DRANG_FS_PERM_USER_WRITE)
|
||||
mode |= S_IWUSR;
|
||||
if (permissions & DRANG_FS_PERM_USER_EXEC)
|
||||
mode |= S_IXUSR;
|
||||
if (permissions & DRANG_FS_PERM_GROUP_READ)
|
||||
mode |= S_IRGRP;
|
||||
if (permissions & DRANG_FS_PERM_GROUP_WRITE)
|
||||
mode |= S_IWGRP;
|
||||
if (permissions & DRANG_FS_PERM_GROUP_EXEC)
|
||||
mode |= S_IXGRP;
|
||||
if (permissions & DRANG_FS_PERM_OTHER_READ)
|
||||
mode |= S_IROTH;
|
||||
if (permissions & DRANG_FS_PERM_OTHER_WRITE)
|
||||
mode |= S_IWOTH;
|
||||
if (permissions & DRANG_FS_PERM_OTHER_EXEC)
|
||||
mode |= S_IXOTH;
|
||||
|
||||
if (mkdir(path, mode) != 0) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
|
||||
DRANG_END_TRY_IGNORE()
|
||||
}
|
||||
|
||||
int drang_fs_rmdir(const char *path)
|
||||
{
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(path, DRANG_EINVAL);
|
||||
if (rmdir(path) != 0) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
DRANG_END_TRY_IGNORE()
|
||||
}
|
||||
|
||||
int drang_fs_opendir(const char *path, drang_fs_directory_t **out_directory)
|
||||
{
|
||||
struct drang_fs_directory *fs_dir = NULL;
|
||||
DIR *dir = NULL;
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(path, DRANG_EINVAL);
|
||||
DRANG_FAIL_IF_NULL(out_directory, DRANG_EINVAL);
|
||||
|
||||
dir = opendir(path);
|
||||
if (!dir) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
|
||||
fs_dir = DRANG_TRY_ALLOC_T(struct drang_fs_directory);
|
||||
|
||||
fs_dir->dir = dir;
|
||||
|
||||
DRANG_RETURN_IN(out_directory, fs_dir);
|
||||
|
||||
DRANG_CATCH(_)
|
||||
{
|
||||
if (dir) {
|
||||
closedir(dir);
|
||||
}
|
||||
drang_free(fs_dir);
|
||||
}
|
||||
DRANG_END_TRY()
|
||||
}
|
||||
|
||||
int drang_fs_readdir(drang_fs_directory_t *directory, drang_fs_dir_entry_t *out_entry)
|
||||
{
|
||||
struct dirent *entry = NULL;
|
||||
DRANG_BEGIN_TRY()
|
||||
|
||||
entry = readdir(directory->dir);
|
||||
|
||||
DRANG_CHECK(entry != NULL, DRANG_ENOENT);
|
||||
|
||||
while (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
|
||||
entry = readdir(directory->dir);
|
||||
DRANG_CHECK(entry != NULL, DRANG_ENOENT);
|
||||
}
|
||||
|
||||
strncpy(out_entry->name, entry->d_name, DRANG_FS_MAX_REL_PATH_SIZE);
|
||||
out_entry->name[DRANG_FS_MAX_REL_PATH_SIZE - 1] = '\0';
|
||||
switch (entry->d_type) {
|
||||
case DT_REG:
|
||||
out_entry->type = drang_fs_type_file;
|
||||
break;
|
||||
case DT_DIR:
|
||||
out_entry->type = drang_fs_type_directory;
|
||||
break;
|
||||
case DT_LNK:
|
||||
out_entry->type = drang_fs_type_symlink;
|
||||
break;
|
||||
default:
|
||||
out_entry->type = drang_fs_type_unknown;
|
||||
break;
|
||||
}
|
||||
|
||||
DRANG_END_TRY_IGNORE()
|
||||
}
|
||||
|
||||
int drang_fs_closedir(drang_fs_directory_t *directory)
|
||||
{
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(directory, DRANG_EINVAL);
|
||||
if (closedir(directory->dir) != 0) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
|
||||
drang_free(directory);
|
||||
DRANG_END_TRY_IGNORE()
|
||||
}
|
||||
|
||||
int drang_fs_get_cwd(char *buffer, size_t size, size_t *out_size)
|
||||
{
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(buffer, DRANG_EINVAL);
|
||||
if (getcwd(buffer, size) != NULL) {
|
||||
DRANG_RETURN_IN(out_size, strlen(buffer));
|
||||
}
|
||||
|
||||
if (errno == ERANGE) {
|
||||
if (out_size) {
|
||||
long required_size = pathconf(".", _PC_PATH_MAX);
|
||||
*out_size = required_size;
|
||||
}
|
||||
DRANG_FAIL(DRANG_ENOMEM);
|
||||
}
|
||||
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
|
||||
DRANG_END_TRY_IGNORE()
|
||||
}
|
||||
|
||||
int drang_fs_set_cwd(const char *path)
|
||||
{
|
||||
DRANG_BEGIN_TRY()
|
||||
|
||||
if (chdir(path) != 0) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
|
||||
DRANG_END_TRY_IGNORE()
|
||||
}
|
||||
499
Source/DrangPlatform/Source/linux/fs/file.c
Normal file
499
Source/DrangPlatform/Source/linux/fs/file.c
Normal file
|
|
@ -0,0 +1,499 @@
|
|||
|
||||
#include "drang/alloc.h"
|
||||
#include "internal.h"
|
||||
#include <drang/error.h>
|
||||
#include <errno.h>
|
||||
#include <errno_convert.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
static int mode_to_open_flags(const drang_fs_mode_t mode)
|
||||
{
|
||||
int oflags = 0;
|
||||
if (mode & drang_fs_mode_read && mode & drang_fs_mode_write) {
|
||||
oflags |= O_RDWR;
|
||||
} else if (mode & drang_fs_mode_read) {
|
||||
oflags |= O_RDONLY;
|
||||
} else if (mode & drang_fs_mode_write) {
|
||||
oflags |= O_WRONLY;
|
||||
}
|
||||
|
||||
if (mode & drang_fs_mode_append) {
|
||||
oflags |= O_APPEND;
|
||||
}
|
||||
if (mode & drang_fs_mode_create) {
|
||||
oflags |= O_CREAT;
|
||||
}
|
||||
if (mode & drang_fs_mode_truncate) {
|
||||
oflags |= O_TRUNC;
|
||||
}
|
||||
if (mode & drang_fs_mode_exclusive) {
|
||||
oflags |= O_EXCL;
|
||||
}
|
||||
if (oflags == 0)
|
||||
return 0;
|
||||
oflags |= O_LARGEFILE; // Ensure large file support
|
||||
return oflags;
|
||||
}
|
||||
|
||||
static int drang_permissions_to_linux(const drang_fs_permissions_t permissions)
|
||||
{
|
||||
int result = 0;
|
||||
if (permissions & DRANG_FS_PERM_USER_READ)
|
||||
result |= S_IRUSR;
|
||||
if (permissions & DRANG_FS_PERM_USER_WRITE)
|
||||
result |= S_IWUSR;
|
||||
if (permissions & DRANG_FS_PERM_USER_EXEC)
|
||||
result |= S_IXUSR;
|
||||
if (permissions & DRANG_FS_PERM_GROUP_READ)
|
||||
result |= S_IRGRP;
|
||||
if (permissions & DRANG_FS_PERM_GROUP_WRITE)
|
||||
result |= S_IWGRP;
|
||||
if (permissions & DRANG_FS_PERM_GROUP_EXEC)
|
||||
result |= S_IXGRP;
|
||||
if (permissions & DRANG_FS_PERM_OTHER_READ)
|
||||
result |= S_IROTH;
|
||||
if (permissions & DRANG_FS_PERM_OTHER_WRITE)
|
||||
result |= S_IWOTH;
|
||||
if (permissions & DRANG_FS_PERM_OTHER_EXEC)
|
||||
result |= S_IXOTH;
|
||||
return result;
|
||||
}
|
||||
|
||||
char drang_fs_separator(void)
|
||||
{
|
||||
return '/';
|
||||
}
|
||||
|
||||
int drang_fs_open(const char *path, drang_fs_mode_t mode, drang_fs_file_t **out_file)
|
||||
{
|
||||
struct drang_fs_file *file = NULL;
|
||||
DRANG_BEGIN_TRY()
|
||||
|
||||
DRANG_FAIL_IF_NULL(path, DRANG_EINVAL);
|
||||
DRANG_FAIL_IF_NULL(out_file, DRANG_EINVAL);
|
||||
|
||||
const int oflags = mode_to_open_flags(mode);
|
||||
DRANG_CHECK(oflags != 0, DRANG_EINVAL);
|
||||
|
||||
file = DRANG_TRY_CALLOC_T(struct drang_fs_file);
|
||||
|
||||
DRANG_TRY(drang_rwlock_init(&file->lock));
|
||||
|
||||
const int fd = (oflags & O_CREAT) ? open(path, oflags, 0666) : open(path, oflags);
|
||||
if (fd == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
|
||||
file->fd = fd;
|
||||
file->mode = mode;
|
||||
DRANG_RETURN_IN(out_file, file);
|
||||
|
||||
DRANG_CATCH(_)
|
||||
{
|
||||
if (file != NULL) {
|
||||
drang_rwlock_fini(&file->lock);
|
||||
drang_free(file);
|
||||
}
|
||||
}
|
||||
|
||||
DRANG_END_TRY()
|
||||
}
|
||||
|
||||
int drang_fs_close(drang_fs_file_t *file)
|
||||
{
|
||||
bool locked = false;
|
||||
if (file == NULL)
|
||||
return DRANG_EOK;
|
||||
DRANG_BEGIN_TRY()
|
||||
|
||||
DRANG_TRY(drang_fs_flush(file));
|
||||
|
||||
DRANG_CHECK(file->fd >= 0, DRANG_EINVAL);
|
||||
|
||||
DRANG_TRY(drang_rwlock_wrlock(&file->lock));
|
||||
locked = true;
|
||||
const int result = close(file->fd);
|
||||
if (result == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
|
||||
drang_rwlock_wrunlock(&file->lock);
|
||||
|
||||
drang_rwlock_fini(&file->lock);
|
||||
|
||||
drang_free(file);
|
||||
|
||||
DRANG_CATCH(_)
|
||||
{
|
||||
if (locked) {
|
||||
drang_rwlock_wrunlock(&file->lock);
|
||||
}
|
||||
}
|
||||
|
||||
DRANG_END_TRY()
|
||||
}
|
||||
|
||||
// TODO: Implement buffering
|
||||
int drang_fs_read(drang_fs_file_t *file, void *buffer, size_t count, size_t size, size_t *bytes_read)
|
||||
{
|
||||
bool locked = false;
|
||||
DRANG_BEGIN_TRY()
|
||||
|
||||
DRANG_FAIL_IF_NULL(file, DRANG_EINVAL);
|
||||
DRANG_FAIL_IF_NULL(buffer, DRANG_EINVAL);
|
||||
DRANG_CHECK(file->fd >= 0, DRANG_EINVAL);
|
||||
DRANG_CHECK(file->mode & drang_fs_mode_read, DRANG_EPERM);
|
||||
DRANG_CHECK(count > 0 && size > 0, DRANG_EINVAL);
|
||||
|
||||
size_t total_bytes = 0;
|
||||
DRANG_CHECK(!DRANG_MUL_OVERFLOW(count, size, &total_bytes), DRANG_EOVERFLOW);
|
||||
|
||||
DRANG_TRY(drang_rwlock_rdlock(&file->lock));
|
||||
locked = true;
|
||||
|
||||
const ssize_t res = read(file->fd, buffer, total_bytes);
|
||||
if (res == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
|
||||
if (bytes_read != NULL) {
|
||||
*bytes_read = (size_t) res;
|
||||
}
|
||||
|
||||
drang_rwlock_rdunlock(&file->lock);
|
||||
|
||||
DRANG_CATCH(_)
|
||||
{
|
||||
if (locked) {
|
||||
drang_rwlock_rdunlock(&file->lock);
|
||||
}
|
||||
}
|
||||
DRANG_END_TRY()
|
||||
}
|
||||
|
||||
int drang_fs_write(drang_fs_file_t *file, const void *buffer, size_t count, size_t size, size_t *bytes_written)
|
||||
{
|
||||
bool locked = false;
|
||||
DRANG_BEGIN_TRY()
|
||||
|
||||
DRANG_FAIL_IF_NULL(file, DRANG_EINVAL);
|
||||
DRANG_FAIL_IF_NULL(buffer, DRANG_EINVAL);
|
||||
DRANG_CHECK(file->fd >= 0, DRANG_EINVAL);
|
||||
DRANG_CHECK(file->mode & (drang_fs_mode_write | drang_fs_mode_append), DRANG_EPERM);
|
||||
DRANG_CHECK(count > 0 && size > 0, DRANG_EINVAL);
|
||||
|
||||
size_t total_bytes = 0;
|
||||
DRANG_CHECK(!DRANG_MUL_OVERFLOW(count, size, &total_bytes), DRANG_EOVERFLOW);
|
||||
DRANG_TRY(drang_rwlock_wrlock(&file->lock));
|
||||
locked = true;
|
||||
|
||||
const ssize_t res = write(file->fd, buffer, total_bytes);
|
||||
if (res == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
if (bytes_written != NULL) {
|
||||
*bytes_written = (size_t) res;
|
||||
}
|
||||
|
||||
drang_rwlock_wrunlock(&file->lock);
|
||||
|
||||
DRANG_CATCH(_)
|
||||
{
|
||||
if (locked) {
|
||||
drang_rwlock_wrunlock(&file->lock);
|
||||
}
|
||||
}
|
||||
DRANG_END_TRY()
|
||||
}
|
||||
|
||||
int drang_fs_seek(drang_fs_file_t *file, int64_t offset, drang_seek_origin_t origin)
|
||||
{
|
||||
bool locked = false;
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(file, DRANG_EINVAL);
|
||||
DRANG_CHECK(file->fd >= 0, DRANG_EINVAL);
|
||||
int whence = 0;
|
||||
switch (origin) {
|
||||
case drang_seek_origin_begin:
|
||||
whence = SEEK_SET;
|
||||
break;
|
||||
case drang_seek_origin_current:
|
||||
whence = SEEK_CUR;
|
||||
break;
|
||||
case drang_seek_origin_end:
|
||||
whence = SEEK_END;
|
||||
break;
|
||||
default:
|
||||
DRANG_FAIL(DRANG_EINVAL);
|
||||
}
|
||||
|
||||
DRANG_TRY(drang_rwlock_wrlock(&file->lock));
|
||||
locked = true;
|
||||
const off_t res = lseek(file->fd, (off_t) offset, whence);
|
||||
if (res == (off_t) -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
|
||||
drang_rwlock_wrunlock(&file->lock);
|
||||
|
||||
DRANG_CATCH(_)
|
||||
{
|
||||
if (locked) {
|
||||
drang_rwlock_wrunlock(&file->lock);
|
||||
}
|
||||
}
|
||||
DRANG_END_TRY()
|
||||
}
|
||||
|
||||
int drang_fs_tell(drang_fs_file_t *file, uint64_t *position)
|
||||
{
|
||||
bool locked = false;
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(file, DRANG_EINVAL);
|
||||
DRANG_FAIL_IF_NULL(position, DRANG_EINVAL);
|
||||
DRANG_CHECK(file->fd >= 0, DRANG_EINVAL);
|
||||
|
||||
DRANG_TRY(drang_rwlock_rdlock(&file->lock));
|
||||
locked = true;
|
||||
const off_t res = lseek(file->fd, 0, SEEK_CUR);
|
||||
if (res == (off_t) -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
*position = (uint64_t) res;
|
||||
|
||||
drang_rwlock_rdunlock(&file->lock);
|
||||
|
||||
DRANG_CATCH(_)
|
||||
{
|
||||
if (locked) {
|
||||
drang_rwlock_rdunlock(&file->lock);
|
||||
}
|
||||
}
|
||||
DRANG_END_TRY()
|
||||
}
|
||||
|
||||
// TODO: Implement buffering
|
||||
int drang_fs_flush(drang_fs_file_t *file)
|
||||
{
|
||||
bool locked = false;
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(file, DRANG_EINVAL);
|
||||
DRANG_CHECK(file->fd >= 0, DRANG_EINVAL);
|
||||
|
||||
DRANG_TRY(drang_rwlock_wrlock(&file->lock));
|
||||
locked = true;
|
||||
const int res = fsync(file->fd);
|
||||
if (res == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
|
||||
drang_rwlock_wrunlock(&file->lock);
|
||||
|
||||
DRANG_CATCH(_)
|
||||
{
|
||||
if (locked) {
|
||||
drang_rwlock_wrunlock(&file->lock);
|
||||
}
|
||||
}
|
||||
DRANG_END_TRY()
|
||||
}
|
||||
|
||||
int drang_fs_truncate(drang_fs_file_t *file, uint64_t size)
|
||||
{
|
||||
bool locked = false;
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(file, DRANG_EINVAL);
|
||||
DRANG_CHECK(file->fd >= 0, DRANG_EINVAL);
|
||||
DRANG_CHECK(file->mode & drang_fs_mode_write, DRANG_EPERM);
|
||||
|
||||
DRANG_TRY(drang_rwlock_wrlock(&file->lock));
|
||||
locked = true;
|
||||
|
||||
const int res = ftruncate(file->fd, (off_t) size);
|
||||
if (res == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
drang_rwlock_wrunlock(&file->lock);
|
||||
|
||||
DRANG_CATCH(_)
|
||||
{
|
||||
if (locked) {
|
||||
drang_rwlock_wrunlock(&file->lock);
|
||||
}
|
||||
}
|
||||
|
||||
DRANG_END_TRY()
|
||||
}
|
||||
|
||||
int drang_fs_create(const char *path, drang_fs_permissions_t permissions)
|
||||
{
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(path, DRANG_EINVAL);
|
||||
const int linux_perms = drang_permissions_to_linux(permissions);
|
||||
const int fd = open(path, O_CREAT | O_EXCL | O_WRONLY | O_LARGEFILE, linux_perms);
|
||||
if (fd == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
const int res = close(fd);
|
||||
if (res == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
|
||||
DRANG_END_TRY_IGNORE()
|
||||
}
|
||||
|
||||
int drang_fs_remove(const char *path)
|
||||
{
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(path, DRANG_EINVAL);
|
||||
const int res = unlink(path);
|
||||
if (res == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
|
||||
DRANG_END_TRY_IGNORE()
|
||||
}
|
||||
|
||||
int drang_fs_move(const char *old_path, const char *new_path)
|
||||
{
|
||||
int src_fd = -1;
|
||||
int dst_fd = -1;
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(old_path, DRANG_EINVAL);
|
||||
DRANG_FAIL_IF_NULL(new_path, DRANG_EINVAL);
|
||||
if (rename(old_path, new_path) == 0) {
|
||||
DRANG_RETURN();
|
||||
}
|
||||
|
||||
if (errno == EXDEV) { // Cross-device link, need to copy+delete
|
||||
struct stat src_stat = {0};
|
||||
|
||||
if (stat(old_path, &src_stat) == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
src_fd = open(old_path, O_RDONLY | O_LARGEFILE);
|
||||
if (src_fd == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
dst_fd = open(new_path, O_CREAT | O_EXCL | O_WRONLY | O_LARGEFILE, src_stat.st_mode);
|
||||
if (dst_fd == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
char buffer[DRANG_LINUX_FS_BUFFER_SIZE];
|
||||
ssize_t bytes_read = 0;
|
||||
while ((bytes_read = read(src_fd, buffer, sizeof(buffer))) > 0) {
|
||||
const ssize_t write_res = write(dst_fd, buffer, (size_t) bytes_read);
|
||||
if (write_res == -1 || write_res != bytes_read) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
}
|
||||
|
||||
if (bytes_read == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
|
||||
if (close(src_fd) == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
src_fd = -1;
|
||||
|
||||
if (close(dst_fd) == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
dst_fd = -1;
|
||||
|
||||
if (unlink(old_path) == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
|
||||
DRANG_RETURN();
|
||||
}
|
||||
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
|
||||
DRANG_CATCH(_)
|
||||
{
|
||||
if (src_fd != -1) {
|
||||
close(src_fd);
|
||||
}
|
||||
if (dst_fd != -1) {
|
||||
close(dst_fd);
|
||||
unlink(new_path);
|
||||
}
|
||||
}
|
||||
|
||||
DRANG_END_TRY()
|
||||
}
|
||||
|
||||
bool drang_fs_exists(const char *path)
|
||||
{
|
||||
if (path == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct stat buffer;
|
||||
return (stat(path, &buffer) == 0);
|
||||
}
|
||||
|
||||
int drang_fs_stat(const char *path, struct drang_fs_stat *out_stat)
|
||||
{
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(path, DRANG_EINVAL);
|
||||
DRANG_FAIL_IF_NULL(out_stat, DRANG_EINVAL);
|
||||
|
||||
struct stat st;
|
||||
if (stat(path, &st) == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
out_stat->type = drang_fs_type_file;
|
||||
} else if (S_ISDIR(st.st_mode)) {
|
||||
out_stat->type = drang_fs_type_directory;
|
||||
} else if (S_ISLNK(st.st_mode)) {
|
||||
out_stat->type = drang_fs_type_symlink;
|
||||
} else {
|
||||
out_stat->type = drang_fs_type_unknown;
|
||||
}
|
||||
|
||||
out_stat->size = (uint64_t) st.st_size;
|
||||
out_stat->permissions = 0;
|
||||
if (st.st_mode & S_IRUSR)
|
||||
out_stat->permissions |= DRANG_FS_PERM_USER_READ;
|
||||
if (st.st_mode & S_IWUSR)
|
||||
out_stat->permissions |= DRANG_FS_PERM_USER_WRITE;
|
||||
if (st.st_mode & S_IXUSR)
|
||||
out_stat->permissions |= DRANG_FS_PERM_USER_EXEC;
|
||||
if (st.st_mode & S_IRGRP)
|
||||
out_stat->permissions |= DRANG_FS_PERM_GROUP_READ;
|
||||
if (st.st_mode & S_IWGRP)
|
||||
out_stat->permissions |= DRANG_FS_PERM_GROUP_WRITE;
|
||||
if (st.st_mode & S_IXGRP)
|
||||
out_stat->permissions |= DRANG_FS_PERM_GROUP_EXEC;
|
||||
if (st.st_mode & S_IROTH)
|
||||
out_stat->permissions |= DRANG_FS_PERM_OTHER_READ;
|
||||
if (st.st_mode & S_IWOTH)
|
||||
out_stat->permissions |= DRANG_FS_PERM_OTHER_WRITE;
|
||||
if (st.st_mode & S_IXOTH)
|
||||
out_stat->permissions |= DRANG_FS_PERM_OTHER_EXEC;
|
||||
|
||||
out_stat->created_time = (drang_fs_time_t) st.st_ctim.tv_sec * 1000000000 + st.st_ctim.tv_nsec;
|
||||
out_stat->modified_time = (drang_fs_time_t) st.st_mtim.tv_sec * 1000000000 + st.st_mtim.tv_nsec;
|
||||
out_stat->accessed_time = (drang_fs_time_t) st.st_atim.tv_sec * 1000000000 + st.st_atim.tv_nsec;
|
||||
|
||||
DRANG_END_TRY_IGNORE()
|
||||
}
|
||||
|
||||
int drang_fs_set_permissions(const char *path, drang_fs_permissions_t permissions)
|
||||
{
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(path, DRANG_EINVAL);
|
||||
const int linux_perms = drang_permissions_to_linux(permissions);
|
||||
if (chmod(path, linux_perms) == -1) {
|
||||
DRANG_FAIL(drang_errno_to_error(errno));
|
||||
}
|
||||
|
||||
DRANG_END_TRY_IGNORE()
|
||||
}
|
||||
18
Source/DrangPlatform/Source/linux/fs/internal.h
Normal file
18
Source/DrangPlatform/Source/linux/fs/internal.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
#include "linux/sync/internal.h"
|
||||
#include <dirent.h>
|
||||
#include <drang/fs.h>
|
||||
#include <unistd.h>
|
||||
#define DRANG_LINUX_FS_BUFFER_SIZE (4096)
|
||||
|
||||
struct drang_fs_file
|
||||
{
|
||||
int fd;
|
||||
drang_fs_mode_t mode;
|
||||
struct drang_rwlock lock;
|
||||
};
|
||||
|
||||
struct drang_fs_directory
|
||||
{
|
||||
DIR *dir;
|
||||
};
|
||||
35
Source/DrangPlatform/Source/win32/common.c
Normal file
35
Source/DrangPlatform/Source/win32/common.c
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#include "common.h"
|
||||
#include <drang/error.h>
|
||||
int win32_error_to_drang(const DWORD error)
|
||||
{
|
||||
switch (error) {
|
||||
case ERROR_SUCCESS:
|
||||
return DRANG_EOK;
|
||||
case ERROR_FILE_NOT_FOUND:
|
||||
case ERROR_PATH_NOT_FOUND:
|
||||
case ERROR_INVALID_DRIVE:
|
||||
return DRANG_ENOENT;
|
||||
case ERROR_ACCESS_DENIED:
|
||||
case ERROR_SHARING_VIOLATION:
|
||||
case ERROR_LOCK_VIOLATION:
|
||||
return DRANG_EPERM;
|
||||
case ERROR_ALREADY_EXISTS:
|
||||
case ERROR_FILE_EXISTS:
|
||||
return DRANG_EBUSY;
|
||||
case ERROR_NOT_ENOUGH_MEMORY:
|
||||
case ERROR_OUTOFMEMORY:
|
||||
return DRANG_ENOMEM;
|
||||
case ERROR_DISK_FULL:
|
||||
return DRANG_ENOSPC;
|
||||
case ERROR_INVALID_PARAMETER:
|
||||
return DRANG_EINVAL;
|
||||
case ERROR_WRITE_PROTECT:
|
||||
return DRANG_EROFS;
|
||||
case ERROR_IO_DEVICE:
|
||||
case ERROR_IO_PENDING:
|
||||
return DRANG_EIO;
|
||||
case ERROR_CRC:
|
||||
default:
|
||||
return DRANG_EPLATFORM; // Unknown or unhandled error
|
||||
}
|
||||
}
|
||||
10
Source/DrangPlatform/Source/win32/common.h
Normal file
10
Source/DrangPlatform/Source/win32/common.h
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
#include <drang/platform.h>
|
||||
|
||||
DRANG_BEGIN_DECLS
|
||||
|
||||
DRANG_PLATFORM_API int win32_error_to_drang(DWORD error);
|
||||
|
||||
DRANG_END_DECLS
|
||||
154
Source/DrangPlatform/Source/win32/fs/dir.c
Normal file
154
Source/DrangPlatform/Source/win32/fs/dir.c
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
#include "internal.h"
|
||||
#include "win32/common.h"
|
||||
#include <drang/alloc.h>
|
||||
#include <drang/error.h>
|
||||
#include <drang/fs.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int drang_fs_mkdir(const char *path, drang_fs_permissions_t permissions)
|
||||
{
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_UNUSED(permissions); // Permissions are not used on Windows
|
||||
|
||||
if (!CreateDirectoryA(path, NULL)) {
|
||||
DRANG_FAIL(win32_error_to_drang(GetLastError()));
|
||||
}
|
||||
|
||||
DRANG_END_TRY_IGNORE()
|
||||
}
|
||||
|
||||
int drang_fs_rmdir(const char *path)
|
||||
{
|
||||
DRANG_BEGIN_TRY()
|
||||
if (!RemoveDirectoryA(path)) {
|
||||
DRANG_FAIL(win32_error_to_drang(GetLastError()));
|
||||
}
|
||||
DRANG_END_TRY_IGNORE()
|
||||
}
|
||||
|
||||
int drang_fs_opendir(const char *path, drang_fs_directory_t **out_directory)
|
||||
{
|
||||
struct drang_fs_directory *fs_dir = NULL;
|
||||
char search_path[MAX_PATH];
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(path, DRANG_EINVAL);
|
||||
DRANG_FAIL_IF_NULL(out_directory, DRANG_EINVAL);
|
||||
|
||||
fs_dir = DRANG_TRY_CALLOC_T(struct drang_fs_directory);
|
||||
snprintf(search_path, MAX_PATH, "%s\\*", path);
|
||||
fs_dir->handle = FindFirstFileA(search_path, &fs_dir->find_data);
|
||||
if (fs_dir->handle == INVALID_HANDLE_VALUE) {
|
||||
DRANG_FAIL(win32_error_to_drang(GetLastError()));
|
||||
}
|
||||
fs_dir->first_entry = true;
|
||||
DRANG_RETURN_IN(out_directory, fs_dir);
|
||||
|
||||
DRANG_CATCH(_)
|
||||
{
|
||||
if (fs_dir) {
|
||||
if (fs_dir->handle != INVALID_HANDLE_VALUE) {
|
||||
FindClose(fs_dir->handle);
|
||||
}
|
||||
drang_free(fs_dir);
|
||||
}
|
||||
}
|
||||
|
||||
DRANG_END_TRY()
|
||||
}
|
||||
|
||||
static int skip_invalid_entries(struct drang_fs_directory *dir)
|
||||
{
|
||||
DRANG_BEGIN_TRY()
|
||||
|
||||
while (strcmp(dir->find_data.cFileName, ".") == 0 || strcmp(dir->find_data.cFileName, "..") == 0) {
|
||||
if (!FindNextFileA(dir->handle, &dir->find_data)) {
|
||||
const DWORD err = GetLastError();
|
||||
if (err == ERROR_NO_MORE_FILES) {
|
||||
DRANG_FAIL(DRANG_ENOENT);
|
||||
}
|
||||
DRANG_FAIL(win32_error_to_drang(err));
|
||||
}
|
||||
}
|
||||
|
||||
DRANG_END_TRY_IGNORE()
|
||||
}
|
||||
|
||||
int drang_fs_readdir(drang_fs_directory_t *directory, drang_fs_dir_entry_t *out_entry)
|
||||
{
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(directory, DRANG_EINVAL);
|
||||
DRANG_FAIL_IF_NULL(out_entry, DRANG_EINVAL);
|
||||
|
||||
if (directory->first_entry) {
|
||||
directory->first_entry = false;
|
||||
DRANG_TRY(skip_invalid_entries(directory));
|
||||
} else {
|
||||
if (!FindNextFileA(directory->handle, &directory->find_data)) {
|
||||
const DWORD err = GetLastError();
|
||||
if (err == ERROR_NO_MORE_FILES) {
|
||||
DRANG_FAIL(DRANG_ENOENT);
|
||||
}
|
||||
DRANG_FAIL(win32_error_to_drang(err));
|
||||
}
|
||||
|
||||
DRANG_TRY(skip_invalid_entries(directory));
|
||||
}
|
||||
|
||||
strncpy(out_entry->name, directory->find_data.cFileName, DRANG_FS_MAX_REL_PATH_SIZE);
|
||||
out_entry->name[DRANG_FS_MAX_REL_PATH_SIZE - 1] = '\0';
|
||||
if (directory->find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
out_entry->type = drang_fs_type_directory;
|
||||
} else if (directory->find_data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
|
||||
out_entry->type = drang_fs_type_symlink;
|
||||
} else {
|
||||
out_entry->type = drang_fs_type_file;
|
||||
}
|
||||
|
||||
DRANG_END_TRY_IGNORE()
|
||||
}
|
||||
|
||||
int drang_fs_closedir(drang_fs_directory_t *directory)
|
||||
{
|
||||
DRANG_BEGIN_TRY()
|
||||
if (directory) {
|
||||
if (directory->handle != INVALID_HANDLE_VALUE) {
|
||||
if (!FindClose(directory->handle)) {
|
||||
DRANG_FAIL(win32_error_to_drang(GetLastError()));
|
||||
}
|
||||
}
|
||||
drang_free(directory);
|
||||
}
|
||||
DRANG_END_TRY_IGNORE()
|
||||
}
|
||||
|
||||
int drang_fs_get_cwd(char *buffer, size_t size, size_t *out_size)
|
||||
{
|
||||
DWORD length = 0;
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(buffer, DRANG_EINVAL);
|
||||
|
||||
length = GetCurrentDirectoryA((DWORD) size, buffer);
|
||||
if (length == 0) {
|
||||
DRANG_FAIL(win32_error_to_drang(GetLastError()));
|
||||
}
|
||||
|
||||
if (out_size) {
|
||||
*out_size = length;
|
||||
}
|
||||
|
||||
DRANG_CHECK(length < size, DRANG_ENOMEM);
|
||||
|
||||
DRANG_END_TRY_IGNORE()
|
||||
}
|
||||
|
||||
int drang_fs_set_cwd(const char *path)
|
||||
{
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(path, DRANG_EINVAL);
|
||||
|
||||
if (!SetCurrentDirectoryA(path)) {
|
||||
DRANG_FAIL(win32_error_to_drang(GetLastError()));
|
||||
}
|
||||
|
||||
DRANG_END_TRY_IGNORE()
|
||||
}
|
||||
442
Source/DrangPlatform/Source/win32/fs/file.c
Normal file
442
Source/DrangPlatform/Source/win32/fs/file.c
Normal file
|
|
@ -0,0 +1,442 @@
|
|||
#include "drang/sync.h"
|
||||
#include "internal.h"
|
||||
#include "win32/common.h"
|
||||
#include <drang/alloc.h>
|
||||
#include <drang/error.h>
|
||||
|
||||
static void mode_to_access(drang_fs_mode_t mode, DWORD *out_access, DWORD *out_creation)
|
||||
{
|
||||
DWORD access = 0;
|
||||
DWORD creation = 0;
|
||||
if (mode & drang_fs_mode_read) {
|
||||
access |= GENERIC_READ;
|
||||
}
|
||||
if (mode & drang_fs_mode_write) {
|
||||
access |= GENERIC_WRITE;
|
||||
}
|
||||
if (mode & drang_fs_mode_append) {
|
||||
access |= FILE_APPEND_DATA;
|
||||
}
|
||||
if (mode & drang_fs_mode_create) {
|
||||
if (mode & drang_fs_mode_exclusive) {
|
||||
creation = CREATE_NEW;
|
||||
} else {
|
||||
creation = OPEN_ALWAYS;
|
||||
}
|
||||
} else if (mode & drang_fs_mode_truncate) {
|
||||
creation = TRUNCATE_EXISTING;
|
||||
} else {
|
||||
creation = OPEN_EXISTING;
|
||||
}
|
||||
*out_access = access;
|
||||
*out_creation = creation;
|
||||
}
|
||||
|
||||
char drang_fs_path_separator(void)
|
||||
{
|
||||
return '\\';
|
||||
}
|
||||
|
||||
int drang_fs_open(const char *path, drang_fs_mode_t mode, drang_fs_file_t **out_file)
|
||||
{
|
||||
struct drang_fs_file *file = NULL;
|
||||
HANDLE handle = INVALID_HANDLE_VALUE;
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(path, DRANG_EINVAL);
|
||||
DRANG_FAIL_IF_NULL(out_file, DRANG_EINVAL);
|
||||
|
||||
DWORD access = 0;
|
||||
DWORD creation = 0;
|
||||
mode_to_access(mode, &access, &creation);
|
||||
handle = CreateFileA(path,
|
||||
access,
|
||||
0, //TODO: Implement sharing
|
||||
NULL,
|
||||
creation,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
DRANG_FAIL(win32_error_to_drang(GetLastError()));
|
||||
}
|
||||
|
||||
file = DRANG_TRY_CALLOC_T(struct drang_fs_file);
|
||||
file->handle = handle;
|
||||
file->mode = mode;
|
||||
DRANG_TRY(drang_rwlock_init(&file->lock));
|
||||
*out_file = file;
|
||||
|
||||
DRANG_CATCH(_)
|
||||
{
|
||||
if (handle != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(handle);
|
||||
}
|
||||
if (file) {
|
||||
drang_rwlock_fini(&file->lock);
|
||||
drang_free(file);
|
||||
}
|
||||
}
|
||||
DRANG_END_TRY()
|
||||
}
|
||||
|
||||
int drang_fs_close(drang_fs_file_t *file)
|
||||
{
|
||||
bool locked = false;
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(file, DRANG_EINVAL);
|
||||
|
||||
DRANG_TRY(drang_rwlock_wrlock(&file->lock));
|
||||
locked = true;
|
||||
if (file->handle != INVALID_HANDLE_VALUE) {
|
||||
DRANG_CHECK(CloseHandle(file->handle), win32_error_to_drang(GetLastError()));
|
||||
file->handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
drang_rwlock_fini(&file->lock);
|
||||
locked = false;
|
||||
drang_free(file);
|
||||
|
||||
DRANG_CATCH(_)
|
||||
{
|
||||
if (locked) {
|
||||
drang_rwlock_wrunlock(&file->lock);
|
||||
}
|
||||
}
|
||||
|
||||
DRANG_END_TRY()
|
||||
}
|
||||
|
||||
int drang_fs_read(drang_fs_file_t *file, void *buffer, size_t count, size_t size, size_t *bytes_read)
|
||||
{
|
||||
bool locked = false;
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(file, DRANG_EINVAL);
|
||||
DRANG_FAIL_IF_NULL(buffer, DRANG_EINVAL);
|
||||
if (count == 0 || size == 0) {
|
||||
if (bytes_read)
|
||||
*bytes_read = 0;
|
||||
DRANG_RETURN();
|
||||
}
|
||||
DRANG_CHECK(file->mode & drang_fs_mode_read, DRANG_EPERM);
|
||||
|
||||
DRANG_TRY(drang_rwlock_rdlock(&file->lock));
|
||||
locked = true;
|
||||
DWORD total_bytes = 0;
|
||||
DRANG_CHECK(!DRANG_MUL_OVERFLOW(count, size, &total_bytes), DRANG_EOVERFLOW);
|
||||
|
||||
DWORD read_bytes = 0;
|
||||
if (!ReadFile(file->handle, buffer, total_bytes, &read_bytes, NULL)) {
|
||||
DRANG_FAIL(win32_error_to_drang(GetLastError()));
|
||||
}
|
||||
|
||||
if (bytes_read) {
|
||||
*bytes_read = (size_t) read_bytes;
|
||||
}
|
||||
|
||||
drang_rwlock_rdunlock(&file->lock);
|
||||
locked = false;
|
||||
|
||||
DRANG_CATCH(_)
|
||||
{
|
||||
if (locked) {
|
||||
drang_rwlock_rdunlock(&file->lock);
|
||||
}
|
||||
}
|
||||
|
||||
DRANG_END_TRY()
|
||||
}
|
||||
|
||||
int drang_fs_write(drang_fs_file_t *file, const void *buffer, size_t count, size_t size, size_t *bytes_written)
|
||||
{
|
||||
bool locked = false;
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(file, DRANG_EINVAL);
|
||||
DRANG_FAIL_IF_NULL(buffer, DRANG_EINVAL);
|
||||
if (count == 0 || size == 0) {
|
||||
if (bytes_written)
|
||||
*bytes_written = 0;
|
||||
DRANG_RETURN();
|
||||
}
|
||||
DRANG_CHECK(file->mode & (drang_fs_mode_write | drang_fs_mode_append), DRANG_EPERM);
|
||||
DRANG_TRY(drang_rwlock_wrlock(&file->lock));
|
||||
locked = true;
|
||||
DWORD total_bytes = 0;
|
||||
DRANG_CHECK(!DRANG_MUL_OVERFLOW(count, size, &total_bytes), DRANG_EOVERFLOW);
|
||||
if (file->mode & drang_fs_mode_append) {
|
||||
const LARGE_INTEGER zero = {0};
|
||||
if (!SetFilePointerEx(file->handle, zero, NULL, FILE_END)) {
|
||||
DRANG_FAIL(win32_error_to_drang(GetLastError()));
|
||||
}
|
||||
}
|
||||
DWORD written_bytes = 0;
|
||||
if (!WriteFile(file->handle, buffer, total_bytes, &written_bytes, NULL)) {
|
||||
DRANG_FAIL(win32_error_to_drang(GetLastError()));
|
||||
}
|
||||
if (bytes_written) {
|
||||
*bytes_written = (size_t) written_bytes;
|
||||
}
|
||||
drang_rwlock_wrunlock(&file->lock);
|
||||
locked = false;
|
||||
DRANG_CATCH(_)
|
||||
{
|
||||
if (locked) {
|
||||
drang_rwlock_wrunlock(&file->lock);
|
||||
}
|
||||
}
|
||||
DRANG_END_TRY()
|
||||
}
|
||||
|
||||
int drang_fs_seek(drang_fs_file_t *file, int64_t offset, drang_seek_origin_t origin)
|
||||
{
|
||||
bool locked = false;
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(file, DRANG_EINVAL);
|
||||
DRANG_TRY(drang_rwlock_wrlock(&file->lock));
|
||||
locked = true;
|
||||
DWORD move_method = 0;
|
||||
switch (origin) {
|
||||
case drang_seek_origin_begin:
|
||||
move_method = FILE_BEGIN;
|
||||
break;
|
||||
case drang_seek_origin_current:
|
||||
move_method = FILE_CURRENT;
|
||||
break;
|
||||
case drang_seek_origin_end:
|
||||
move_method = FILE_END;
|
||||
break;
|
||||
}
|
||||
LARGE_INTEGER li;
|
||||
li.QuadPart = offset;
|
||||
if (!SetFilePointerEx(file->handle, li, NULL, move_method)) {
|
||||
DRANG_FAIL(win32_error_to_drang(GetLastError()));
|
||||
}
|
||||
drang_rwlock_wrunlock(&file->lock);
|
||||
locked = false;
|
||||
DRANG_CATCH(_)
|
||||
{
|
||||
if (locked) {
|
||||
drang_rwlock_wrunlock(&file->lock);
|
||||
}
|
||||
}
|
||||
DRANG_END_TRY()
|
||||
}
|
||||
|
||||
int drang_fs_tell(drang_fs_file_t *file, uint64_t *position)
|
||||
{
|
||||
bool locked = false;
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(file, DRANG_EINVAL);
|
||||
DRANG_FAIL_IF_NULL(position, DRANG_EINVAL);
|
||||
DRANG_TRY(drang_rwlock_rdlock(&file->lock));
|
||||
locked = true;
|
||||
LARGE_INTEGER zero = {0};
|
||||
LARGE_INTEGER pos = {0};
|
||||
if (!SetFilePointerEx(file->handle, zero, &pos, FILE_CURRENT)) {
|
||||
DRANG_FAIL(win32_error_to_drang(GetLastError()));
|
||||
}
|
||||
*position = (uint64_t) pos.QuadPart;
|
||||
drang_rwlock_rdunlock(&file->lock);
|
||||
locked = false;
|
||||
DRANG_CATCH(_)
|
||||
{
|
||||
if (locked) {
|
||||
drang_rwlock_rdunlock(&file->lock);
|
||||
}
|
||||
}
|
||||
DRANG_END_TRY()
|
||||
}
|
||||
|
||||
int drang_fs_flush(drang_fs_file_t *file)
|
||||
{
|
||||
bool locked = false;
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(file, DRANG_EINVAL);
|
||||
DRANG_CHECK(file->mode & (drang_fs_mode_write | drang_fs_mode_append), DRANG_EPERM);
|
||||
DRANG_TRY(drang_rwlock_wrlock(&file->lock));
|
||||
locked = true;
|
||||
if (!FlushFileBuffers(file->handle)) {
|
||||
DRANG_FAIL(win32_error_to_drang(GetLastError()));
|
||||
}
|
||||
drang_rwlock_wrunlock(&file->lock);
|
||||
locked = false;
|
||||
DRANG_CATCH(_)
|
||||
{
|
||||
if (locked) {
|
||||
drang_rwlock_wrunlock(&file->lock);
|
||||
}
|
||||
}
|
||||
DRANG_END_TRY()
|
||||
}
|
||||
int drang_fs_truncate(drang_fs_file_t *file, uint64_t size)
|
||||
{
|
||||
bool locked = false;
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(file, DRANG_EINVAL);
|
||||
DRANG_CHECK(file->mode & drang_fs_mode_write, DRANG_EPERM);
|
||||
DRANG_TRY(drang_rwlock_wrlock(&file->lock));
|
||||
locked = true;
|
||||
LARGE_INTEGER li;
|
||||
li.QuadPart = (LONGLONG) size;
|
||||
if (!SetFilePointerEx(file->handle, li, NULL, FILE_BEGIN)) {
|
||||
DRANG_FAIL(win32_error_to_drang(GetLastError()));
|
||||
}
|
||||
if (!SetEndOfFile(file->handle)) {
|
||||
DRANG_FAIL(win32_error_to_drang(GetLastError()));
|
||||
}
|
||||
drang_rwlock_wrunlock(&file->lock);
|
||||
locked = false;
|
||||
DRANG_CATCH(_)
|
||||
{
|
||||
if (locked) {
|
||||
drang_rwlock_wrunlock(&file->lock);
|
||||
}
|
||||
}
|
||||
DRANG_END_TRY()
|
||||
}
|
||||
|
||||
int drang_fs_create(const char *path, drang_fs_permissions_t permissions)
|
||||
{
|
||||
HANDLE handle = INVALID_HANDLE_VALUE;
|
||||
DRANG_UNUSED(permissions);
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(path, DRANG_EINVAL);
|
||||
|
||||
// Create the file with CREATE_NEW to fail if it already exists
|
||||
handle = CreateFileA(path,
|
||||
GENERIC_WRITE, // Need write access to create
|
||||
0, // No sharing during creation
|
||||
NULL, // Default security attributes
|
||||
CREATE_NEW, // Fail if file already exists
|
||||
FILE_ATTRIBUTE_NORMAL, // Normal file attributes
|
||||
NULL); // No template file
|
||||
|
||||
DRANG_CHECK(handle != INVALID_HANDLE_VALUE, win32_error_to_drang(GetLastError()));
|
||||
|
||||
// Close the file handle immediately since we're just creating an empty file
|
||||
CloseHandle(handle);
|
||||
|
||||
// Note: Windows doesn't support Unix-style permissions directly.
|
||||
// The permissions parameter is ignored on Windows as documented behavior
|
||||
// may vary between platforms. For a full implementation, we could use
|
||||
// SetFileSecurity() with ACLs, but that's complex and not typically needed.
|
||||
|
||||
DRANG_CATCH(_)
|
||||
{
|
||||
if (handle != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(handle);
|
||||
}
|
||||
}
|
||||
DRANG_END_TRY()
|
||||
}
|
||||
|
||||
int drang_fs_remove(const char *path)
|
||||
{
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(path, DRANG_EINVAL);
|
||||
|
||||
if (!DeleteFileA(path)) {
|
||||
DRANG_FAIL(win32_error_to_drang(GetLastError()));
|
||||
}
|
||||
|
||||
// No cleanup needed for this function
|
||||
DRANG_END_TRY_IGNORE()
|
||||
}
|
||||
|
||||
int drang_fs_move(const char *old_path, const char *new_path)
|
||||
{
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(old_path, DRANG_EINVAL);
|
||||
DRANG_FAIL_IF_NULL(new_path, DRANG_EINVAL);
|
||||
if (!MoveFileA(old_path, new_path)) {
|
||||
DRANG_FAIL(win32_error_to_drang(GetLastError()));
|
||||
}
|
||||
DRANG_END_TRY_IGNORE()
|
||||
}
|
||||
|
||||
bool drang_fs_exists(const char *path)
|
||||
{
|
||||
if (path == NULL)
|
||||
return false;
|
||||
const DWORD attrs = GetFileAttributesA(path);
|
||||
if (attrs == INVALID_FILE_ATTRIBUTES) {
|
||||
false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int drang_fs_stat(const char *path, struct drang_fs_stat *out_stat)
|
||||
{
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(path, DRANG_EINVAL);
|
||||
DRANG_FAIL_IF_NULL(out_stat, DRANG_EINVAL);
|
||||
|
||||
WIN32_FILE_ATTRIBUTE_DATA file_data;
|
||||
if (!GetFileAttributesExA(path, GetFileExInfoStandard, &file_data)) {
|
||||
DRANG_FAIL(win32_error_to_drang(GetLastError()));
|
||||
}
|
||||
|
||||
// Determine file type
|
||||
if (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
out_stat->type = drang_fs_type_directory;
|
||||
out_stat->size = 0; // Directories have no size
|
||||
} else if (file_data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
|
||||
// This might be a symlink, but we need to check further
|
||||
out_stat->type = drang_fs_type_symlink;
|
||||
out_stat->size = ((uint64_t) file_data.nFileSizeHigh << 32) | file_data.nFileSizeLow;
|
||||
} else {
|
||||
out_stat->type = drang_fs_type_file;
|
||||
out_stat->size = ((uint64_t) file_data.nFileSizeHigh << 32) | file_data.nFileSizeLow;
|
||||
}
|
||||
|
||||
// Convert Windows permissions to Unix-style permissions
|
||||
// Windows doesn't have the same permission model, so we'll approximate
|
||||
out_stat->permissions = DRANG_FS_PERM_USER_READ | DRANG_FS_PERM_GROUP_READ | DRANG_FS_PERM_OTHER_READ;
|
||||
|
||||
if (!(file_data.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
|
||||
out_stat->permissions |= DRANG_FS_PERM_USER_WRITE | DRANG_FS_PERM_GROUP_WRITE | DRANG_FS_PERM_OTHER_WRITE;
|
||||
}
|
||||
|
||||
// For directories and executables, add execute permissions
|
||||
if ((file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
|| (strlen(path) > 4
|
||||
&& (_stricmp(path + strlen(path) - 4, ".exe") == 0 || _stricmp(path + strlen(path) - 4, ".bat") == 0
|
||||
|| _stricmp(path + strlen(path) - 4, ".cmd") == 0))) {
|
||||
out_stat->permissions |= DRANG_FS_PERM_USER_EXEC | DRANG_FS_PERM_GROUP_EXEC | DRANG_FS_PERM_OTHER_EXEC;
|
||||
}
|
||||
|
||||
// Convert FILETIME to Unix timestamp (seconds since epoch)
|
||||
// Windows FILETIME is 100-nanosecond intervals since January 1, 1601
|
||||
// Unix timestamp is seconds since January 1, 1970
|
||||
const uint64_t WINDOWS_EPOCH_DIFF = 11644473600ULL; // Seconds between 1601 and 1970
|
||||
|
||||
// Convert creation time
|
||||
uint64_t created_100ns = ((uint64_t) file_data.ftCreationTime.dwHighDateTime << 32)
|
||||
| file_data.ftCreationTime.dwLowDateTime;
|
||||
out_stat->created_time = (created_100ns / 10000000ULL) - WINDOWS_EPOCH_DIFF;
|
||||
|
||||
// Convert modification time
|
||||
uint64_t modified_100ns = ((uint64_t) file_data.ftLastWriteTime.dwHighDateTime << 32)
|
||||
| file_data.ftLastWriteTime.dwLowDateTime;
|
||||
out_stat->modified_time = (modified_100ns / 10000000ULL) - WINDOWS_EPOCH_DIFF;
|
||||
|
||||
// Convert access time
|
||||
uint64_t accessed_100ns = ((uint64_t) file_data.ftLastAccessTime.dwHighDateTime << 32)
|
||||
| file_data.ftLastAccessTime.dwLowDateTime;
|
||||
out_stat->accessed_time = (accessed_100ns / 10000000ULL) - WINDOWS_EPOCH_DIFF;
|
||||
|
||||
DRANG_END_TRY_IGNORE()
|
||||
}
|
||||
|
||||
int drang_fs_set_permissions(const char *path, drang_fs_permissions_t permissions)
|
||||
{
|
||||
DRANG_BEGIN_TRY()
|
||||
DRANG_FAIL_IF_NULL(path, DRANG_EINVAL);
|
||||
|
||||
// Windows doesn't support Unix-style permissions directly.
|
||||
// The permissions parameter is ignored on Windows as documented behavior
|
||||
// may vary between platforms. For a full implementation, we could use
|
||||
// SetFileSecurity() with ACLs, but that's complex and not typically needed.
|
||||
DRANG_UNUSED(permissions);
|
||||
|
||||
DRANG_END_TRY_IGNORE()
|
||||
}
|
||||
20
Source/DrangPlatform/Source/win32/fs/internal.h
Normal file
20
Source/DrangPlatform/Source/win32/fs/internal.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
#include <drang/fs.h>
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
|
||||
#include "../sync/internal.h"
|
||||
|
||||
struct drang_fs_file
|
||||
{
|
||||
HANDLE handle;
|
||||
drang_fs_mode_t mode;
|
||||
struct drang_rwlock lock;
|
||||
};
|
||||
|
||||
struct drang_fs_directory
|
||||
{
|
||||
HANDLE handle;
|
||||
WIN32_FIND_DATAA find_data;
|
||||
bool first_entry;
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue