blob: 4ad08ef2c84096f84cfb364650614bb72b5c000b [file] [log] [blame]
/* ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
Copyright (c) 2014-2015 Datalight, Inc.
All Rights Reserved Worldwide.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; use version 2 of the License.
This program is distributed in the hope that it will be useful,
but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* Businesses and individuals that for commercial or other reasons cannot
comply with the terms of the GPLv2 license may obtain a commercial license
before incorporating Reliance Edge into proprietary software for
distribution in any form. Visit http://www.datalight.com/reliance-edge for
more information.
*/
/** @file
@brief Implementation of the Reliance Edge FSE API.
*/
#include <redfs.h>
#if REDCONF_API_FSE == 1
/** @defgroup red_group_fse The File System Essentials Interface
@{
*/
#include <redvolume.h>
#include <redcoreapi.h>
#include <redfse.h>
static REDSTATUS FseEnter(uint8_t bVolNum);
static void FseLeave(void);
static bool gfFseInited; /* Whether driver is initialized. */
/** @brief Initialize the Reliance Edge file system driver.
Prepares the Reliance Edge file system driver to be used. Must be the first
Reliance Edge function to be invoked: no volumes can be mounted until the
driver has been initialized.
If this function is called when the Reliance Edge driver is already
initialized, it does nothing and returns success.
This function is not thread safe: attempting to initialize from multiple
threads could leave things in a bad state.
@return A negated ::REDSTATUS code indicating the operation result.
@retval 0 Operation was successful.
*/
REDSTATUS RedFseInit(void)
{
REDSTATUS ret;
if(gfFseInited)
{
ret = 0;
}
else
{
ret = RedCoreInit();
if(ret == 0)
{
gfFseInited = true;
}
}
return ret;
}
/** @brief Uninitialize the Reliance Edge file system driver.
Tears down the Reliance Edge file system driver. Cannot be used until all
Reliance Edge volumes are unmounted. A subsequent call to RedFseInit()
will initialize the driver again.
If this function is called when the Reliance Edge driver is already
uninitialized, it does nothing and returns success.
This function is not thread safe: attempting to uninitialize from multiple
threads could leave things in a bad state.
@return A negated ::REDSTATUS code indicating the operation result.
@retval 0 Operation was successful.
@retval -RED_EBUSY At least one volume is still mounted.
*/
REDSTATUS RedFseUninit(void)
{
REDSTATUS ret = 0;
if(!gfFseInited)
{
ret = 0;
}
else
{
uint8_t bVolNum;
#if REDCONF_TASK_COUNT > 1U
RedOsMutexAcquire();
#endif
for(bVolNum = 0U; bVolNum < REDCONF_VOLUME_COUNT; bVolNum++)
{
if(gaRedVolume[bVolNum].fMounted)
{
ret = -RED_EBUSY;
break;
}
}
if(ret == 0)
{
gfFseInited = false;
}
#if REDCONF_TASK_COUNT > 1U
RedOsMutexRelease();
#endif
if(ret == 0)
{
ret = RedCoreUninit();
}
}
return ret;
}
/** @brief Mount a file system volume.
Prepares the file system volume to be accessed. Mount will fail if the
volume has never been formatted, or if the on-disk format is inconsistent
with the compile-time configuration.
If the volume is already mounted, this function does nothing and returns
success.
@param bVolNum The volume number of the volume to be mounted.
@return A negated ::REDSTATUS code indicating the operation result.
@retval 0 Operation was successful.
@retval -RED_EINVAL @p bVolNum is an invalid volume number; or the driver is
uninitialized.
@retval -RED_EIO Volume not formatted, improperly formatted, or corrupt.
*/
REDSTATUS RedFseMount(
uint8_t bVolNum)
{
REDSTATUS ret;
ret = FseEnter(bVolNum);
if(ret == 0)
{
if(!gpRedVolume->fMounted)
{
ret = RedCoreVolMount();
}
FseLeave();
}
return ret;
}
/** @brief Unmount a file system volume.
This function discards the in-memory state for the file system and marks it
as unmounted. Subsequent attempts to access the volume will fail until the
volume is mounted again.
If unmount automatic transaction points are enabled, this function will
commit a transaction point prior to unmounting. If unmount automatic
transaction points are disabled, this function will unmount without
transacting, effectively discarding the working state.
Before unmounting, this function will wait for any active file system
thread to complete by acquiring the FS mutex. The volume will be marked as
unmounted before the FS mutex is released, so subsequent FS threads will
possibly block and then see an error when attempting to access a volume
which is unmounting or unmounted.
If the volume is already unmounted, this function does nothing and returns
success.
@param bVolNum The volume number of the volume to be unmounted.
@return A negated ::REDSTATUS code indicating the operation result.
@retval 0 Operation was successful.
@retval -RED_EINVAL @p bVolNum is an invalid volume number; or the driver is
uninitialized.
@retval -RED_EIO I/O error during unmount automatic transaction point.
*/
REDSTATUS RedFseUnmount(
uint8_t bVolNum)
{
REDSTATUS ret;
ret = FseEnter(bVolNum);
if(ret == 0)
{
if(gpRedVolume->fMounted)
{
ret = RedCoreVolUnmount();
}
FseLeave();
}
return ret;
}
#if (REDCONF_READ_ONLY == 0) && (REDCONF_API_FSE_FORMAT == 1)
/** @brief Format a file system volume.
Uses the statically defined volume configuration. After calling this
function, the volume needs to be mounted -- see RedFseMount().
An error is returned if the volume is mounted.
@param bVolNum The volume number of the volume to be formatted.
@return A negated ::REDSTATUS code indicating the operation result.
@retval 0 Operation was successful.
@retval -RED_EBUSY The volume is mounted.
@retval -RED_EINVAL @p bVolNum is an invalid volume number; or the driver is
uninitialized.
@retval -RED_EIO I/O error formatting the volume.
*/
REDSTATUS RedFseFormat(
uint8_t bVolNum)
{
REDSTATUS ret;
ret = FseEnter(bVolNum);
if(ret == 0)
{
ret = RedCoreVolFormat();
FseLeave();
}
return ret;
}
#endif
/** @brief Read from a file.
Data which has not yet been written, but which is before the end-of-file
(sparse data), shall read as zeroes. A short read -- where the number of
bytes read is less than requested -- indicates that the requested read was
partially or, if zero bytes were read, entirely beyond the end-of-file.
If @p ullFileOffset is at or beyond the maximum file size, it is treated
like any other read entirely beyond the end-of-file: no data is read and
zero is returned.
@param bVolNum The volume number of the file to read.
@param ulFileNum The file number of the file to read.
@param ullFileOffset The file offset to read from.
@param ulLength The number of bytes to read.
@param pBuffer The buffer to populate with the data read. Must be
at least ulLength bytes in size.
@return The number of bytes read (nonnegative) or a negated ::REDSTATUS
code indicating the operation result (negative).
@retval >=0 The number of bytes read from the file.
@retval -RED_EBADF @p ulFileNum is not a valid file number.
@retval -RED_EINVAL @p bVolNum is an invalid volume number or not mounted;
or @p pBuffer is `NULL`; or @p ulLength exceeds
INT32_MAX and cannot be returned properly.
@retval -RED_EIO A disk I/O error occurred.
*/
int32_t RedFseRead(
uint8_t bVolNum,
uint32_t ulFileNum,
uint64_t ullFileOffset,
uint32_t ulLength,
void *pBuffer)
{
int32_t ret;
if(ulLength > (uint32_t)INT32_MAX)
{
ret = -RED_EINVAL;
}
else
{
ret = FseEnter(bVolNum);
}
if(ret == 0)
{
uint32_t ulReadLen = ulLength;
ret = RedCoreFileRead(ulFileNum, ullFileOffset, &ulReadLen, pBuffer);
FseLeave();
if(ret == 0)
{
ret = (int32_t)ulReadLen;
}
}
return ret;
}
#if REDCONF_READ_ONLY == 0
/** @brief Write to a file.
If the write extends beyond the end-of-file, the file size will be
increased.
A short write -- where the number of bytes written is less than requested
-- indicates either that the file system ran out of space but was still
able to write some of the request; or that the request would have caused
the file to exceed the maximum file size, but some of the data could be
written prior to the file size limit.
If an error is returned (negative return), either none of the data was
written or a critical error occurred (like an I/O error) and the file
system volume will be read-only.
@param bVolNum The volume number of the file to write.
@param ulFileNum The file number of the file to write.
@param ullFileOffset The file offset to write at.
@param ulLength The number of bytes to write.
@param pBuffer The buffer containing the data to be written. Must
be at least ulLength bytes in size.
@return The number of bytes written (nonnegative) or a negated ::REDSTATUS
code indicating the operation result (negative).
@retval >0 The number of bytes written to the file.
@retval -RED_EBADF @p ulFileNum is not a valid file number.
@retval -RED_EFBIG No data can be written to the given file offset since
the resulting file size would exceed the maximum file
size.
@retval -RED_EINVAL @p bVolNum is an invalid volume number or not mounted;
or @p pBuffer is `NULL`; or @p ulLength exceeds
INT32_MAX and cannot be returned properly.
@retval -RED_EIO A disk I/O error occurred.
@retval -RED_ENOSPC No data can be written because there is insufficient
free space.
@retval -RED_EROFS The file system volume is read-only.
*/
int32_t RedFseWrite(
uint8_t bVolNum,
uint32_t ulFileNum,
uint64_t ullFileOffset,
uint32_t ulLength,
const void *pBuffer)
{
int32_t ret;
if(ulLength > (uint32_t)INT32_MAX)
{
ret = -RED_EINVAL;
}
else
{
ret = FseEnter(bVolNum);
}
if(ret == 0)
{
uint32_t ulWriteLen = ulLength;
ret = RedCoreFileWrite(ulFileNum, ullFileOffset, &ulWriteLen, pBuffer);
FseLeave();
if(ret == 0)
{
ret = (int32_t)ulWriteLen;
}
}
return ret;
}
#endif
#if (REDCONF_READ_ONLY == 0) && (REDCONF_API_FSE_TRUNCATE == 1)
/** @brief Truncate a file (set the file size).
Allows the file size to be increased, decreased, or to remain the same. If
the file size is increased, the new area is sparse (will read as zeroes).
If the file size is decreased, the data beyond the new end-of-file will
return to free space once it is no longer part of the committed state
(either immediately or after the next transaction point).
This function can fail when the disk is full if @p ullNewFileSize is
non-zero. If decreasing the file size, this can be fixed by transacting and
trying again: Reliance Edge guarantees that it is possible to perform a
truncate of at least one file that decreases the file size after a
transaction point. If disk full transactions are enabled, this will happen
automatically.
@param bVolNum The volume number of the file to truncate.
@param ulFileNum The file number of the file to truncate.
@param ullNewFileSize The new file size, in bytes.
@return A negated ::REDSTATUS code indicating the operation result.
@retval 0 Operation was successful.
@retval -RED_EBADF @p ulFileNum is not a valid file number.
@retval -RED_EFBIG @p ullNewFileSize exceeds the maximum file size.
@retval -RED_EINVAL @p bVolNum is an invalid volume number or not mounted.
@retval -RED_EIO A disk I/O error occurred.
@retval -RED_ENOSPC Insufficient free space to perform the truncate.
@retval -RED_EROFS The file system volume is read-only.
*/
REDSTATUS RedFseTruncate(
uint8_t bVolNum,
uint32_t ulFileNum,
uint64_t ullNewFileSize)
{
REDSTATUS ret;
ret = FseEnter(bVolNum);
if(ret == 0)
{
ret = RedCoreFileTruncate(ulFileNum, ullNewFileSize);
FseLeave();
}
return ret;
}
#endif
/** @brief Retrieve the size of a file.
@param bVolNum The volume number of the file whose size is being read.
@param ulFileNum The file number of the file whose size is being read.
@return The size of the file (nonnegative) or a negated ::REDSTATUS code
indicating the operation result (negative).
@retval >=0 The size of the file.
@retval -RED_EBADF @p ulFileNum is not a valid file number.
@retval -RED_EINVAL @p bVolNum is an invalid volume number or not mounted.
@retval -RED_EIO A disk I/O error occurred.
*/
int64_t RedFseSizeGet(
uint8_t bVolNum,
uint32_t ulFileNum)
{
int64_t ret;
ret = FseEnter(bVolNum);
if(ret == 0)
{
uint64_t ullSize;
ret = RedCoreFileSizeGet(ulFileNum, &ullSize);
FseLeave();
if(ret == 0)
{
/* Unless there is an on-disk format change, the maximum file size
is guaranteed to be less than INT64_MAX, and so it can be safely
returned in an int64_t.
*/
REDASSERT(ullSize < (uint64_t)INT64_MAX);
ret = (int64_t)ullSize;
}
}
return ret;
}
#if (REDCONF_READ_ONLY == 0) && (REDCONF_API_FSE_TRANSMASKSET == 1)
/** @brief Update the transaction mask.
The following events are available:
- #RED_TRANSACT_UMOUNT
- #RED_TRANSACT_WRITE
- #RED_TRANSACT_TRUNCATE
- #RED_TRANSACT_VOLFULL
The #RED_TRANSACT_MANUAL macro (by itself) may be used to disable all
automatic transaction events. The #RED_TRANSACT_MASK macro is a bitmask of
all transaction flags, excluding those representing excluded functionality.
Attempting to enable events for excluded functionality will result in an
error.
@param bVolNum The volume number of the volume whose transaction mask
is being changed.
@param ulEventMask A bitwise-OR'd mask of automatic transaction events to
be set as the current transaction mode.
@return A negated ::REDSTATUS code indicating the operation result.
@retval 0 Operation was successful.
@retval -RED_EINVAL @p bVolNum is an invalid volume number or not mounted;
or @p ulEventMask contains invalid bits.
@retval -RED_EROFS The file system volume is read-only.
*/
REDSTATUS RedFseTransMaskSet(
uint8_t bVolNum,
uint32_t ulEventMask)
{
REDSTATUS ret;
ret = FseEnter(bVolNum);
if(ret == 0)
{
ret = RedCoreTransMaskSet(ulEventMask);
FseLeave();
}
return ret;
}
#endif
#if REDCONF_API_FSE_TRANSMASKGET == 1
/** @brief Read the transaction mask.
If the volume is read-only, the returned event mask is always zero.
@param bVolNum The volume number of the volume whose transaction mask
is being retrieved.
@param pulEventMask Populated with a bitwise-OR'd mask of automatic
transaction events which represent the current
transaction mode for the volume.
@return A negated ::REDSTATUS code indicating the operation result.
@retval 0 Operation was successful.
@retval -RED_EINVAL @p bVolNum is an invalid volume number or not mounted;
or @p pulEventMask is `NULL`.
*/
REDSTATUS RedFseTransMaskGet(
uint8_t bVolNum,
uint32_t *pulEventMask)
{
REDSTATUS ret;
ret = FseEnter(bVolNum);
if(ret == 0)
{
ret = RedCoreTransMaskGet(pulEventMask);
FseLeave();
}
return ret;
}
#endif
#if REDCONF_READ_ONLY == 0
/** @brief Commit a transaction point.
Reliance Edge is a transactional file system. All modifications, of both
metadata and filedata, are initially working state. A transaction point
is a process whereby the working state atomically becomes the committed
state, replacing the previous committed state. Whenever Reliance Edge is
mounted, including after power loss, the state of the file system after
mount is the most recent committed state. Nothing from the committed
state is ever missing, and nothing from the working state is ever included.
@param bVolNum The volume number of the volume to transact.
@return A negated ::REDSTATUS code indicating the operation result.
@retval 0 Operation was successful.
@retval -RED_EINVAL @p bVolNum is an invalid volume number or not mounted.
@retval -RED_EIO A disk I/O error occurred.
@retval -RED_EROFS The file system volume is read-only.
*/
REDSTATUS RedFseTransact(
uint8_t bVolNum)
{
REDSTATUS ret;
ret = FseEnter(bVolNum);
if(ret == 0)
{
ret = RedCoreVolTransact();
FseLeave();
}
return ret;
}
#endif
/** @} */
/** @brief Enter the file system driver.
@param bVolNum The volume to be accessed.
@return A negated ::REDSTATUS code indicating the operation result.
@retval 0 Operation was successful.
@retval -RED_EINVAL The file system driver is uninitialized; or @p bVolNum
is not a valid volume number.
*/
static REDSTATUS FseEnter(
uint8_t bVolNum)
{
REDSTATUS ret;
if(gfFseInited)
{
#if REDCONF_TASK_COUNT > 1U
RedOsMutexAcquire();
#endif
/* This also serves to range-check the volume number (even in single
volume configurations).
*/
ret = RedCoreVolSetCurrent(bVolNum);
#if REDCONF_TASK_COUNT > 1U
if(ret != 0)
{
RedOsMutexRelease();
}
#endif
}
else
{
ret = -RED_EINVAL;
}
return ret;
}
/** @brief Leave the file system driver.
*/
static void FseLeave(void)
{
REDASSERT(gfFseInited);
#if REDCONF_TASK_COUNT > 1U
RedOsMutexRelease();
#endif
}
#endif /* REDCONF_API_FSE == 1 */