From patchwork Sun Feb 13 12:28:02 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: nihil-admirari@tutanota.com X-Patchwork-Id: 34261 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6838:14aa:0:0:0:0 with SMTP id bz10csp4709586nkb; Sun, 13 Feb 2022 04:28:14 -0800 (PST) X-Google-Smtp-Source: ABdhPJzvypGVJi8HKz6IUNW+wpNhQE0SjgmKnxjm3fIsiEV78TEHAD0u9moA6JhfJUhHP73xPoZq X-Received: by 2002:a05:6402:3711:: with SMTP id ek17mr10413463edb.430.1644755293920; Sun, 13 Feb 2022 04:28:13 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1644755293; cv=none; d=google.com; s=arc-20160816; b=OvKzkBzqo0Ft7DRis1L8nGS2bCnKRWxKtZ3J6tNp9kCPsOkWZbih6LfzMKZ/EcTp4L 4YipuWvivnvNE2+s88E0FNxHvBH3RW+by6aqcNKN8ZkSgDTdGMY5pOZ9O19aQiNXIihR u9RNEjQkQLzoyR+w52R56cqhfd+IFcZfJn677NGUM8E3SILVYRXWaxESo4XZ8Dqv2sbD t3scWXogSBcCAUxM4Dnl0vhauom14jARiXXO7M5XxizG+tKF5jHCo/nC5qdLf1eCNkNF 4gChmu3g9rK9oMUlCTrQ0Z156LRyjNG7Q29ygVTnGS3G5cN/WBJqnh/JtDhrQD1lIOcm i0jw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:reply-to:list-subscribe :list-help:list-post:list-archive:list-unsubscribe:list-id :precedence:subject:mime-version:message-id:to:from:date :dkim-signature:delivered-to; bh=yfaXVtWyry7wQTsSPd4M/BIqiXtd0ICBWrJ8wIF15RM=; b=ZVt7tLqS5U5TslqRiJ2BLSfL81yk+xVe2LLQC819B8kPkw8J8xLTPOoYbDfk2gtizG sQ76bz+cj7nM5W89MA8NlLZh4lnj2VqcJDW1Hxhs4j9NSTCBgJN8t8+6S+z0v66+7Sh8 8lEUATigatjS2l+j948jjRm9HtIjNFzfzM/r0m5bAk6z1zaTQkpbA05I/FZunqUp8Lt1 4wlCNyXhUrd1lX3mQcP1uIPqhkwH3hyyYGe8CT+wIHIaGws3KiDrV2LyofFbbbJvptPS svZNmMwF/bniiDnCjlN3cEBe/8qzH1yWtMp4oxCu9xWNgZLNUI5gyvOzqlmAT/oKkq2m areA== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@tutanota.com header.s=s1 header.b=MnMPKFKs; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=tutanota.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id s23si648648edr.659.2022.02.13.04.28.13; Sun, 13 Feb 2022 04:28:13 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@tutanota.com header.s=s1 header.b=MnMPKFKs; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=tutanota.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 97FE668B167; Sun, 13 Feb 2022 14:28:09 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from w1.tutanota.de (w1.tutanota.de [81.3.6.162]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 03A2968A048 for ; Sun, 13 Feb 2022 14:28:03 +0200 (EET) Received: from w3.tutanota.de (unknown [192.168.1.164]) by w1.tutanota.de (Postfix) with ESMTP id 78C52FBF3B2 for ; Sun, 13 Feb 2022 12:28:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1644755282; s=s1; d=tutanota.com; h=From:From:To:To:Subject:Subject:Content-Description:Content-ID:Content-Type:Content-Type:Content-Transfer-Encoding:Content-Transfer-Encoding:Cc:Date:Date:In-Reply-To:MIME-Version:MIME-Version:Message-ID:Message-ID:Reply-To:References:Sender; bh=HltsOeKwIjPje1kyteZ2Lju5AisIEpKG2piNQLiwpfo=; b=MnMPKFKssatxkV+ZgDy76AP3XqznKOf5nSxjx2csy475dB3tkTmeKbdnknw5II1d 8Hc9Mk0AeqTizd7Z9cGRzqoPi0Nwg5Z2PWSiuBdqmOdW46mZENeio8/sK9UVf5eIEkG Ct10IWHxcsgdFdUIwseA+xQIihhRf2rgXqyHinTEy4Gh2bLJyuYklB2aWRwAanqSo9k TGMyGUZ3QDPbx+WKHkeBp2imHY19G9zo23kHMSNhcrcEIaNp1IPp4PcTFlKjBB1sj9M B5QsH4scSw1MSj0lkiJ7R5O1ElvQvND7zPpLZ3UctyfmEj9ti9lU0it+HFHPgHU/gdu 5/rviEW5fA== Date: Sun, 13 Feb 2022 13:28:02 +0100 (CET) From: nihil-admirari@tutanota.com To: ffmpeg-devel@ffmpeg.org Message-ID: MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH] Long path support for Windows (fixes #8885) X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: Trwje+XK8zDB Long path support is enabled by adding a manifest to all of fftools. MAX_PATH-sized buffers are replaced with dynamically sized ones. Signed-off-by: nihil-admirari <50202386+nihil-admirari@users.noreply.github.com> Signed-off-by: nihil-admirari <50202386+nihil-admirari@users.noreply.github.com> ---  compat/w32dlfcn.h                | 58 +++++++++++++++++++++++++++-----  fftools/Makefile                 |  5 +++  fftools/cmdutils.c               | 31 +++++++++++++----  fftools/long_paths_utf8.manifest | 12 +++++++  fftools/long_paths_utf8.rc       |  3 ++  libavformat/avisynth.c           | 10 +++---  libavutil/wchar_filename.h       | 37 ++++++++++++++++++++  7 files changed, 136 insertions(+), 20 deletions(-)  create mode 100644 fftools/long_paths_utf8.manifest  create mode 100644 fftools/long_paths_utf8.rc diff --git a/compat/w32dlfcn.h b/compat/w32dlfcn.h index 52a94ef..a36e97c 100644 --- a/compat/w32dlfcn.h +++ b/compat/w32dlfcn.h @@ -25,6 +25,30 @@  #if (_WIN32_WINNT < 0x0602) || HAVE_WINRT  #include "libavutil/wchar_filename.h"  #endif + +static inline wchar_t *get_module_filename(const HMODULE module) +{ +    wchar_t *path = NULL; +    int path_size = 0, path_len = 0; + +    do { +        path_size = path_size ? 1.5 * path_size : MAX_PATH; +        wchar_t *new_path = av_realloc_array(path, path_size, sizeof *path); +        if (!new_path) { +            av_free(path); +            return NULL; +        } +        path = new_path; +        path_len = GetModuleFileNameW(module, path, path_size); +    } while (path_len && path_size <= 32768 && path_size <= path_len); + +    if (!path_len) { +        av_free(path); +        return NULL; +    } +    return path; +} +  /**   * Safe function used to open dynamic libs. This attempts to improve program security   * by removing the current directory from the dll search path. Only dll's found in the @@ -38,24 +62,40 @@ static inline HMODULE win32_dlopen(const char *name)      // Need to check if KB2533623 is available      if (!GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "SetDefaultDllDirectories")) {          HMODULE module = NULL; -        wchar_t *path = NULL, *name_w = NULL; -        DWORD pathlen; +        wchar_t *path = NULL, *new_path = NULL, *name_w = NULL; +        DWORD pathlen, pathsize, namelen;          if (utf8towchar(name, &name_w))              goto exit; -        path = (wchar_t *)av_calloc(MAX_PATH, sizeof(wchar_t)); +        namelen = wcslen(name_w);          // Try local directory first -        pathlen = GetModuleFileNameW(NULL, path, MAX_PATH); -        pathlen = wcsrchr(path, '\\') - path; -        if (pathlen == 0 || pathlen + wcslen(name_w) + 2 > MAX_PATH) +        path = get_module_filename(NULL); +        pathlen = path ? wcsrchr(path, '\\') - path : 0; +        if (pathlen == 0) +            goto exit; +        pathsize = pathlen + namelen + 2; +        new_path = av_realloc_array(path, pathsize, sizeof *path); +        if (!new_path)              goto exit; -        path[pathlen] = '\\'; +        path = new_path;          wcscpy(path + pathlen + 1, name_w);          module = LoadLibraryExW(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);          if (module == NULL) {              // Next try System32 directory -            pathlen = GetSystemDirectoryW(path, MAX_PATH); -            if (pathlen == 0 || pathlen + wcslen(name_w) + 2 > MAX_PATH) +            pathlen = GetSystemDirectoryW(path, pathsize); +            if (pathlen == 0)                  goto exit; +            if (pathlen + namelen + 2 > pathsize) { +                pathsize = pathlen + namelen + 2; +                new_path = av_realloc_array(path, pathsize, sizeof *path); +                if (!new_path) +                    goto exit; +                path = new_path; +                // The buffer might have been not enough for system directory +                // in the first place. +                pathlen = GetSystemDirectoryW(path, pathsize); +                if (pathlen == 0) +                    goto exit; +            }              path[pathlen] = '\\';              wcscpy(path + pathlen + 1, name_w);              module = LoadLibraryExW(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); diff --git a/fftools/Makefile b/fftools/Makefile index da42078..53438b6 100644 --- a/fftools/Makefile +++ b/fftools/Makefile @@ -11,6 +11,11 @@ ALLAVPROGS_G = $(AVBASENAMES:%=%$(PROGSSUF)_g$(EXESUF))    OBJS-ffmpeg                        += fftools/ffmpeg_opt.o fftools/ffmpeg_filter.o fftools/ffmpeg_hw.o   +# Windows resource files +OBJS-ffmpeg-$(HAVE_GNU_WINDRES) += fftools/long_paths_utf8.o +OBJS-ffplay-$(HAVE_GNU_WINDRES) += fftools/long_paths_utf8.o +OBJS-ffprobe-$(HAVE_GNU_WINDRES) += fftools/long_paths_utf8.o +  define DOFFTOOL  OBJS-$(1) += fftools/cmdutils.o fftools/$(1).o $(OBJS-$(1)-yes)  $(1)$(PROGSSUF)_g$(EXESUF): $$(OBJS-$(1)) diff --git a/fftools/cmdutils.c b/fftools/cmdutils.c index 4b50e15..ea78897 100644 --- a/fftools/cmdutils.c +++ b/fftools/cmdutils.c @@ -62,6 +62,7 @@  #endif  #ifdef _WIN32  #include +#include "compat/w32dlfcn.h"  #endif    static int init_report(const char *env); @@ -2065,6 +2066,9 @@ FILE *get_preset_file(char *filename, size_t filename_size,  {      FILE *f = NULL;      int i; +#if HAVE_GETMODULEHANDLE && defined(_WIN32) +    char *datadir = NULL; +#endif      const char *base[3] = { getenv("FFMPEG_DATADIR"),                              getenv("HOME"),                              FFMPEG_DATADIR, }; @@ -2074,19 +2078,31 @@ FILE *get_preset_file(char *filename, size_t filename_size,          f = fopen(filename, "r");      } else {  #if HAVE_GETMODULEHANDLE && defined(_WIN32) -        char datadir[MAX_PATH], *ls; +        wchar_t *datadir_w = get_module_filename(NULL);          base[2] = NULL;   -        if (GetModuleFileNameA(GetModuleHandleA(NULL), datadir, sizeof(datadir) - 1)) +        if (wchartoansi(datadir_w, &datadir)) +            datadir = NULL; +        av_free(datadir_w); + +        if (datadir)          { -            for (ls = datadir; ls < datadir + strlen(datadir); ls++) +            char *ls; +            for (ls = datadir; *ls; ls++)                  if (*ls == '\\') *ls = '/';                if (ls = strrchr(datadir, '/'))              { -                *ls = 0; -                strncat(datadir, "/ffpresets",  sizeof(datadir) - 1 - strlen(datadir)); -                base[2] = datadir; +                const int datadir_len = ls - datadir; +                const int desired_size = datadir_len + strlen("/ffpresets") + 1; +                char *new_datadir = av_realloc_array( +                    datadir, desired_size, sizeof *datadir); +                if (new_datadir) { +                    datadir = new_datadir; +                    datadir[datadir_len] = 0; +                    strncat(datadir, "/ffpresets",  desired_size - 1 - datadir_len); +                    base[2] = datadir; +                }              }          }  #endif @@ -2106,6 +2122,9 @@ FILE *get_preset_file(char *filename, size_t filename_size,          }      }   +#if HAVE_GETMODULEHANDLE && defined(_WIN32) +    av_free(datadir); +#endif      return f;  }   diff --git a/fftools/long_paths_utf8.manifest b/fftools/long_paths_utf8.manifest new file mode 100644 index 0000000..d1ac1e4 --- /dev/null +++ b/fftools/long_paths_utf8.manifest @@ -0,0 +1,12 @@ + + + +    +      true +      UTF-8 +    + diff --git a/fftools/long_paths_utf8.rc b/fftools/long_paths_utf8.rc new file mode 100644 index 0000000..f33de76 --- /dev/null +++ b/fftools/long_paths_utf8.rc @@ -0,0 +1,3 @@ +#include + +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "long_paths_utf8.manifest" diff --git a/libavformat/avisynth.c b/libavformat/avisynth.c index 350ac6d..3738cb1 100644 --- a/libavformat/avisynth.c +++ b/libavformat/avisynth.c @@ -34,6 +34,7 @@  /* Platform-specific directives. */  #ifdef _WIN32    #include "compat/w32dlfcn.h" +  #include "libavutil/wchar_filename.h"    #undef EXTERN_C    #define AVISYNTH_LIB "avisynth"  #else @@ -572,8 +573,7 @@ static int avisynth_open_file(AVFormatContext *s)      AVS_Value arg, val;      int ret;  #ifdef _WIN32 -    char filename_ansi[MAX_PATH * 4]; -    wchar_t filename_wc[MAX_PATH * 4]; +    char *filename_ansi = NULL;  #endif        if (ret = avisynth_context_create(s)) @@ -581,10 +581,10 @@ static int avisynth_open_file(AVFormatContext *s)    #ifdef _WIN32      /* Convert UTF-8 to ANSI code page */ -    MultiByteToWideChar(CP_UTF8, 0, s->url, -1, filename_wc, MAX_PATH * 4); -    WideCharToMultiByte(CP_THREAD_ACP, 0, filename_wc, -1, filename_ansi, -                        MAX_PATH * 4, NULL, NULL); +    if (utf8toansi(s->url, &filename_ansi)) +        goto fail;      arg = avs_new_value_string(filename_ansi); +    av_free(filename_ansi);  #else      arg = avs_new_value_string(s->url);  #endif diff --git a/libavutil/wchar_filename.h b/libavutil/wchar_filename.h index 90f0824..32260a4 100644 --- a/libavutil/wchar_filename.h +++ b/libavutil/wchar_filename.h @@ -40,6 +40,43 @@ static inline int utf8towchar(const char *filename_utf8, wchar_t **filename_w)      MultiByteToWideChar(CP_UTF8, 0, filename_utf8, -1, *filename_w, num_chars);      return 0;  } + +av_warn_unused_result +static inline int wchartoansi(const wchar_t *filename_w, char **filename) +{ +    const int num_chars = WideCharToMultiByte(CP_THREAD_ACP, 0, filename_w, -1, +                                              NULL, 0, NULL, NULL); +    if (num_chars <= 0) { +        *filename = NULL; +        return 0; +    } +    *filename = (char *)av_calloc(num_chars, sizeof(char)); +    if (!*filename) { +        errno = ENOMEM; +        return -1; +    } +    WideCharToMultiByte(CP_THREAD_ACP, 0, filename_w, -1, +                        *filename, num_chars, NULL, NULL); +    return 0; +} + +av_warn_unused_result +static inline int utf8toansi(const char *filename_utf8, char **filename) +{ +    wchar_t *filename_w = NULL; +    int ret = -1; +    if (utf8towchar(filename_utf8, &filename_w)) +        return -1; + +    if (!filename_w) { +        *filename = NULL; +        return 0; +    } + +    ret = wchartoansi(filename_w, filename); +    av_free(filename_w); +    return ret; +}  #endif    #endif /* AVUTIL_WCHAR_FILENAME_H */