From 4ddcef2453ff8415520d759a70825c8ea6a912c4 Mon Sep 17 00:00:00 2001 From: anders_k Date: Thu, 14 May 2015 18:41:06 +0000 Subject: [PATCH] Fixed 4+ TiB freespace calculation bug (bugs #1115 & #896) git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@6582 212acab6-be3b-0410-9dea-997c60f758d6 --- Docs/src/history.but | 6 ++- Source/exehead/Ui.c | 95 ++++++++++++++++++++++++-------------------- 2 files changed, 56 insertions(+), 45 deletions(-) diff --git a/Docs/src/history.but b/Docs/src/history.but index f3bf556f..ca7a920f 100644 --- a/Docs/src/history.but +++ b/Docs/src/history.but @@ -14,12 +14,14 @@ Released on ?, 201? \b Added !appendfile /RawNL switch +\b Fixed 4+ TiB freespace calculation bug (\W{http://sf.net/p/nsis/bugs/1115/}{bug #1115}) + +\b Preliminary Windows 10 support + \b Exec[Wait] sets the CREATE_DEFAULT_ERROR_MODE flag when creating a process \b Fixed minor issues in the Pascal NSIS plug-in SDK and removed the \cw{extrap} global variable -\b Windows 10 support - \b Fixed CreateShortcut /NoWorkingDir parsing bug (\W{http://sf.net/p/nsis/bugs/1110/}{bug #1110}) \H{v3.0b1} 3.0 Beta 1 diff --git a/Source/exehead/Ui.c b/Source/exehead/Ui.c index f65c93d9..6ad9f5ef 100644 --- a/Source/exehead/Ui.c +++ b/Source/exehead/Ui.c @@ -862,18 +862,41 @@ static INT_PTR CALLBACK UninstProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARA } #endif +#ifndef _NSIS_NO_INT64_SHR +#define NRT_U64Shr32(v,s) ( (v) >> (s) ) +#else +#define NRT_U64Shr32 Int64ShrlMod32 +#endif -static void NSISCALL SetSizeText(int dlgItem, int prefix, unsigned kb) +static void NSISCALL SetSizeText64(int dlgItem, int prefix, ULARGE_INTEGER kb64) { TCHAR scalestr[32], byte[32]; - unsigned sh = 20; int scale = LANG_GIGA; + UINT intgr, fract; - if (kb < 1024 * 1024) { sh = 10; scale = LANG_MEGA; } - if (kb < 1024) { sh = 0; scale = LANG_KILO; } + if (kb64.HighPart) // 4TB+ ? + { + kb64.QuadPart = NRT_U64Shr32(kb64.QuadPart, 20); // Convert from KiB to GiB + // wsprintf only supports the I64 size specifier on WinXP+. + // Older versions would crash because %s will use a bad pointer if we use "%I64u%s%s". + // Consequently we will only use the bottom 32-bits of the size (in GiB), + // this means we will display the wrong number if you have more than 4194303 TiB of free space. + intgr = kb64.LowPart; + fract = 0; // We don't even attempt to calculate this + } + else + { + unsigned sh = 20, kb = kb64.LowPart; + if (kb < 1024 * 1024) sh = 10, scale = LANG_MEGA; + if (kb < 1024) sh = 0, scale = LANG_KILO; - if (kb < (0xFFFFFFFF - ((1 << 20) / 20))) // check for overflow - kb += (1 << sh) / 20; // round numbers for better display (e.g. 1.59 => 1.6) + if (kb < (0xFFFFFFFF - ((1 << 20) / 20))) // check for overflow + kb += (1 << sh) / 20; // round numbers for better display (e.g. 1.59 => 1.6) + + intgr = kb >> sh; + // 0x00FFFFFF mask is used to prevent overflow that causes bad results + fract = (((kb & 0x00FFFFFF) * 10) >> sh) % 10; + } #if _MSC_VER == 1200 // patch #1982084 wsprintf( @@ -884,16 +907,19 @@ static void NSISCALL SetSizeText(int dlgItem, int prefix, unsigned kb) g_tmp + mystrlen(g_tmp), #endif _T("%u.%u%s%s"), - kb >> sh, - (((kb & 0x00FFFFFF) * 10) >> sh) % 10, // 0x00FFFFFF mask is used to - // prevent overflow that causes - // bad results + intgr, fract, GetNSISString(scalestr, scale), GetNSISString(byte, LANG_BYTE) - ); + ); my_SetDialogItemText(m_curwnd,dlgItem,g_tmp); } +static void NSISCALL SetSizeText(int dlgItem, int prefix, unsigned kb) +{ + ULARGE_INTEGER kb64; + kb64.QuadPart = kb; + SetSizeText64(dlgItem, prefix, kb64); +} static int NSISCALL _sumsecsfield(int idx) { @@ -1028,12 +1054,8 @@ static INT_PTR CALLBACK DirProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM l { static TCHAR s[NSIS_MAX_STRLEN]; int error = 0; - int available_set = 0; - unsigned total, available; - -#if defined(__GNUC__) && ((__GNUC__ * 1000) + __GNUC_MINOR__) < 4006 - available = 0; // warning: 'available' may be used uninitialized in this function -#endif + UINT total, available_set = FALSE; + ULARGE_INTEGER available; GetUIText(IDC_DIR,dir); if (!is_valid_instpath(dir)) @@ -1068,19 +1090,13 @@ static INT_PTR CALLBACK DirProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM l if (GDFSE) #endif { - ULARGE_INTEGER available64; ULARGE_INTEGER a, b; - TCHAR *p; - TCHAR *pw = NULL; + TCHAR *p, *pw = NULL; while (pw != s) // trimslashtoend() cut the entire string { - if (GDFSE(s, &available64, &a, &b)) + if (GDFSE(s, &available, &a, &b)) { -#ifndef _NSIS_NO_INT64_SHR - available = (int)(available64.QuadPart >> 10); -#else - available = (int)(Int64ShrlMod32(available64.QuadPart, 10)); -#endif + available.QuadPart = NRT_U64Shr32(available.QuadPart, 10); available_set++; break; } @@ -1098,8 +1114,8 @@ static INT_PTR CALLBACK DirProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM l } } } - - if (!available_set && sizeof(void*) <= 4) +#ifndef _WIN64 + if (!available_set) { DWORD spc, bps, fc, tc; TCHAR *root; @@ -1107,33 +1123,26 @@ static INT_PTR CALLBACK DirProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM l // GetDiskFreeSpaceEx accepts any path, but GetDiskFreeSpace accepts only the root mystrcpy(s,dir); root=skip_root(s); - if (root) - *root=0; + if (root) *root=0; // GetDiskFreeSpaceEx is not available if (GetDiskFreeSpace(s, &spc, &bps, &fc, &tc)) { - available = (int)MulDiv(bps * spc, fc, 1 << 10); + available.QuadPart = (int)MulDiv(bps * spc, fc, 1 << 10); available_set++; } } - - total = (unsigned) sumsecsfield(size_kb); - -#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wuninitialized" // available_set is checked first so available is initialized -#endif - if (available_set && available < total) - error = NSIS_INSTDIR_NOT_ENOUGH_SPACE; -#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) -#pragma GCC diagnostic pop #endif + total = (UINT) sumsecsfield(size_kb); + + if (available_set) + if (available.QuadPart < total) + error = NSIS_INSTDIR_NOT_ENOUGH_SPACE; if (LANG_STR_TAB(LANG_SPACE_REQ)) { SetSizeText(IDC_SPACEREQUIRED,LANG_SPACE_REQ,total); if (available_set) - SetSizeText(IDC_SPACEAVAILABLE,LANG_SPACE_AVAIL,available); + SetSizeText64(IDC_SPACEAVAILABLE,LANG_SPACE_AVAIL,available); else SetUITextNT(IDC_SPACEAVAILABLE,_T("")); }