From 18854bf4bc4fa6e736fed7af8caee7501172d4e2 Mon Sep 17 00:00:00 2001 From: Valerie Snyder Date: Mon, 11 Aug 2025 18:02:09 -0400 Subject: [PATCH] Windows: Fix filepath basename issue On Windows, the cli_basename function should treat both '/' and '\' as path separators. Most Windows APIs also accept both. On Linux/Unix, it makes sense when using a filepath that is more for informational purposes or where it may have come from a Windows system, to treat the '\' as a path separator. But in situations where the the path is needed for some critical action, like moving or deleting a file, we can't treat it as a path separator. --- common/actions.c | 4 ++-- libclamav/fmap.c | 2 +- libclamav/scanners.c | 12 ++++++------ libclamav/str.c | 29 ++++++++++++++++++++-------- libclamav/str.h | 12 ++++++++++-- libclamav/unzip.c | 2 +- libfreshclam/libfreshclam_internal.c | 2 +- 7 files changed, 42 insertions(+), 21 deletions(-) diff --git a/common/actions.c b/common/actions.c index 6e48172c2..07bbaecdc 100644 --- a/common/actions.c +++ b/common/actions.c @@ -452,7 +452,7 @@ static int traverse_rename(const char *source, const char *destination) #endif #ifndef _WIN32 - ret = cli_basename(source, strlen(source), &source_basename); + ret = cli_basename(source, strlen(source), &source_basename, false /* posix_support_backslash_pathsep */); if (CL_SUCCESS != ret) { logg(LOGG_INFO, "traverse_rename: Failed to get basename of source path:%s\n\tError: %d\n", source, (int)ret); goto done; @@ -564,7 +564,7 @@ static int traverse_unlink(const char *target) goto done; } - ret = cli_basename(target, strlen(target), &target_basename); + ret = cli_basename(target, strlen(target), &target_basename, false /* posix_support_backslash_pathsep */); if (CL_SUCCESS != ret) { logg(LOGG_INFO, "traverse_unlink: Failed to get basename of target path: %s\n\tError: %d\n", target, (int)ret); goto done; diff --git a/libclamav/fmap.c b/libclamav/fmap.c index 1bf11fcba..d45eb02ec 100644 --- a/libclamav/fmap.c +++ b/libclamav/fmap.c @@ -995,7 +995,7 @@ cl_error_t fmap_dump_to_file(fmap_t *map, const char *filepath, const char *tmpd /* Create a filename prefix that includes the original filename, if available */ if (filepath != NULL) { - if (CL_SUCCESS != cli_basename(filepath, strlen(filepath), &filebase)) { + if (CL_SUCCESS != cli_basename(filepath, strlen(filepath), &filebase, true /* posix_support_backslash_pathsep */)) { cli_dbgmsg("fmap_dump_to_file: Unable to determine basename from filepath.\n"); } else if ((start_offset != 0) && (end_offset != map->real_len)) { /* If we're only dumping a portion of the file, include the offsets in the prefix,... diff --git a/libclamav/scanners.c b/libclamav/scanners.c index db4018149..c8b1686c7 100644 --- a/libclamav/scanners.c +++ b/libclamav/scanners.c @@ -389,7 +389,7 @@ static cl_error_t cli_scanrar_file(const char *filepath, int desc, cli_ctx *ctx) * Extract the file... */ if (0 != metadata.filename[0]) { - (void)cli_basename(metadata.filename, strlen(metadata.filename), &filename_base); + (void)cli_basename(metadata.filename, strlen(metadata.filename), &filename_base, true /* posix_support_backslash_pathsep */); } if (!(ctx->engine->keeptmp) || @@ -837,7 +837,7 @@ static cl_error_t cli_scanegg(cli_ctx *ctx) * Drop to a temp file, if requested. */ if (NULL != metadata.filename) { - (void)cli_basename(metadata.filename, strlen(metadata.filename), &filename_base); + (void)cli_basename(metadata.filename, strlen(metadata.filename), &filename_base, true /* posix_support_backslash_pathsep */); } if (ctx->engine->keeptmp) { @@ -4716,7 +4716,7 @@ cl_error_t cli_magic_scan(cli_ctx *ctx, cli_file_t type) * Keep-temp enabled, so create a sub-directory to provide extraction directory recursion. */ if ((NULL != ctx->fmap->name) && - (CL_SUCCESS == cli_basename(ctx->fmap->name, strlen(ctx->fmap->name), &fmap_basename))) { + (CL_SUCCESS == cli_basename(ctx->fmap->name, strlen(ctx->fmap->name), &fmap_basename, true /* posix_support_backslash_pathsep */))) { /* * The fmap has a name, lets include it in the new sub-directory. */ @@ -5910,7 +5910,7 @@ static cl_error_t scan_common(cl_fmap_t *map, const char *filepath, const char * if ((ctx.engine->keeptmp) && (NULL != ctx.target_filepath) && - (CL_SUCCESS == cli_basename(ctx.target_filepath, strlen(ctx.target_filepath), &target_basename))) { + (CL_SUCCESS == cli_basename(ctx.target_filepath, strlen(ctx.target_filepath), &target_basename, true /* posix_support_backslash_pathsep */))) { /* Include the basename in the temp directory */ new_temp_prefix_len = strlen("YYYYMMDD_HHMMSS-") + strlen(target_basename); new_temp_prefix = cli_max_calloc(1, new_temp_prefix_len + 1); @@ -6186,7 +6186,7 @@ cl_error_t cl_scandesc_callback(int desc, const char *filename, const char **vir } if (NULL != filename) { - (void)cli_basename(filename, strlen(filename), &filename_base); + (void)cli_basename(filename, strlen(filename), &filename_base, true /* posix_support_backslash_pathsep */); } if (NULL == (map = fmap(desc, 0, sb.st_size, filename_base))) { @@ -6226,7 +6226,7 @@ cl_error_t cl_scanmap_callback(cl_fmap_t *map, const char *filename, const char if (NULL != filename && map->name == NULL) { // Use the provided name for the fmap name if one wasn't already set. - cli_basename(filename, strlen(filename), &map->name); + cli_basename(filename, strlen(filename), &map->name, true /* posix_support_backslash_pathsep */); } return scan_common(map, filename, virname, scanned, engine, scanoptions, context); diff --git a/libclamav/str.c b/libclamav/str.c index 4de1b6d1f..9c6b4a5cf 100644 --- a/libclamav/str.c +++ b/libclamav/str.c @@ -424,8 +424,7 @@ char *cli_strrcpy(char *dest, const char *source) /* by NJH */ return NULL; } - while ((*dest++ = *source++)) - ; + while ((*dest++ = *source++)); return --dest; } @@ -470,8 +469,7 @@ char *__cli_strndup(const char *s, size_t n) size_t __cli_strnlen(const char *s, size_t n) { size_t i = 0; - for (; (i < n) && s[i] != '\0'; ++i) - ; + for (; (i < n) && s[i] != '\0'; ++i); return i; } @@ -1058,8 +1056,11 @@ int cli_hexnibbles(char *str, int len) return 0; } -cl_error_t cli_basename(const char *filepath, size_t filepath_len, - char **filebase) +cl_error_t cli_basename( + const char *filepath, + size_t filepath_len, + char **filebase, + bool posix_support_backslash_pathsep) { cl_error_t status = CL_EARG; const char *index = NULL; @@ -1071,13 +1072,25 @@ cl_error_t cli_basename(const char *filepath, size_t filepath_len, index = filepath + filepath_len - 1; +#ifdef _WIN32 while (index > filepath) { - if (index[0] == PATHSEP[0]) + if ((index[0] == '/') || (index[0] == '\\')) break; index--; } - if ((index != filepath) || (index[0] == PATHSEP[0])) + if ((index != filepath) || (index[0] == '/') || (index[0] == '\\')) { index++; + } +#else + while (index > filepath) { + if ((index[0] == '/') || (index[0] == '\\' && posix_support_backslash_pathsep)) + break; + index--; + } + if ((index != filepath) || (index[0] == '/') || (index[0] == '\\' && posix_support_backslash_pathsep)) { + index++; + } +#endif if (0 == CLI_STRNLEN(index, filepath_len - (index - filepath))) { cli_dbgmsg("cli_basename: Provided path does not include a file name.\n"); diff --git a/libclamav/str.h b/libclamav/str.h index 9b4b0b7f3..344b3b964 100644 --- a/libclamav/str.h +++ b/libclamav/str.h @@ -96,15 +96,23 @@ int cli_hexnibbles(char *str, int len); size_t cli_strlcat(char *dst, const char *src, size_t sz); /* libclamav/strlcat.c */ /** - * @brief Get the file basename including extension from a file path. + * @brief Get the file basename including extension from a file path. + * + * Will treat both '/' and '\' as path separators. * * Caller is responsible for freeing filebase. * An empty string will be returned if the caller inputs a directory with a trailing slash (no file). * * @param filepath The filepath in question. * @param[out] filebase An allocated string containing the file basename. + * @param posix_support_backslash_pathsep Whether to treat backslashes as path separators on Linux/Unix systems. + * * @return cl_error_t CL_SUCCESS, CL_EARG, CL_EFORMAT, or CL_EMEM */ -cl_error_t cli_basename(const char *filepath, size_t filepath_len, char **filebase); +cl_error_t cli_basename( + const char *filepath, + size_t filepath_len, + char **filebase, + bool posix_support_backslash_pathsep); #endif diff --git a/libclamav/unzip.c b/libclamav/unzip.c index fd0a8dab6..3abaf41e6 100644 --- a/libclamav/unzip.c +++ b/libclamav/unzip.c @@ -658,7 +658,7 @@ static cl_error_t parse_local_file_header( src = fmap_need_ptr_once(map, zip, name_size); if (name_size && (NULL != src)) { memcpy(name, zip, name_size); - if (CL_SUCCESS != cli_basename(name, name_size, &original_filename)) { + if (CL_SUCCESS != cli_basename(name, name_size, &original_filename, true /* posix_support_backslash_pathsep */)) { original_filename = NULL; } } diff --git a/libfreshclam/libfreshclam_internal.c b/libfreshclam/libfreshclam_internal.c index e26b00d74..38f223c48 100644 --- a/libfreshclam/libfreshclam_internal.c +++ b/libfreshclam/libfreshclam_internal.c @@ -2623,7 +2623,7 @@ fc_error_t updatedb( logg(LOGG_DEBUG, "updatedb: Moving signature file %s to database directory\n", signfile); // get the basename of the signfile - if (CL_SUCCESS != cli_basename(signfile, strlen(signfile), &newSignFilename)) { + if (CL_SUCCESS != cli_basename(signfile, strlen(signfile), &newSignFilename, false /* posix_support_backslash_pathsep */)) { logg(LOGG_ERROR, "updatedb: Failed to get basename of '%s'\n", signfile); goto done; }