From b04c4ff80da4a09cbf091f73b7da2cb1e94c8f36 Mon Sep 17 00:00:00 2001 From: Joseph Hickey Date: Tue, 29 Dec 2020 11:10:38 -0500 Subject: [PATCH] Fix #573, add OS_FileSysStatVolume Add OS_FileSysStatVolume as replacement for OS_fsBytesFree and OS_fsBlocksFree. Update unit tests and stubs for the new API call. Does not (yet) deprecate the existing functions, as references still need to be updated elsewhere in apps. --- src/os/inc/osapi-filesys.h | 37 ++++++ src/os/shared/inc/os-shared-filesys.h | 16 --- src/os/shared/src/osapi-filesys.c | 31 +++++ src/tests/file-api-test/file-api-test.c | 40 +++--- .../shared/src/coveragetest-filesys.c | 52 ++++++++ .../osfilesys-test/ut_osfilesys_diskio_test.c | 117 ++++++++++++++++++ .../osfilesys-test/ut_osfilesys_diskio_test.h | 1 + .../osfilesys-test/ut_osfilesys_test.c | 1 + src/ut-stubs/osapi-utstub-filesys.c | 22 ++++ 9 files changed, 283 insertions(+), 34 deletions(-) diff --git a/src/os/inc/osapi-filesys.h b/src/os/inc/osapi-filesys.h index 8b3d6797c..176243189 100644 --- a/src/os/inc/osapi-filesys.h +++ b/src/os/inc/osapi-filesys.h @@ -41,6 +41,19 @@ typedef struct uint32 FreeVolumes; /**< @brief Total number of volumes free */ } os_fsinfo_t; +/* + * @brief The data type filled in by the OS_FileSysStatVolume() call. + * + * Encapsulates detail information about the size and available space + * in a mounted file system volume. + */ +typedef struct +{ + size_t block_size; /**< Block size of underlying FS */ + osal_blockcount_t total_blocks; /**< Total blocks in underlying FS */ + osal_blockcount_t blocks_free; /**< Available blocks in underlying FS */ +} OS_statvfs_t; + /* * Exported Functions */ @@ -201,6 +214,30 @@ int32 OS_fsBlocksFree(const char *name); */ int32 OS_fsBytesFree(const char *name, uint64 *bytes_free); +/*-------------------------------------------------------------------------------------*/ +/** + * @brief Obtains information about size and free space in a volume + * + * Populates the supplied OS_statvfs_t structure, which includes + * the block size and total/free blocks in a file system volume. + * + * This replaces two older OSAL calls: + * + * OS_fsBlocksFree() is determined by reading the blocks_free + * output struct member + * OS_fsBytesFree() is determined by multiplying blocks_free + * by the block_size member + * + * @param[in] name The device/path to operate on + * @param[out] statbuf Output structure to populate + * + * @return Execution status, see @ref OSReturnCodes + * @retval #OS_SUCCESS @copybrief OS_SUCCESS + * @retval #OS_INVALID_POINTER if name or statbuf is NULL + * @retval #OS_ERROR if the OS call failed + */ +int32 OS_FileSysStatVolume(const char *name, OS_statvfs_t *statbuf); + /*-------------------------------------------------------------------------------------*/ /** * @brief Checks the health of a file system and repairs it if necessary diff --git a/src/os/shared/inc/os-shared-filesys.h b/src/os/shared/inc/os-shared-filesys.h index e00058301..717699f1f 100644 --- a/src/os/shared/inc/os-shared-filesys.h +++ b/src/os/shared/inc/os-shared-filesys.h @@ -84,22 +84,6 @@ enum OS_FILESYS_TYPE_MAX }; -/* - * The data type filled in by the "statvfs" call. - * - * This is defined here since there is no public API to get this info, - * only the total bytes free is accessible via the current OSAL API. - * - * However, returning the detailed info at this level means that the - * more detailed information could be made available with a new API call. - */ -typedef struct -{ - size_t block_size; - osal_blockcount_t total_blocks; - osal_blockcount_t blocks_free; -} OS_statvfs_t; - typedef struct { char device_name[OS_FS_DEV_NAME_LEN]; /**< The name of the underlying block device, if applicable */ diff --git a/src/os/shared/src/osapi-filesys.c b/src/os/shared/src/osapi-filesys.c index bc8156307..71a98119e 100644 --- a/src/os/shared/src/osapi-filesys.c +++ b/src/os/shared/src/osapi-filesys.c @@ -601,6 +601,37 @@ int32 OS_fsBytesFree(const char *name, uint64 *bytes_free) } /* end OS_fsBytesFree */ +/*---------------------------------------------------------------- + * + * Function: OS_FileSysStatVolume + * + * Purpose: Implemented per public OSAL API + * See description in API and header file for detail + * + *-----------------------------------------------------------------*/ +int32 OS_FileSysStatVolume(const char *name, OS_statvfs_t *statbuf) +{ + int32 return_code; + OS_object_token_t token; + + /* Check parameters */ + OS_CHECK_PATHNAME(name); + OS_CHECK_POINTER(statbuf); + + return_code = OS_ObjectIdGetBySearch(OS_LOCK_MODE_GLOBAL, LOCAL_OBJID_TYPE, OS_FileSys_FindVirtMountPoint, + (void *)name, &token); + + if (return_code == OS_SUCCESS) + { + return_code = OS_FileSysStatVolume_Impl(&token, statbuf); + + OS_ObjectIdRelease(&token); + } + + return return_code; + +} /* end OS_FileSysStatVolume */ + /*---------------------------------------------------------------- * * Function: OS_chkfs diff --git a/src/tests/file-api-test/file-api-test.c b/src/tests/file-api-test/file-api-test.c index 1fc6b84a5..2ff84524f 100644 --- a/src/tests/file-api-test/file-api-test.c +++ b/src/tests/file-api-test/file-api-test.c @@ -424,18 +424,19 @@ void TestReadWriteLseek(void) ---------------------------------------------------------------------------------------*/ void TestMkRmDirFreeBytes(void) { - int32 status; - char filename1[OS_MAX_PATH_LEN]; - char filename2[OS_MAX_PATH_LEN]; - char dir1[OS_MAX_PATH_LEN]; - char dir2[OS_MAX_PATH_LEN]; - char buffer1[OS_MAX_PATH_LEN]; - char buffer2[OS_MAX_PATH_LEN]; - char copybuffer1[OS_MAX_PATH_LEN]; - char copybuffer2[OS_MAX_PATH_LEN]; - osal_id_t fd1; - osal_id_t fd2; - size_t size; + int32 status; + char filename1[OS_MAX_PATH_LEN]; + char filename2[OS_MAX_PATH_LEN]; + char dir1[OS_MAX_PATH_LEN]; + char dir2[OS_MAX_PATH_LEN]; + char buffer1[OS_MAX_PATH_LEN]; + char buffer2[OS_MAX_PATH_LEN]; + char copybuffer1[OS_MAX_PATH_LEN]; + char copybuffer2[OS_MAX_PATH_LEN]; + osal_id_t fd1; + osal_id_t fd2; + size_t size; + OS_statvfs_t statbuf; /* make the directory names for testing, as well as the filenames and the buffers * to put in the files */ @@ -450,8 +451,9 @@ void TestMkRmDirFreeBytes(void) /* NOTE: The blocks free call is not necessarily implemented on all filesystems. * So the response of OS_ERR_NOT_IMPLEMENTED is acceptable. */ - status = OS_fsBlocksFree("/drive0"); - UtAssert_True(status == OS_ERR_NOT_IMPLEMENTED || status >= OS_SUCCESS, "Checking Free Blocks: %d", (int)status); + status = OS_FileSysStatVolume("/drive0", &statbuf); + UtAssert_True(status == OS_ERR_NOT_IMPLEMENTED || status == OS_SUCCESS, "Checking Free Blocks: status=%d blocks=%lu", + (int)status, (unsigned long)statbuf.blocks_free); /* make the two directories */ status = OS_mkdir(dir1, 0); @@ -486,8 +488,9 @@ void TestMkRmDirFreeBytes(void) memset(buffer1, 0, sizeof(buffer1)); memset(buffer2, 0, sizeof(buffer2)); - status = OS_fsBlocksFree("/drive0"); - UtAssert_True(status == OS_ERR_NOT_IMPLEMENTED || status >= OS_SUCCESS, "Checking Free Blocks: %d", (int)status); + status = OS_FileSysStatVolume("/drive0", &statbuf); + UtAssert_True(status == OS_ERR_NOT_IMPLEMENTED || status == OS_SUCCESS, "Checking Free Blocks: status=%d blocks=%lu", + (int)status, (unsigned long)statbuf.blocks_free); /* read back out of the files what we wrote into them */ size = strlen(copybuffer1); @@ -526,8 +529,9 @@ void TestMkRmDirFreeBytes(void) status = OS_rmdir(dir2); UtAssert_True(status == OS_SUCCESS, "status after rmdir 2 = %d", (int)status); - status = OS_fsBlocksFree("/drive0"); - UtAssert_True(status == OS_ERR_NOT_IMPLEMENTED || status >= OS_SUCCESS, "Checking Free Blocks: %d", (int)status); + status = OS_FileSysStatVolume("/drive0", &statbuf); + UtAssert_True(status == OS_ERR_NOT_IMPLEMENTED || status == OS_SUCCESS, "Checking Free Blocks: status=%d blocks=%lu", + (int)status, (unsigned long)statbuf.blocks_free); } /*--------------------------------------------------------------------------------------- diff --git a/src/unit-test-coverage/shared/src/coveragetest-filesys.c b/src/unit-test-coverage/shared/src/coveragetest-filesys.c index eb744b0f3..d8cdf1b38 100644 --- a/src/unit-test-coverage/shared/src/coveragetest-filesys.c +++ b/src/unit-test-coverage/shared/src/coveragetest-filesys.c @@ -328,6 +328,57 @@ void Test_OS_fsBytesFree(void) UtAssert_True(actual == expected, "OS_fsBytesFree() (%ld) == OS_FS_ERR_PATH_INVALID", (long)actual); } +void Test_OS_FileSysStatVolume(void) +{ + /* + * Test Case For: + * int32 OS_FileSysStatVolume(const char *name, OS_statvfs_t *statbuf) + */ + + OS_statvfs_t statbuf; + OS_statvfs_t statref; + int32 expected; + int32 actual; + + statref.block_size = OSAL_SIZE_C(1024); + statref.blocks_free = OSAL_BLOCKCOUNT_C(1111); + statref.total_blocks = OSAL_BLOCKCOUNT_C(2222); + UT_SetDataBuffer(UT_KEY(OS_FileSysStatVolume_Impl), &statref, sizeof(statref), false); + OS_filesys_table[1].flags = OS_FILESYS_FLAG_IS_READY | OS_FILESYS_FLAG_IS_MOUNTED_SYSTEM | + OS_FILESYS_FLAG_IS_MOUNTED_VIRTUAL; + + expected = OS_SUCCESS; + actual = OS_FileSysStatVolume("/cf", &statbuf); + UtAssert_True(actual == expected, "OS_FileSysStatVolume() (%ld) == OS_SUCCESS", (long)actual); + + UtAssert_True(statbuf.block_size == statref.block_size, "blocks_size (%lu) == %lu", (unsigned long)statbuf.block_size, + (unsigned long)statref.block_size); + UtAssert_True(statbuf.total_blocks == statref.total_blocks, "total_blocks (%lu) == %lu", + (unsigned long)statbuf.total_blocks, (unsigned long)statref.total_blocks); + UtAssert_True(statbuf.blocks_free == statref.blocks_free, "blocks_free (%lu) == %lu", (unsigned long)statbuf.blocks_free, + (unsigned long)statref.blocks_free); + + /* validate error checking */ + expected = OS_INVALID_POINTER; + actual = OS_FileSysStatVolume(NULL, &statbuf); + UtAssert_True(actual == expected, "OS_FileSysStatVolume() (%ld) == OS_INVALID_POINTER", (long)actual); + actual = OS_FileSysStatVolume("/cf", NULL); + UtAssert_True(actual == expected, "OS_FileSysStatVolume() (%ld) == OS_INVALID_POINTER", (long)actual); + + /* Test Fail due to no matching VolTab entry */ + UT_SetDefaultReturnValue(UT_KEY(OS_ObjectIdGetBySearch), OS_ERR_NAME_NOT_FOUND); + expected = OS_ERR_NAME_NOT_FOUND; + actual = OS_FileSysStatVolume("/cf", &statbuf); + UtAssert_True(actual == expected, "OS_FileSysStatVolume() (%ld) == OS_ERR_NAME_NOT_FOUND", (long)actual); + UT_ResetState(UT_KEY(OS_ObjectIdGetBySearch)); + + /* Verify pass through of impl error */ + UT_SetDefaultReturnValue(UT_KEY(OS_FileSysStatVolume_Impl), OS_ERR_OPERATION_NOT_SUPPORTED); + expected = OS_ERR_OPERATION_NOT_SUPPORTED; + actual = OS_FileSysStatVolume("/cf", &statbuf); + UtAssert_True(actual == expected, "OS_FileSysStatVolume() (%ld) == OS_ERR_OPERATION_NOT_SUPPORTED", (long)actual); +} + void Test_OS_chkfs(void) { /* @@ -577,4 +628,5 @@ void UtTest_Setup(void) ADD_TEST(OS_GetFsInfo); ADD_TEST(OS_TranslatePath); ADD_TEST(OS_FileSys_FindVirtMountPoint); + ADD_TEST(OS_FileSysStatVolume); } diff --git a/src/unit-tests/osfilesys-test/ut_osfilesys_diskio_test.c b/src/unit-tests/osfilesys-test/ut_osfilesys_diskio_test.c index ebbc668b8..410e1efbf 100644 --- a/src/unit-tests/osfilesys-test/ut_osfilesys_diskio_test.c +++ b/src/unit-tests/osfilesys-test/ut_osfilesys_diskio_test.c @@ -1271,6 +1271,123 @@ void UT_os_fsbytesfree_test() return; } +/*--------------------------------------------------------------------------------* +** Syntax: int32 OS_fsstatvolume(const char *name) +** Purpose: Returns the number of blocks free in a the file system +** Parameters: *name - a pointer to the name of the drive to check for free blocks +** Returns: OS_INVALID_POINTER if the pointer passed in is NULL +** OS_FS_ERR_PATH_TOO_LONG if the path passed in is too long +** OS_ERROR if the OS call failed +** Number of blocks free in a volume if succeeded +** OS_ERR_NOT_IMPLEMENTED if not implemented +** ----------------------------------------------------- +** Test #0: Not-implemented condition +** 1) Call this routine +** 2) If the returned value is OS_ERR_NOT_IMPLEMENTED, then exit test +** 3) Otherwise, continue. +** ----------------------------------------------------- +** Test #1: Null-pointer-arg condition +** 1) Call this routine with a null pointer as one of the arguments +** 2) Expect the returned value to be +** (a) OS_INVALID_POINTER +** ----------------------------------------------------- +** Test #2: Path-too-long-arg condition +** 1) Call this routine with a path name of length greater than Volume table's +** name as argument +** 2) Expect the returned value to be +** (a) OS_FS_ERR_PATH_TOO_LONG +** ----------------------------------------------------- +** Test #3: OS-call-failure condition +** 1) Setup the test to cause the OS call to fail inside this routine +** 2) Call this routine +** 3) Expect the returned value to be +** (a) OS_ERROR +** ----------------------------------------------------- +** Test#4: Nominal condition +** 1) Make sure no file system has been previously created +** 2) Call OS_mkfs +** 3) Expect the returned value to be +** (a) OS_SUCCESS +** 4) Call OS_mount with device name used in #2 +** 5) Expect the returned value to be +** (a) OS_SUCCESS +** 6) Call this routine with mount-point used in #4 +** 7) Expect the returned value to be +** (a) greater than or equal to 0 +** --------------------------------------------------------------------------------*/ +void UT_os_fsstatvolume_test(void) +{ + const char * testDesc; + OS_statvfs_t statbuf; + + /*-----------------------------------------------------*/ + testDesc = "API not implemented"; + + if (OS_FileSysStatVolume("/cf", &statbuf) == OS_ERR_NOT_IMPLEMENTED) + { + UT_OS_TEST_RESULT(testDesc, UTASSERT_CASETYPE_NA); + goto UT_os_fsstatvolume_test_exit_tag; + } + + /*-----------------------------------------------------*/ + testDesc = "#1a Null-pointer-arg"; + + if (OS_FileSysStatVolume(NULL, &statbuf) == OS_INVALID_POINTER) + UT_OS_TEST_RESULT(testDesc, UTASSERT_CASETYPE_PASS); + else + UT_OS_TEST_RESULT(testDesc, UTASSERT_CASETYPE_FAILURE); + + /*-----------------------------------------------------*/ + testDesc = "#1b Null-pointer-arg"; + + if (OS_FileSysStatVolume("/cf", NULL) == OS_INVALID_POINTER) + UT_OS_TEST_RESULT(testDesc, UTASSERT_CASETYPE_PASS); + else + UT_OS_TEST_RESULT(testDesc, UTASSERT_CASETYPE_FAILURE); + + /*-----------------------------------------------------*/ + testDesc = "#2 Path-too-long-arg"; + + if (OS_FileSysStatVolume(g_fsLongName, &statbuf) == OS_FS_ERR_PATH_TOO_LONG) + UT_OS_TEST_RESULT(testDesc, UTASSERT_CASETYPE_PASS); + else + UT_OS_TEST_RESULT(testDesc, UTASSERT_CASETYPE_FAILURE); + + /*-----------------------------------------------------*/ + testDesc = "#3 OS-call-failure"; + + UT_OS_TEST_RESULT(testDesc, UTASSERT_CASETYPE_INFO); + + /*-----------------------------------------------------*/ + testDesc = "#4 Nominal"; + + if (OS_mkfs(g_fsAddrPtr, g_devNames[4], g_volNames[4], g_blkSize, g_blkCnt) != OS_SUCCESS) + { + testDesc = "#4 Nominal - File-system-create failed"; + UT_OS_TEST_RESULT(testDesc, UTASSERT_CASETYPE_TSF); + goto UT_os_fsstatvolume_test_exit_tag; + } + + if (OS_mount(g_devNames[4], g_mntNames[4]) != OS_SUCCESS) + { + testDesc = "#4 Nominal - File-system-mount failed"; + UT_OS_TEST_RESULT(testDesc, UTASSERT_CASETYPE_TSF); + goto UT_os_fsstatvolume_test_exit_tag; + } + + if (OS_FileSysStatVolume(g_mntNames[4], &statbuf) >= 0) + UT_OS_TEST_RESULT(testDesc, UTASSERT_CASETYPE_PASS); + else + UT_OS_TEST_RESULT(testDesc, UTASSERT_CASETYPE_FAILURE); + + /* Reset test environment */ + OS_unmount(g_mntNames[4]); + OS_rmfs(g_devNames[4]); + +UT_os_fsstatvolume_test_exit_tag: + return; +} + /*================================================================================* ** End of File: ut_osfilesys_diskio_test.c **================================================================================*/ diff --git a/src/unit-tests/osfilesys-test/ut_osfilesys_diskio_test.h b/src/unit-tests/osfilesys-test/ut_osfilesys_diskio_test.h index c6ab2dd2a..f1420c68c 100644 --- a/src/unit-tests/osfilesys-test/ut_osfilesys_diskio_test.h +++ b/src/unit-tests/osfilesys-test/ut_osfilesys_diskio_test.h @@ -71,6 +71,7 @@ void UT_os_checkfs_test(void); void UT_os_fsblocksfree_test(void); void UT_os_fsbytesfree_test(void); +void UT_os_fsstatvolume_test(void); /*--------------------------------------------------------------------------------*/ diff --git a/src/unit-tests/osfilesys-test/ut_osfilesys_test.c b/src/unit-tests/osfilesys-test/ut_osfilesys_test.c index 16a147d97..3850eff52 100644 --- a/src/unit-tests/osfilesys-test/ut_osfilesys_test.c +++ b/src/unit-tests/osfilesys-test/ut_osfilesys_test.c @@ -137,6 +137,7 @@ void UtTest_Setup(void) UtTest_Add(UT_os_checkfs_test, NULL, NULL, "OS_chkfs"); UtTest_Add(UT_os_fsblocksfree_test, NULL, NULL, "OS_fsBlocksFree"); UtTest_Add(UT_os_fsbytesfree_test, NULL, NULL, "OS_fsBytesFree"); + UtTest_Add(UT_os_fsstatvolume_test, NULL, NULL, "OS_FileSysStatVolume"); } /*================================================================================* diff --git a/src/ut-stubs/osapi-utstub-filesys.c b/src/ut-stubs/osapi-utstub-filesys.c index 02188510c..b696751e5 100644 --- a/src/ut-stubs/osapi-utstub-filesys.c +++ b/src/ut-stubs/osapi-utstub-filesys.c @@ -192,6 +192,28 @@ int32 OS_fsBytesFree(const char *name, uint64 *bytes_free) return status; } +/***************************************************************************** + * + * Stub function for OS_FileSysStatVolume() + * + *****************************************************************************/ +int32 OS_FileSysStatVolume(const char *name, OS_statvfs_t *statbuf) +{ + UT_Stub_RegisterContext(UT_KEY(OS_FileSysStatVolume), name); + UT_Stub_RegisterContext(UT_KEY(OS_FileSysStatVolume), statbuf); + + int32 status; + + status = UT_DEFAULT_IMPL(OS_FileSysStatVolume); + + if (status == OS_SUCCESS && UT_Stub_CopyToLocal(UT_KEY(OS_FileSysStatVolume), statbuf, sizeof(*statbuf)) < sizeof(*statbuf)) + { + memset(statbuf, 0, sizeof(*statbuf)); + } + + return status; +} + /***************************************************************************** * * Stub function for OS_chkfs()