From 021bba6e575c8a035e7ab57da09d6662a12310ba Mon Sep 17 00:00:00 2001 From: Preston-Sundar Date: Tue, 7 Jun 2022 21:09:42 +0000 Subject: [PATCH] Add cache directory out of memory check. --- client/api.c | 9 ++++++- client/defines.h | 15 +---------- client/includes.h | 2 ++ client/packageutils.c | 54 +++++++++++++++++++++++++++++-------- client/prototypes.h | 12 +++++++-- common/utils.c | 2 +- include/tdnferror.h | 2 ++ include/tdnftypes.h | 1 - pytests/tests/test_cache.py | 11 ++++++-- tools/cli/lib/api.c | 8 ++++++ tools/cli/lib/installcmd.c | 16 +++++------ 11 files changed, 90 insertions(+), 42 deletions(-) diff --git a/client/api.c b/client/api.c index e491eeaa..c74f041f 100644 --- a/client/api.c +++ b/client/api.c @@ -1567,6 +1567,7 @@ TDNFResolve( Queue queueGoal = {0}; char** ppszPkgsNotResolved = NULL; PTDNF_SOLVED_PKG_INFO pSolvedPkgInfo = NULL; + uint64_t qwAvailCacheBytes = 0; if(!pTdnf || !ppSolvedPkgInfo) { @@ -1634,9 +1635,15 @@ TDNFResolve( pSolvedPkgInfo->pPkgsToDowngrade || pSolvedPkgInfo->pPkgsToReinstall; - dwError = TDNFCalculateTotalDownloadSize(pSolvedPkgInfo); + dwError = TDNFGetAvailableCacheBytes(pTdnf->pConf, &qwAvailCacheBytes); BAIL_ON_TDNF_ERROR(dwError); + if (pSolvedPkgInfo->nNeedDownload) + { + dwError = TDNFCheckDownloadCacheBytes(pSolvedPkgInfo, qwAvailCacheBytes); + BAIL_ON_TDNF_ERROR(dwError); + } + pSolvedPkgInfo->ppszPkgsNotResolved = ppszPkgsNotResolved; *ppSolvedPkgInfo = pSolvedPkgInfo; diff --git a/client/defines.h b/client/defines.h index 74c385ad..aade03a8 100644 --- a/client/defines.h +++ b/client/defines.h @@ -233,24 +233,11 @@ typedef enum "ERROR_TDNF_DOWNGRADE_NOT_ALLOWED",\ "a downgrade is not allowed below the minimal version. Check 'minversions' in the configuration."},\ {ERROR_TDNF_PERM, "ERROR_TDNF_PERM", "Operation not permitted. You have to be root."},\ - {ERROR_TDNF_INVALID_PARAMETER, "ERROR_TDNF_INVALID_PARAMETER", "Invalid argument."},\ - {ERROR_TDNF_OUT_OF_MEMORY, "ERROR_TDNF_OUT_OF_MEMORY", "Out of memory."},\ - {ERROR_TDNF_NO_DATA, "ERROR_TDNF_NO_DATA", "No data available."},\ - {ERROR_TDNF_FILE_NOT_FOUND, "ERROR_TDNF_FILE_NOT_FOUND", "File not found."},\ - {ERROR_TDNF_ACCESS_DENIED, "ERROR_TDNF_ACCESS_DENIED", "Permission denied."},\ - {ERROR_TDNF_ALREADY_EXISTS, "ERROR_TDNF_ALREADY_EXISTS", "File exists."},\ - {ERROR_TDNF_INVALID_ADDRESS, "ERROR_TDNF_INVALID_ADDRESS", "Bad address."},\ - {ERROR_TDNF_CALL_INTERRUPTED, "ERROR_TDNF_CALL_INTERRUPTED", "Interrupted syscall."},\ - {ERROR_TDNF_FILESYS_IO, "ERROR_TDNF_FILESYS_IO", "Filesystem I/O error."},\ - {ERROR_TDNF_SYM_LOOP, "ERROR_TDNF_SYM_LOOP", "Too many links."},\ - {ERROR_TDNF_NAME_TOO_LONG, "ERROR_TDNF_NAME_TOO_LONG", "File name too long."},\ - {ERROR_TDNF_CALL_NOT_SUPPORTED, "ERROR_TDNF_CALL_NOT_SUPPORTED", "Invalid syscall number."},\ - {ERROR_TDNF_INVALID_DIR, "ERROR_TDNF_INVALID_DIR", "Not a directory."},\ - {ERROR_TDNF_OVERFLOW, "ERROR_TDNF_OVERFLOW", "Value too large for defined data type."},\ {ERROR_TDNF_OPT_NOT_FOUND, "ERROR_TDNF_OPT_NOT_FOUND", "A required option was not found"},\ {ERROR_TDNF_OPERATION_ABORTED, "ERROR_TDNF_OPERATION_ABORTED", "Operation aborted."},\ {ERROR_TDNF_INVALID_INPUT, "ERROR_TDNF_INVALID_INPUT", "Invalid input."},\ {ERROR_TDNF_CACHE_DISABLED, "ERROR_TDNF_CACHE_DISABLED", "cache only is set, but no repo data found"},\ + {ERROR_TDNF_CACHE_DIR_OUT_OF_DISK_SPACE, "ERROR_TDNF_CACHE_DIR_OUT_OF_DISK_SPACE", "Insufficient disk space at cache directory /var/cache/tdnf (unless specified differently in config). Try freeing space first."},\ {ERROR_TDNF_EVENT_CTXT_ITEM_NOT_FOUND, "ERROR_TDNF_EVENT_CTXT_ITEM_NOT_FOUND", "An event context item was not found. This is usually related to plugin events. Try --noplugins to deactivate all plugins or --disableplugin= to deactivate a specific one. You can permanently deactivate an offending plugin by setting enable=0 in the plugin config file."},\ {ERROR_TDNF_EVENT_CTXT_ITEM_INVALID_TYPE, "ERROR_TDNF_EVENT_CTXT_ITEM_INVALID_TYPE", "An event item type had a mismatch. This is usually related to plugin events. Try --noplugins to deactivate all plugins or --disableplugin= to deactivate a specific one. You can permanently deactivate an offending plugin by setting enable=0 in the plugin config file."},\ {ERROR_TDNF_NO_GPGKEY_CONF_ENTRY, "ERROR_TDNF_NO_GPGKEY_CONF_ENTRY", "gpgkey entry is missing for this repo. please add gpgkey in repo file or use --nogpgcheck to ignore."}, \ diff --git a/client/includes.h b/client/includes.h index 9067bb84..9814b0d3 100644 --- a/client/includes.h +++ b/client/includes.h @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include diff --git a/client/packageutils.c b/client/packageutils.c index e177daa2..dee11b0c 100644 --- a/client/packageutils.c +++ b/client/packageutils.c @@ -945,24 +945,53 @@ TDNFCheckProtectedPkgs( } uint32_t -TDNFCalculateTotalDownloadSize( - PTDNF_SOLVED_PKG_INFO pSolvedPkgInfo +TDNFGetAvailableCacheBytes( + PTDNF_CONF pConf, + uint64_t* pqwAvailCacheDirBytes ) { uint32_t dwError = 0; - uint32_t dwTotalDownloadSizeBytes = 0; - uint8_t byPkgIndex = 0; - PTDNF_PKG_INFO pPkgInfo = NULL; + struct statfs tmpStatfsBuffer = {0}; - if(!pSolvedPkgInfo) + if(!pConf || !pConf->pszCacheDir || !pqwAvailCacheDirBytes) { dwError = ERROR_TDNF_INVALID_PARAMETER; BAIL_ON_TDNF_ERROR(dwError); } - if(!pSolvedPkgInfo->nNeedDownload) + if (statfs(pConf->pszCacheDir, &tmpStatfsBuffer) != 0) { - pSolvedPkgInfo->dwTotalDownloadSizeBytes = 0; + dwError = errno; + BAIL_ON_TDNF_SYSTEM_ERROR(dwError); + } + + *pqwAvailCacheDirBytes = tmpStatfsBuffer.f_bsize * tmpStatfsBuffer.f_bavail; + +cleanup: + return dwError; + +error: + if(pqwAvailCacheDirBytes) + { + *pqwAvailCacheDirBytes = 0; + } + goto cleanup; +} + +uint32_t +TDNFCheckDownloadCacheBytes( + PTDNF_SOLVED_PKG_INFO pSolvedPkgInfo, + uint64_t qwAvailCacheBytes + ) +{ + uint32_t dwError = 0; + uint64_t qwTotalDownloadSizeBytes = 0; + uint8_t byPkgIndex = 0; + PTDNF_PKG_INFO pPkgInfo = NULL; + + if(!pSolvedPkgInfo) + { + dwError = ERROR_TDNF_INVALID_PARAMETER; BAIL_ON_TDNF_ERROR(dwError); } @@ -977,13 +1006,16 @@ TDNFCalculateTotalDownloadSize( { pPkgInfo = ppPkgsNeedDownload[byPkgIndex]; while(pPkgInfo) { - dwTotalDownloadSizeBytes += pPkgInfo->dwDownloadSizeBytes; + qwTotalDownloadSizeBytes += pPkgInfo->dwDownloadSizeBytes; + if (qwTotalDownloadSizeBytes > qwAvailCacheBytes) + { + dwError = ERROR_TDNF_CACHE_DIR_OUT_OF_DISK_SPACE; + BAIL_ON_TDNF_ERROR(dwError); + } pPkgInfo = pPkgInfo->pNext; } } - pSolvedPkgInfo->dwTotalDownloadSizeBytes = dwTotalDownloadSizeBytes; - error: return dwError; } diff --git a/client/prototypes.h b/client/prototypes.h index 7eb5491c..78f8a8c1 100644 --- a/client/prototypes.h +++ b/client/prototypes.h @@ -439,10 +439,18 @@ TDNFCheckProtectedPkgs( ); uint32_t -TDNFCalculateTotalDownloadSize( - PTDNF_SOLVED_PKG_INFO pSolvedPkgInfo +TDNFGetAvailableCacheBytes( + PTDNF_CONF pConf, + uint64_t* pqwAvailCacheBytes ); +uint32_t +TDNFCheckDownloadCacheBytes( + PTDNF_SOLVED_PKG_INFO pSolvedPkgInfo, + uint64_t qwAvailCacheBytes + ); + + uint32_t TDNFPopulatePkgInfoArrayDependencies( PSolvSack pSack, diff --git a/common/utils.c b/common/utils.c index 26e87125..bffa182b 100644 --- a/common/utils.c +++ b/common/utils.c @@ -156,7 +156,7 @@ TDNFUtilsFormatSize( dwError = TDNFAllocateMemory(1, nMaxSize, (void**)&pszFormattedSize); BAIL_ON_TDNF_ERROR(dwError); - if(sprintf(pszFormattedSize, "%6.2f%c", dSize, pszSizes[nIndex]) < 0) + if(snprintf(pszFormattedSize, nMaxSize, "%6.2f%c", dSize, pszSizes[nIndex]) >= nMaxSize) { dwError = ERROR_TDNF_OUT_OF_MEMORY; BAIL_ON_TDNF_ERROR(dwError); diff --git a/include/tdnferror.h b/include/tdnferror.h index 96fb2e08..3493949c 100644 --- a/include/tdnferror.h +++ b/include/tdnferror.h @@ -69,6 +69,8 @@ extern "C" { // cache only set, but no cache available #define ERROR_TDNF_CACHE_DISABLED 1034 #define ERROR_TDNF_DOWNGRADE_NOT_ALLOWED 1035 +// cache directory out of memory +#define ERROR_TDNF_CACHE_DIR_OUT_OF_DISK_SPACE 1036 //curl errors #define ERROR_TDNF_CURL_INIT 1200 diff --git a/include/tdnftypes.h b/include/tdnftypes.h index 460b49d3..0b255462 100644 --- a/include/tdnftypes.h +++ b/include/tdnftypes.h @@ -174,7 +174,6 @@ typedef struct _TDNF_SOLVED_PKG_INFO { int nNeedAction; int nNeedDownload; - uint32_t dwTotalDownloadSizeBytes; TDNF_ALTERTYPE nAlterType; PTDNF_PKG_INFO pPkgsNotAvailable; PTDNF_PKG_INFO pPkgsExisting; diff --git a/pytests/tests/test_cache.py b/pytests/tests/test_cache.py index 475ac50f..cd9714ec 100644 --- a/pytests/tests/test_cache.py +++ b/pytests/tests/test_cache.py @@ -18,8 +18,15 @@ def setup_test(utils): def teardown_test(utils): - pkgname = utils.config["sglversion_pkgname"] - utils.run(['tdnf', 'erase', '-y', pkgname]) + run_cmd = ['tdnf', 'erase', '-y'] + pkg_list = [ + utils.config["mulversion_pkgname"], + utils.config["sglversion_pkgname"], + utils.config["sglversion2_pkgname"], + ] + for pkgname in pkg_list: + utils.erase_package(pkgname) + utils.run(run_cmd) def clean_cache(utils): diff --git a/tools/cli/lib/api.c b/tools/cli/lib/api.c index 9cec5cb7..4c654a8f 100644 --- a/tools/cli/lib/api.c +++ b/tools/cli/lib/api.c @@ -297,6 +297,10 @@ TDNFCliInfoCommand( CHECK_JD_RC(jd_map_add_string(jd_pkg, "Repo", pPkg->pszRepoName)); CHECK_JD_RC(jd_map_add_string(jd_pkg, "Url", pPkg->pszURL)); CHECK_JD_RC(jd_map_add_int(jd_pkg, "InstallSize", pPkg->dwInstallSizeBytes)); + if (pPkg->dwDownloadSizeBytes) + { + CHECK_JD_RC(jd_map_add_int(jd_pkg, "DownloadSize", pPkg->dwDownloadSizeBytes)); + } CHECK_JD_RC(jd_map_add_string(jd_pkg, "Summary", pPkg->pszSummary)); CHECK_JD_RC(jd_map_add_string(jd_pkg, "License", pPkg->pszLicense)); CHECK_JD_RC(jd_map_add_string(jd_pkg, "Description", pPkg->pszDescription)); @@ -319,6 +323,10 @@ TDNFCliInfoCommand( pr_crit("Version : %s\n", pPkg->pszVersion); pr_crit("Release : %s\n", pPkg->pszRelease); pr_crit("Install Size : %s (%u)\n", pPkg->pszFormattedSize, pPkg->dwInstallSizeBytes); + if (pPkg->dwDownloadSizeBytes) + { + pr_crit("Download Size : %s (%u)\n", pPkg->pszFormattedDownloadSize, pPkg->dwDownloadSizeBytes); + } pr_crit("Repo : %s\n", pPkg->pszRepoName); pr_crit("Summary : %s\n", pPkg->pszSummary); pr_crit("URL : %s\n", pPkg->pszURL); diff --git a/tools/cli/lib/installcmd.c b/tools/cli/lib/installcmd.c index 0f3b7fb3..8a37889f 100644 --- a/tools/cli/lib/installcmd.c +++ b/tools/cli/lib/installcmd.c @@ -713,21 +713,17 @@ PrintAction( pPkgInfo = pPkgInfo->pNext; } - TDNFUtilsFormatSize(dwTotalInstallSize, &pszTotalInstallSize); + dwError = TDNFUtilsFormatSize(dwTotalInstallSize, &pszTotalInstallSize); + BAIL_ON_TDNF_ERROR(dwError); pr_info("\nTotal installed size: %s\n", pszTotalInstallSize); - TDNFUtilsFormatSize(dwTotalDownloadSize, &pszTotalDownloadSize); + dwError = TDNFUtilsFormatSize(dwTotalDownloadSize, &pszTotalDownloadSize); + BAIL_ON_TDNF_ERROR(dwError); pr_info("Total download size: %s\n", pszTotalDownloadSize); cleanup: - if (pszTotalInstallSize) - { - free(pszTotalInstallSize); - } - if (pszTotalDownloadSize) - { - free(pszTotalDownloadSize); - } + TDNFFreeMemory(pszTotalInstallSize); + TDNFFreeMemory(pszTotalDownloadSize); return dwError; error: