diff --git a/open-vm-tools/lib/include/strutil.h b/open-vm-tools/lib/include/strutil.h index 0ce50b0f3..bb74bf212 100644 --- a/open-vm-tools/lib/include/strutil.h +++ b/open-vm-tools/lib/include/strutil.h @@ -69,4 +69,6 @@ void StrUtil_SafeStrcatF(char **prefix, const char *fmt, ...) PRINTF_DECL(2, 3); char *StrUtil_TrimWhitespace(const char *str); +char *StrUtil_ReplaceAll(const char *orig, const char *what, const char *with); + #endif /* STRUTIL_H */ diff --git a/open-vm-tools/lib/misc/strutil.c b/open-vm-tools/lib/misc/strutil.c index 1ba231273..880bf6f19 100644 --- a/open-vm-tools/lib/misc/strutil.c +++ b/open-vm-tools/lib/misc/strutil.c @@ -37,6 +37,30 @@ #include "util.h" +/* + *----------------------------------------------------------------------------- + * + * StrUtil_IsEmpty -- + * + * Test if a non-NULL string is empty. + * + * Results: + * TRUE if the string has length 0, FALSE otherwise. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +static INLINE Bool +StrUtil_IsEmpty(const char *str) // IN: +{ + ASSERT(str != NULL); + return str[0] == '\0'; +} + + /* *----------------------------------------------------------------------------- * @@ -1235,3 +1259,66 @@ StrUtil_TrimWhitespace(const char *str) // IN return res; } + + +/* + *----------------------------------------------------------------------------- + * + * StrUtil_ReplaceAll -- + * + * Replaces all occurrences of a non-empty substring with non-NULL pattern + * in non-NULL string. + * + * Results: + * Returns pointer to the allocated resulting string. The caller is + * responsible for freeing it. + * + *----------------------------------------------------------------------------- + */ + +char * +StrUtil_ReplaceAll(const char *orig, // IN + const char *what, // IN + const char *with) // IN +{ + char *result; + const char *current; + char *tmp; + size_t lenWhat; + size_t lenWith; + size_t lenBefore; + size_t occurrences = 0; + size_t lenNew; + + ASSERT(orig != NULL); + ASSERT(!StrUtil_IsEmpty(what)); + ASSERT(with != NULL); + + lenWhat = strlen(what); + lenWith = strlen(with); + + current = orig; + while ((tmp = strstr(current, what)) != NULL) { + current = tmp + lenWhat; + ++occurrences; + } + + lenNew = strlen(orig) + (lenWith - lenWhat) * occurrences; + tmp = Util_SafeMalloc(lenNew + 1); + result = tmp; + + while (occurrences--) { + current = strstr(orig, what); + lenBefore = current - orig; + tmp = memcpy(tmp, orig, lenBefore); + tmp += lenBefore; + tmp = memcpy(tmp, with, lenWith); + tmp += lenWith; + orig += lenBefore + lenWhat; + } + memcpy(tmp, orig, strlen(orig)); + + result[lenNew] = '\0'; + + return result; +} diff --git a/open-vm-tools/lib/misc/strutil.c.orig b/open-vm-tools/lib/misc/strutil.c.orig new file mode 100644 index 000000000..1ba231273 --- /dev/null +++ b/open-vm-tools/lib/misc/strutil.c.orig @@ -0,0 +1,1237 @@ +/********************************************************* + * Copyright (C) 1998-2015 VMware, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation version 2.1 and no later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + *********************************************************/ + +/* + * strutil.c -- + * + * String utility functions. + */ + +#include +#include +#include +#include +#if !defined(_WIN32) +#include /* For strncasecmp */ +#endif +#include "vmware.h" +#include "strutil.h" +#include "str.h" +#include "dynbuf.h" +#include "vm_ctype.h" +#include "util.h" + + +/* + *----------------------------------------------------------------------------- + * + * StrUtil_GetNextToken -- + * + * Get the next token from a string after a given index w/o modifying the + * original string. + * + * Results: + * An allocated, NUL-terminated string containing the token. 'index' is + * updated to point after the returned token + * NULL if no tokens are left + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +char * +StrUtil_GetNextToken(unsigned int *index, // IN/OUT: Index to start at + const char *str, // IN : String to parse + const char *delimiters) // IN : Chars separating tokens +{ + unsigned int startIndex; + unsigned int length; + char *token; + + ASSERT(index); + ASSERT(str); + ASSERT(delimiters); + ASSERT(*index <= strlen(str)); + +#define NOT_DELIMITER (Str_Strchr(delimiters, str[*index]) == NULL) + + /* Skip leading delimiters. */ + for (; ; (*index)++) { + if (str[*index] == '\0') { + return NULL; + } + + if (NOT_DELIMITER) { + break; + } + } + startIndex = *index; + + /* + * Walk the string until we reach the end of it, or we find a + * delimiter. + */ + for ((*index)++; str[*index] != '\0' && NOT_DELIMITER; (*index)++) { + } + +#undef NOT_DELIMITER + + length = *index - startIndex; + ASSERT(length); + token = Util_SafeMalloc(length + 1 /* NUL */); + memcpy(token, str + startIndex, length); + token[length] = '\0'; + + return token; +} + + +/* + *----------------------------------------------------------------------------- + * + * StrUtil_GetNextIntToken -- + * + * Acts like StrUtil_GetNextToken except it returns an int32. + * + * Results: + * TRUE if a valid int was parsed and 'out' contains the parsed int. + * FALSE otherwise. Contents of 'out' are undefined. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +StrUtil_GetNextIntToken(int32 *out, // OUT : parsed int + unsigned int *index, // IN/OUT: Index to start at + const char *str, // IN : String to parse + const char *delimiters) // IN : Chars separating tokens +{ + char *resultStr; + Bool valid = FALSE; + + ASSERT(out); + ASSERT(index); + ASSERT(str); + ASSERT(delimiters); + + resultStr = StrUtil_GetNextToken(index, str, delimiters); + if (resultStr == NULL) { + return FALSE; + } + + valid = StrUtil_StrToInt(out, resultStr); + free(resultStr); + + return valid; +} + + +/* + *----------------------------------------------------------------------------- + * + * StrUtil_GetNextUintToken -- + * + * Acts like StrUtil_GetNextIntToken except it returns an uint32. + * + * Results: + * TRUE if a valid int was parsed and 'out' contains the parsed int. + * FALSE otherwise. Contents of 'out' are undefined. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +StrUtil_GetNextUintToken(uint32 *out, // OUT : parsed int + unsigned int *index, // IN/OUT: Index to start at + const char *str, // IN : String to parse + const char *delimiters) // IN : Chars separating tokens +{ + char *resultStr; + Bool valid = FALSE; + + ASSERT(out); + ASSERT(index); + ASSERT(str); + ASSERT(delimiters); + + resultStr = StrUtil_GetNextToken(index, str, delimiters); + if (resultStr == NULL) { + return FALSE; + } + + valid = StrUtil_StrToUint(out, resultStr); + free(resultStr); + + return valid; +} + + +/* + *----------------------------------------------------------------------------- + * + * StrUtil_GetNextInt64Token -- + * + * Acts like StrUtil_GetNextToken except it returns an int64. + * + * Results: + * TRUE on a successful retrieval. FALSE otherwise. + * Token is stored in 'out', which is left undefined in the FALSE case. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +StrUtil_GetNextInt64Token(int64 *out, // OUT: The output value + unsigned int *index, // IN/OUT: Index to start at + const char *str, // IN : String to parse + const char *delimiters) // IN : Chars separating tokens +{ + char *resultStr; + Bool result; + + ASSERT(out); + ASSERT(index); + ASSERT(str); + ASSERT(delimiters); + + resultStr = StrUtil_GetNextToken(index, str, delimiters); + result = resultStr ? StrUtil_StrToInt64(out, resultStr) : FALSE; + free(resultStr); + + return result; +} + + +/* + *----------------------------------------------------------------------------- + * + * StrUtil_DecimalStrToUint -- + * + * Convert a string into an integer. + * + * Results: + * TRUE if the conversion was successful, and 'out' contains the converted + * result, and 'str' is updated to point to new place after last processed + * digit. + * FALSE otherwise. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +StrUtil_DecimalStrToUint(unsigned int *out, // OUT + const char **str) // IN/OUT : String to parse +{ + unsigned long val; + char *ptr; + + ASSERT(out); + ASSERT(str); + + errno = 0; + val = strtoul(*str, &ptr, 10); + if (ptr == *str || errno == ERANGE || val != (unsigned int)val) { + return FALSE; + } + *str = ptr; + *out = (unsigned int)val; + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * StrUtil_StrToInt -- + * + * Convert a string into an integer. + * + * Results: + * TRUE if the conversion was successful and 'out' contains the converted + * result. + * FALSE otherwise. 'out' is undefined. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +StrUtil_StrToInt(int32 *out, // OUT + const char *str) // IN : String to parse +{ + char *ptr; + long val; + + ASSERT(out); + ASSERT(str); + + errno = 0; + + val = strtol(str, &ptr, 0); + *out = (int32)val; + + /* + * Input must be complete, no overflow, and value read must fit into + * 32 bits - both signed and unsigned values are accepted. + */ + + return *ptr == '\0' && errno != ERANGE && + (val == (int32)val || val == (uint32)val); +} + + +/* + *----------------------------------------------------------------------------- + * + * StrUtil_StrToUint -- + * + * Convert a string into unsigned integer. + * + * Results: + * TRUE if the conversion succeeded and 'out' contains the result. + * FALSE otherwise. 'out' is undefined. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +StrUtil_StrToUint(uint32 *out, // OUT + const char *str) // IN : String to parse +{ + char *ptr; + unsigned long val; + + ASSERT(out); + ASSERT(str); + + errno = 0; + + val = strtoul(str, &ptr, 0); + *out = (uint32)val; + + /* + * Input must be complete, no overflow, and value read must fit into 32 + * bits - both signed and unsigned values are accepted. + */ + + return *ptr == '\0' && errno != ERANGE && + (val == (uint32)val || val == (int32)val); +} + + +/* + *----------------------------------------------------------------------------- + * + * StrUtil_StrToInt64 -- + * + * Convert a string into a 64bit integer. + * + * Results: + * TRUE if conversion was successful, FALSE otherwise. + * Value is stored in 'out', which is left undefined in the FALSE case. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +StrUtil_StrToInt64(int64 *out, // OUT: The output value + const char *str) // IN : String to parse +{ + char *ptr; + + ASSERT(out); + ASSERT(str); + + errno = 0; + +#if defined(_WIN32) + *out = _strtoi64(str, &ptr, 0); +#elif defined(__FreeBSD__) + *out = strtoq(str, &ptr, 0); +#else + *out = strtoll(str, &ptr, 0); +#endif + + return ptr[0] == '\0' && errno != ERANGE; +} + + +/* + *----------------------------------------------------------------------------- + * + * StrUtil_StrToUint64 -- + * + * Convert a string into an unsigned 64bit integer. + * + * Results: + * TRUE if conversion was successful, FALSE otherwise. + * Value is stored in 'out', which is left undefined in the FALSE case. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +StrUtil_StrToUint64(uint64 *out, // OUT: The output value + const char *str) // IN : String to parse +{ + char *ptr; + + ASSERT(out); + ASSERT(str); + + errno = 0; + +#if defined(_WIN32) + *out = _strtoui64(str, &ptr, 0); +#elif defined(__FreeBSD__) + *out = strtouq(str, &ptr, 0); +#else + *out = strtoull(str, &ptr, 0); +#endif + + return ptr[0] == '\0' && errno != ERANGE && errno != EINVAL; +} + + +/* + *----------------------------------------------------------------------------- + * + * StrUtil_StrToSizet -- + * + * Convert a string into an unsigned integer that is either 32-bits or + * 64-bits long, depending on the underlying architecture. + * + * Results: + * TRUE if conversion was successful, FALSE otherwise. + * Value is stored in 'out', which is left undefined in the FALSE case. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +StrUtil_StrToSizet(size_t *out, // OUT: The output value + const char *str) // IN : String to parse +{ + char *ptr; + + ASSERT(out); + ASSERT(str); + + errno = 0; +#if defined VM_64BIT + ASSERT_ON_COMPILE(sizeof *out == sizeof(uint64)); +# if defined(_WIN32) + *out = _strtoui64(str, &ptr, 0); +# elif defined(__FreeBSD__) + *out = strtouq(str, &ptr, 0); +# else + *out = strtoull(str, &ptr, 0); +# endif +#else + ASSERT_ON_COMPILE(sizeof *out == sizeof(uint32)); + *out = strtoul(str, &ptr, 0); +#endif + + return *ptr == '\0' && errno != ERANGE; +} + + +/* + *----------------------------------------------------------------------------- + * + * StrUtil_StrToDouble -- + * + * Convert a string into a double. + * + * Results: + * TRUE if the conversion was successful and 'out' contains the converted + * result. + * FALSE otherwise. 'out' is undefined. + * + * Side effects: + * Modifies errno. + * + *----------------------------------------------------------------------------- + */ + +Bool +StrUtil_StrToDouble(double *out, // OUT: The output value + const char *str) // IN : String to parse +{ + char *ptr = NULL; + + ASSERT(out); + ASSERT(str); + + errno = 0; + + *out = strtod(str, &ptr); + + /* + * Input must be complete and no overflow. + */ + + return *ptr == '\0' && errno != ERANGE; +} + + +/* + *----------------------------------------------------------------------------- + * + * StrUtil_CapacityToBytes -- + * + * Converts a string containing a measure of capacity (such as + * "100MB" or "1.5k") into an unadorned and primitive quantity of bytes + * capacity. The comment before the switch statement describes the kinds + * of capacity expressible. + * + * Results: + * TRUE if conversion was successful, FALSE otherwise. + * Value is stored in 'out', which is left undefined in the FALSE case. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +StrUtil_CapacityToBytes(uint64 *out, // OUT: The output value + const char *str, // IN: String to parse + unsigned int bytes) // IN: Bytes per unit in an + // unadorned string + +{ + double quantity; + char *rest; + + ASSERT(out); + ASSERT(str); + + errno = 0; + quantity = strtod(str, &rest); + if (errno == ERANGE) { + return FALSE; + } + + /* Skip over any whitespace in the suffix. */ + while (*rest == ' ' || *rest == '\t') { + rest++; + } + if (*rest != '\0') { + uint64 shift; + Bool suffixOK = TRUE; + + /* + * [kK], [mM], [gG], and [tT] represent kilo, mega, giga, and tera + * byte quantities respectively. [bB] represents a singular byte + * quantity. [sS] represents a sector quantity. + * + * For kilo, mega, giga, and tera we're OK with an additional byte + * suffix. Otherwise, the presence of an additional suffix is an error. + */ + switch (*rest) { + case 'b': case 'B': shift = 0; suffixOK = FALSE; break; + case 's': case 'S': shift = 9; suffixOK = FALSE; break; + case 'k': case 'K': shift = 10; break; + case 'm': case 'M': shift = 20; break; + case 'g': case 'G': shift = 30; break; + case 't': case 'T': shift = 40; break; + default : return FALSE; + } + switch(*++rest) { + case '\0': + break; + case 'b': case 'B': + if (suffixOK && !*++rest) { + break; + } + /* FALLTHRU */ + default: + return FALSE; + } + quantity *= CONST64U(1) << shift; + } else { + /* + * No suffix, so multiply by the number of bytes per unit as specified + * by the caller. + */ + quantity *= bytes; + } + + *out = quantity; + + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * StrUtil_CapacityToSectorType -- + * + * Converts a string containing a measure of disk capacity (such as + * "100MB" or "1.5k") into an unadorned and primitive quantity of sector + * capacity. + * + * Results: + * TRUE if conversion was successful, FALSE otherwise. + * Value is stored in 'out', which is left undefined in the FALSE case. + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +Bool +StrUtil_CapacityToSectorType(SectorType *out, // OUT: The output value + const char *str, // IN: String to parse + unsigned int bytes) // IN: Bytes per unit in an + // unadorned string + +{ + uint64 quantityInBytes; + + if (StrUtil_CapacityToBytes(&quantityInBytes, str, bytes) == FALSE) { + return FALSE; + } + + /* + * Convert from "number of bytes" to "number of sectors", rounding up or + * down appropriately. + * + * XXX: We should use DISKLIB_SECTOR_SIZE, but do we really want the + * disklib header dependencies in this file? + * + */ + *out = (SectorType)((quantityInBytes + 256) / 512); + + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * StrUtil_FormatSizeInBytes -- + * + * Format a size (in bytes) to a string in a user-friendly way. + * + * Example: 160041885696 -> "149.1 GB" + * + * Results: + * The allocated, NUL-terminated string (not localized). + * + * Side effects: + * None + * + *----------------------------------------------------------------------------- + */ + +char * +StrUtil_FormatSizeInBytesUnlocalized(uint64 size) // IN +{ + /* + * XXX TODO, BUG 199661: + * This is a direct copy of Msg_FormatSizeInBytes without localization. + * These two functions should ideally share the basic functionality, and + * just differ in the string localization + */ + + char const *fmt; + double sizeInSelectedUnit; + unsigned int precision; + char *sizeFormat; + char *sizeString; + char *result; + static const double epsilon = 0.01; + double delta; + + if (size >= CONST64U(1) << 40 /* 1 TB */) { + fmt = "%s TB"; + sizeInSelectedUnit = (double)size / (CONST64U(1) << 40); + precision = 1; + } else if (size >= CONST64U(1) << 30 /* 1 GB */) { + fmt = "%s GB"; + sizeInSelectedUnit = (double)size / (CONST64U(1) << 30); + precision = 1; + } else if (size >= CONST64U(1) << 20 /* 1 MB */) { + fmt = "%s MB"; + sizeInSelectedUnit = (double)size / (CONST64U(1) << 20); + precision = 1; + } else if (size >= CONST64U(1) << 10 /* 1 KB */) { + fmt = "%s KB"; + sizeInSelectedUnit = (double)size / (CONST64U(1) << 10); + precision = 1; + } else if (size >= CONST64U(2) /* 2 bytes */) { + fmt = "%s bytes"; + sizeInSelectedUnit = (double)size; + precision = 0; // No fractional byte. + } else if (size >= CONST64U(1) /* 1 byte */) { + fmt = "%s byte"; + sizeInSelectedUnit = (double)size; + precision = 0; // No fractional byte. + } else { + ASSERT(size == CONST64U(0) /* 0 bytes */); + fmt = "%s bytes"; + sizeInSelectedUnit = (double)size; + precision = 0; // No fractional byte. + } + + /* + * We cast to uint32 instead of uint64 here because of a problem with the + * NetWare Tools build. However, it's safe to cast to uint32 since we have + * already reduced the range of sizeInSelectedUnit above. + */ + // If it would display with .0, round it off and display the integer value. + delta = (uint32)(sizeInSelectedUnit + 0.5) - sizeInSelectedUnit; + if (delta < 0) { + delta = -delta; + } + if (delta <= epsilon) { + precision = 0; + sizeInSelectedUnit = (double)(uint32)(sizeInSelectedUnit + 0.5); + } + + sizeFormat = Str_Asprintf(NULL, "%%.%uf", precision); + sizeString = Str_Asprintf(NULL, sizeFormat, sizeInSelectedUnit); + result = Str_Asprintf(NULL, fmt, sizeString); + free(sizeFormat); + free(sizeString); + + return result; +} + + +/* + *---------------------------------------------------------------------- + * + * StrUtil_GetLongestLineLength -- + * + * Given a buffer with one or more lines + * this function computes the length of the + * longest line in a buffer. Input buffer is an array of + * arbitrary bytes (including NUL character), line separator + * is '\n', and is counted in line length. Like: + * "", 0 => 0 + * "\n", 1 => 1 + * "X", 1 => 1 + * "XX\n", 3 => 3 + * "X\nY", 3 => 2 + * "\n\n", 2 => 1 + * + * Results: + * Returns the length of the longest line in the 'buf'. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +size_t +StrUtil_GetLongestLineLength(const char *buf, //IN + size_t bufLength) //IN +{ + size_t longest = 0; + + while (bufLength) { + const char* next; + size_t len; + + next = memchr(buf, '\n', bufLength); + if (next) { + next++; + len = next - buf; + } else { + len = bufLength; + } + if (len > longest) { + longest = len; + } + bufLength -= len; + buf = next; + } + + return longest; +} + + +/* + *---------------------------------------------------------------------- + * + * StrUtil_StartsWith -- + * + * Determines if a string starts with another string. + * + * Results: + * TRUE if 's' starts with 'prefix', FALSE otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +Bool +StrUtil_StartsWith(const char *s, // IN + const char *prefix) // IN +{ + ASSERT(s != NULL); + ASSERT(prefix != NULL); + + while (*prefix && *prefix == *s) { + prefix++; + s++; + } + + return *prefix == '\0'; +} + + +/* + *---------------------------------------------------------------------- + * + * StrUtil_CaselessStartsWith -- + * + * A case-insensitive version of StrUtil_StartsWith. + * + * Results: + * TRUE if 's' starts with 'prefix', FALSE otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +Bool +StrUtil_CaselessStartsWith(const char *s, // IN + const char *prefix) // IN +{ + ASSERT(s != NULL); + ASSERT(prefix != NULL); + + return Str_Strncasecmp(s, prefix, strlen(prefix)) == 0; +} + + +/* + *----------------------------------------------------------------------------- + * + * StrUtil_EndsWith -- + * + * Detects if a string ends with another string. + * + * Results: + * TRUE if string 'suffix' is found at the end of string 's' + * FALSE otherwise. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +Bool +StrUtil_EndsWith(const char *s, // IN + const char *suffix) // IN +{ + size_t slen; + size_t suffixlen; + + ASSERT(s); + ASSERT(suffix); + + slen = strlen(s); + suffixlen = strlen(suffix); + + if (suffixlen > slen) { + return FALSE; + } + + return strcmp(s + slen - suffixlen, suffix) == 0; +} + + +/* + *----------------------------------------------------------------------------- + * + * StrUtil_IsASCII -- + * + * Results: + * Returns TRUE if the string contains only ASCII characters, FALSE + * otherwise. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +Bool +StrUtil_IsASCII(const char *s) // IN +{ + ASSERT(s != NULL); + + while (*s != '\0') { + if (!CType_IsAscii(*s)) { + return FALSE; + } + s++; + } + + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * StrUtil_VDynBufPrintf -- + * + * This is a vprintf() variant which appends directly into a + * dynbuf. The dynbuf is not NUL-terminated: The printf() result + * is written immediately after the last byte in the DynBuf. + * + * This function does not use any temporary buffer. The printf() + * result can be arbitrarily large. This function automatically + * grows the DynBuf as necessary. + * + * Results: + * TRUE on success, FALSE on memory allocation failure. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +Bool +StrUtil_VDynBufPrintf(DynBuf *b, // IN/OUT + const char *fmt, // IN + va_list args) // IN +{ + /* + * Arbitrary lower-limit on buffer size allocation, to avoid doing + * many tiny enlarge operations. + */ + + const size_t minAllocSize = 128; + + while (1) { + int i; + size_t size = DynBuf_GetSize(b); + size_t allocSize = DynBuf_GetAllocatedSize(b); + + /* Make sure the buffer isn't still unallocated */ + if (allocSize < minAllocSize) { + Bool success = DynBuf_Enlarge(b, minAllocSize); + if (!success) { + return FALSE; + } + continue; + } + + /* + * Is there any allocated-but-not-occupied space? If so, try the printf. + * If there was no space to begin with, or Str_Vsnprintf() ran out of + * space, this will fail. + */ + + if (allocSize - size > 0) { + va_list tmpArgs; + + va_copy(tmpArgs, args); + i = Str_Vsnprintf((char *) DynBuf_Get(b) + size, allocSize - size, + fmt, tmpArgs); + va_end(tmpArgs); + } else { + i = -1; + } + + if (i >= 0) { + /* + * Success. Enlarge the buffer. + * + * The ASSERT here is to verify that printf() isn't lying + * about the length of the string it wrote. This actually + * happens, believe it or not. See bug 253674. + */ + + ASSERT(i + size == allocSize || + ((char *)DynBuf_Get(b))[i + size] == '\0'); + + DynBuf_SetSize(b, size + i); + break; + + } else { + /* + * Failure. We must grow the buffer first. Note that this is + * just a minimum size- dynbuf will probably decide to double + * the actual reallocated buffer size. + */ + + Bool success = DynBuf_Enlarge(b, size + minAllocSize); + + if (!success) { + return FALSE; + } + } + } + + return TRUE; +} + + +/* + *----------------------------------------------------------------------------- + * + * StrUtil_DynBufPrintf -- + * + * A StrUtil_VDynBufPrintf() wrapper which takes a variadic argument list. + * + * Results: + * TRUE on success, FALSE on memory allocation failure. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +Bool +StrUtil_DynBufPrintf(DynBuf *b, // IN/OUT + const char *fmt, // IN + ...) // IN +{ + va_list args; + Bool success; + + va_start(args, fmt); + success = StrUtil_VDynBufPrintf(b, fmt, args); + va_end(args); + + return success; +} + + +/* + *----------------------------------------------------------------------------- + * + * StrUtil_SafeDynBufPrintf -- + * + * A 'safe' variant of StrUtil_SafeDynBufPrintf(), which catches + * memory allocation failures and panics. + * + * Results: + * None. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +void +StrUtil_SafeDynBufPrintf(DynBuf *b, // IN/OUT + const char *fmt, // IN + ...) // IN +{ + va_list args; + Bool success; + + va_start(args, fmt); + success = StrUtil_VDynBufPrintf(b, fmt, args); + va_end(args); + + VERIFY(success); +} + + +/* + *----------------------------------------------------------------------------- + * + * StrUtil_SafeStrcat -- + * + * Given an input buffer, append another string and return the resulting + * string. The input buffer is freed along the way. A fancy strcat(). + * + * Results: + * New buffer returned via 'prefix' + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +void +StrUtil_SafeStrcat(char **prefix, // IN/OUT + const char *str) // IN +{ + char *tmp; + size_t plen = *prefix != NULL ? strlen(*prefix) : 0; + size_t slen = strlen(str); + + /* Check for overflow */ + VERIFY((size_t)-1 - plen > slen + 1); + + tmp = Util_SafeRealloc(*prefix, plen + slen + 1 /* NUL */); + + memcpy(tmp + plen, str, slen + 1 /* NUL */); + *prefix = tmp; +} + + +/* + *----------------------------------------------------------------------------- + * + * StrUtil_SafeStrcatFV -- + * + * Given an input buffer, append another string and return the resulting + * string. The input buffer is freed along the way. A fancy vasprintf(). + * + * Results: + * New buffer returned via 'prefix' + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +void +StrUtil_SafeStrcatFV(char **prefix, // IN/OUT + const char *fmt, // IN + va_list args) // IN +{ + char *str = Str_SafeVasprintf(NULL, fmt, args); + StrUtil_SafeStrcat(prefix, str); + free(str); +} + + +/* + *----------------------------------------------------------------------------- + * + * StrUtil_SafeStrcatF -- + * + * Given an input buffer, append another string and return the resulting + * string. The input buffer is freed along the way. A fancy asprintf(). + * + * Results: + * New buffer returned via 'prefix' + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +void +StrUtil_SafeStrcatF(char **prefix, // IN/OUT + const char *fmt, // IN + ...) // IN +{ + va_list args; + + va_start(args, fmt); + StrUtil_SafeStrcatFV(prefix, fmt, args); + va_end(args); +} + + +/* + *----------------------------------------------------------------------------- + * + * StrUtil_TrimWhitespace -- + * + * Return a copy of the input string with leading and trailing + * whitespace removed. + * + * Results: + * See above. Caller should free with free(). + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +char * +StrUtil_TrimWhitespace(const char *str) // IN +{ + char *cur = (char *)str; + char *res = NULL; + size_t len; + + /* Skip leading whitespace. */ + while (*cur && isspace(*cur)) { + cur++; + } + + /* Copy the remaining string. */ + res = Util_SafeStrdup(cur); + + /* Find the beginning of the trailing whitespace. */ + len = strlen(res); + if (len == 0) { + return res; + } + + cur = res + len - 1; + while (cur > res && isspace(*cur)) { + cur--; + } + + /* Truncate it. */ + cur++; + *cur = 0; + + return res; +} diff --git a/open-vm-tools/libDeployPkg/linuxDeployment.c b/open-vm-tools/libDeployPkg/linuxDeployment.c index 951258e3a..c85bbdb26 100644 --- a/open-vm-tools/libDeployPkg/linuxDeployment.c +++ b/open-vm-tools/libDeployPkg/linuxDeployment.c @@ -36,6 +36,8 @@ #include "mspackWrapper.h" #include "rpcout.h" #include "toolsDeployPkg.h" +#include +#include /* * These are covered by #ifndef to give the ability to change these @@ -45,12 +47,17 @@ #define CLEANUPCMD "/bin/rm -r -f " -#ifndef EXTRACTPATH -#define EXTRACTPATH "/tmp/.vmware/linux/deploy" +#ifndef TMP_PATH_VAR +#define TMP_PATH_VAR "/tmp/.vmware/linux/deploy" #endif -#ifndef CLEANUPPATH -#define CLEANUPPATH "/tmp/.vmware" +#ifndef IMC_TMP_PATH_VAR +#define IMC_TMP_PATH_VAR "@@IMC_TMP_PATH_VAR@@" +#endif + +// '/tmp' below will be addressed by PR 1601405. +#ifndef TMP_DIR_PATH_PATTERN +#define TMP_DIR_PATH_PATTERN "/tmp/.vmware-imgcust-dXXXXXX" #endif #ifndef BASEFILENAME @@ -108,7 +115,6 @@ struct List { // Private functions static Bool GetPackageInfo(const char* pkgName, char** cmd, uint8* type); static Bool ExtractZipPackage(const char* pkg, const char* dest); -static Bool CreateDir(const char* path); static void Init(void); static struct List* AddToList(struct List* head, const char* token); static int ListSize(struct List* head); @@ -141,12 +147,21 @@ static LogFunction sLog = NoLogging; * @param args [in] Argument list. * **/ -void -Panic(const char *fmtstr, - va_list args) +NORETURN void +Panic(const char *fmtstr, ...) { - /* Ignored */ - sLog(log_warning, "Panic call back invoked. \n"); + va_list args; + + char *tmp = Util_SafeMalloc(MAXSTRING); + + va_start(args, fmtstr); + vsprintf(tmp, fmtstr, args); + + sLog(log_error, "Panic callback invoked: %s\n", tmp); + + free(tmp); + + exit(1); } // ..................................................................................... @@ -162,12 +177,19 @@ Panic(const char *fmtstr, * **/ void -Debug(const char *fmtstr, - va_list args) +Debug(const char *fmtstr, ...) { - /* Ignored */ #ifdef VMX86_DEBUG - sLog(log_debug, "Debug callback invoked. \n"); + va_list args; + + char *tmp = Util_SafeMalloc(MAXSTRING); + + va_start(args, fmtstr); + vsprintf(tmp, fmtstr, args); + + sLog(log_debug, "Debug callback invoked: %s\n", tmp); + + free(tmp); #endif } @@ -862,11 +884,13 @@ static int Deploy(const char* packageName) { int deployStatus = DEPLOY_SUCCESS; + char* pkgCommand = NULL; char* command = NULL; int deploymentResult; char *nics; char* cleanupCommand; uint8 archiveType; + char *tmpDirPath; // Move to IN PROGRESS state TransitionState(NULL, INPROGRESS); @@ -876,42 +900,49 @@ Deploy(const char* packageName) TOOLSDEPLOYPKG_ERROR_SUCCESS, NULL); + tmpDirPath = mkdtemp((char *)Util_SafeStrdup(TMP_DIR_PATH_PATTERN)); + if (tmpDirPath == NULL) { + SetDeployError("Error creating tmp dir: %s", strerror(errno)); + return DEPLOY_ERROR; + } + sLog(log_info, "Reading cabinet file %s. \n", packageName); // Get the command to execute - if (!GetPackageInfo(packageName, &command, &archiveType)) { + if (!GetPackageInfo(packageName, &pkgCommand, &archiveType)) { SetDeployError("Error extracting package header information. (%s)", GetDeployError()); + free(tmpDirPath); return DEPLOY_ERROR; } - // Print the header command -#ifdef VMX86_DEBUG - sLog(log_debug, "Header command: %s \n ", command); -#endif - - // create the destination directory - if (!CreateDir(EXTRACTPATH "/")) { - free(command); - return DEPLOY_ERROR; + sLog(log_info, "Original deployment command: %s\n", pkgCommand); + if (strstr(pkgCommand, IMC_TMP_PATH_VAR) != NULL) { + command = StrUtil_ReplaceAll(pkgCommand, IMC_TMP_PATH_VAR, tmpDirPath); + } else { + command = StrUtil_ReplaceAll(pkgCommand, TMP_PATH_VAR, tmpDirPath); } + free(pkgCommand); + + sLog(log_info, "Actual deployment command: %s\n", command); if (archiveType == VMWAREDEPLOYPKG_PAYLOAD_TYPE_CAB) { - if (!ExtractCabPackage(packageName, EXTRACTPATH)) { + if (!ExtractCabPackage(packageName, tmpDirPath)) { + free(tmpDirPath); free(command); return DEPLOY_ERROR; } } else if (archiveType == VMWAREDEPLOYPKG_PAYLOAD_TYPE_ZIP) { - if (!ExtractZipPackage(packageName, EXTRACTPATH)) { + if (!ExtractZipPackage(packageName, tmpDirPath)) { + free(tmpDirPath); free(command); return DEPLOY_ERROR; } } // Run the deployment command - sLog(log_info, "Launching deployment %s. \n", command); deploymentResult = ForkExecAndWaitCommand(command); - free (command); + free(command); if (deploymentResult != 0) { sLog(log_error, "Customization process returned with error. \n"); @@ -957,7 +988,7 @@ Deploy(const char* packageName) * sucess/failure of the customization so that at the end of it we always * have nics enabled. */ - nics = GetNicsToEnable(EXTRACTPATH); + nics = GetNicsToEnable(tmpDirPath); if (nics) { // XXX: Sleep before the last SetCustomizationStatusInVmx // This is a temporary-hack for PR 422790 @@ -972,22 +1003,23 @@ Deploy(const char* packageName) } // Clean up command - cleanupCommand = malloc(strlen(CLEANUPCMD) + strlen(CLEANUPPATH) + 1); + cleanupCommand = malloc(strlen(CLEANUPCMD) + strlen(tmpDirPath) + 1); if (!cleanupCommand) { SetDeployError("Error allocating memory."); + free(tmpDirPath); return DEPLOY_ERROR; } strcpy(cleanupCommand, CLEANUPCMD); - strcat(cleanupCommand, CLEANUPPATH); + strcat(cleanupCommand, tmpDirPath); sLog(log_info, "Launching cleanup. \n"); if (ForkExecAndWaitCommand(cleanupCommand) != 0) { - sLog(log_warning, "Error while clean up. Error removing directory %s. (%s)", - EXTRACTPATH, strerror (errno)); - //TODO: What should be done if cleanup fails ?? + sLog(log_warning, "Error while clean up tmp directory %s: (%s)", + tmpDirPath, strerror (errno)); } free (cleanupCommand); + free(tmpDirPath); //Reset the guest OS if (!sSkipReboot && !deploymentResult) { @@ -1248,61 +1280,6 @@ ForkExecAndWaitCommand(const char* command) return retval; } -/** - * Sets up the path for exracting file. For e.g. if the file is /a/b/c/d.abc - * then it creates /a/b/c (skips if any of the directories along the path - * exists). If the the path ends in '/', then the the entire input is assumed - * to be a directory and is created as such. - * - * @param path IN: Complete path of the file - * @return TRUE on success - */ - -Bool -CreateDir(const char* path) -{ - struct stat stats; - char* token; - char* copy; - - // make a copy we can modify - copy = strdup(path); - - // walk through the path (it employs in string replacement) - for (token = copy + 1; *token; ++token) { - - // find - if (*token != '/') { - continue; - } - - /* - * cut it off here for e.g. on first iteration /a/b/c/d.abc will have - * token /a, on second /a/b etc - */ - *token = 0; - - sLog(log_debug, "Creating directory %s", copy); - - // ignore if the directory exists - if (!((stat(copy, &stats) == 0) && S_ISDIR(stats.st_mode))) { - // make directory and check error (accessible only by owner) - if (mkdir(copy, 0700) == -1) { - sLog(log_error, "Unable to create directory %s (%s)", copy, - strerror(errno)); - free(copy); - return FALSE; - } - } - - // restore the token - *token = '/'; - } - - free(copy); - return TRUE; -} - //...................................................................................... /**