tmpfile () на Windows 7 x64 - PullRequest
       32

tmpfile () на Windows 7 x64

2 голосов
/ 06 июня 2011

Запуск следующего кода в Windows 7 x64

#include <stdio.h>
#include <errno.h>

int main() {
    int i;
    FILE *tmp;
    for (i = 0; i < 10000; i++) {
        errno = 0;
        if(!(tmp = tmpfile())) printf("Fail %d, err %d\n", i, errno);
        fclose(tmp);
    }
    return 0;
}

Даёт errno 13 (В доступе отказано), на 637-м и 1004-м вызовах работает нормально на XP (не пробовал 7 x86). Я что-то упустил или это ошибка?

Ответы [ 2 ]

3 голосов
/ 06 июня 2011

Немного обновления из справочной страницы на tmpfile(), которая возвращает FILE*:

Файл будет автоматически удален при закрытии или завершении программы.

Мой вердикт по этому вопросу: удаление файла в Windows - это странно.

Когда вы удаляете файл в Windows, пока что-то содержит дескриптор, вы не можете вызвать CreateFile для чего-то стот же абсолютный путь, в противном случае произойдет сбой с кодом ошибки NT STATUS_DELETE_PENDING, который сопоставляется с кодом Win32 ERROR_ACCESS_DENIED.Это, вероятно, откуда EPERM в errno.Вы можете подтвердить это с помощью такого инструмента, как Sysinternals Process Monitor.

Я предполагаю, что CRT каким-то образом оказался в результате, создав файл, имя которого совпадает с именем, которое использовалось ранее.Я иногда видел, что удаление файлов в Windows может выглядеть асинхронно, потому что какой-то другой процесс (иногда даже антивирусный продукт, в ответ на тот факт, что вы только что закрыли дескриптор удаления при закрытии ...) оставит дескриптороткройте файл, так что для некоторого временного окна вы увидите видимый файл, к которому вы не можете получить дескриптор без нажатия кнопки «Удалить в ожидании» или «Отказано в доступе».Или, возможно, tmpfile просто выбрал имя файла, над которым работает какой-то другой процесс.

Чтобы избежать такого рода вещей, вы можете рассмотреть другой механизм для временных файлов ... Например,Функция, подобная Win32 GetTempFileName, позволяет вам создать свой собственный префикс, который может снизить вероятность столкновения.Эта функция, по-видимому, разрешает условия гонки, повторяя попытку при сбое создания с «уже существует», поэтому будьте осторожны при удалении временных имен файлов, которые генерирует объект - удаление файла отменяет ваши права на использование его одновременно с другими процессами / потоками.

1 голос
/ 10 июня 2016

У меня похожая проблема в Windows 8 - tmpfile () вызывал win32 ERROR_ACCESS_DENIED код ошибки - и да, если вы запускаете приложение с правами администратора - тогда он работает нормально.

Полагаю, здесь упоминается проблема: https://lists.gnu.org/archive/html/bug-gnulib/2007-02/msg00162.html

В Windows функция tmpfile определена, чтобы всегда создавать его временный файл в корневом каталоге. Большинство пользователей не имеют разрешение на это, так что это часто не получится.

Я подозреваю, что это своего рода неполная проблема с портом Windows, поэтому об этой ошибке следует сообщить Microsoft. (Зачем кодировать функцию tmpfile, если она бесполезна?)

Но кто успеет сразиться с ветряными мельницами Microsoft ?! : -)

Я кодировал аналогичную реализацию, используя GetTempPathW / GetModuleFileNameW / _wfopen. Код, с которым я столкнулся, столкнулся с libjpeg - здесь я прилагаю весь исходный код, но вы можете получить код из jpeg_open_backing_store.

jmemwin.cpp:

//
//  Windows port for jpeg lib functions.
//
#define JPEG_INTERNALS
#include <Windows.h>        // GetTempFileName
#undef FAR                  // Will be redefined - disable warning
#include "jinclude.h"
#include "jpeglib.h"

extern "C" {
#include "jmemsys.h"        // jpeg_ api interface.

//
// Memory allocation and freeing are controlled by the regular library routines malloc() and free().
//

GLOBAL(void *) jpeg_get_small (j_common_ptr cinfo, size_t sizeofobject)
{
    return (void *) malloc(sizeofobject);
}

GLOBAL(void) jpeg_free_small (j_common_ptr cinfo, void * object, size_t sizeofobject)
{
    free(object);
}

/*
 * "Large" objects are treated the same as "small" ones.
 * NB: although we include FAR keywords in the routine declarations,
 * this file won't actually work in 80x86 small/medium model; at least,
 * you probably won't be able to process useful-size images in only 64KB.
 */

GLOBAL(void FAR *) jpeg_get_large (j_common_ptr cinfo, size_t sizeofobject)
{
    return (void FAR *) malloc(sizeofobject);
}

GLOBAL(void) jpeg_free_large (j_common_ptr cinfo, void FAR * object, size_t sizeofobject)
{
    free(object);
}

//
//  Used only by command line applications, not by static library compilation
//
#ifndef DEFAULT_MAX_MEM     /* so can override from makefile */
#define DEFAULT_MAX_MEM     1000000L /* default: one megabyte */
#endif

GLOBAL(long) jpeg_mem_available (j_common_ptr cinfo, long min_bytes_needed, long max_bytes_needed, long already_allocated)
{
    // jmemansi.c's jpeg_mem_available implementation was insufficient for some of .jpg loads.
    MEMORYSTATUSEX status = { 0 };
    status.dwLength = sizeof(status);
    GlobalMemoryStatusEx(&status);

    if( status.ullAvailPhys > LONG_MAX )
        // Normally goes here since new PC's have more than 4 Gb of ram.
        return LONG_MAX;

    return (long) status.ullAvailPhys;
}


/*
    Backing store (temporary file) management.
    Backing store objects are only used when the value returned by
    jpeg_mem_available is less than the total space needed.  You can dispense
    with these routines if you have plenty of virtual memory; see jmemnobs.c.
*/

METHODDEF(void) read_backing_store (j_common_ptr cinfo, backing_store_ptr info, void FAR * buffer_address, long file_offset, long byte_count)
{
    if (fseek(info->temp_file, file_offset, SEEK_SET))
        ERREXIT(cinfo, JERR_TFILE_SEEK);

    size_t readed = fread( buffer_address, 1, byte_count, info->temp_file);

    if (readed != (size_t) byte_count)
        ERREXIT(cinfo, JERR_TFILE_READ);
}


METHODDEF(void)
write_backing_store (j_common_ptr cinfo, backing_store_ptr info, void FAR * buffer_address, long file_offset, long byte_count)
{
    if (fseek(info->temp_file, file_offset, SEEK_SET))
        ERREXIT(cinfo, JERR_TFILE_SEEK);

    if (JFWRITE(info->temp_file, buffer_address, byte_count) != (size_t) byte_count)
        ERREXIT(cinfo, JERR_TFILE_WRITE);

    // E.g. if you need to debug writes.
    //if( fflush(info->temp_file) != 0 )
    //    ERREXIT(cinfo, JERR_TFILE_WRITE);
}


METHODDEF(void)
close_backing_store (j_common_ptr cinfo, backing_store_ptr info)
{
    fclose(info->temp_file);
    // File is deleted using 'D' flag on open.
}

static HMODULE DllHandle()
{
    MEMORY_BASIC_INFORMATION info;
    VirtualQuery(DllHandle, &info, sizeof(MEMORY_BASIC_INFORMATION));
    return (HMODULE)info.AllocationBase;
}

GLOBAL(void) jpeg_open_backing_store(j_common_ptr cinfo, backing_store_ptr info, long total_bytes_needed)
{
    // Generate unique filename.
    wchar_t path[ MAX_PATH ] = { 0 };
    wchar_t dllPath[ MAX_PATH ] = { 0 };
    GetTempPathW( MAX_PATH, path );

    // Based on .exe or .dll filename
    GetModuleFileNameW( DllHandle(), dllPath, MAX_PATH );

    wchar_t* p = wcsrchr( dllPath, L'\\');
    wchar_t* ext = wcsrchr( p + 1, L'.');

    if( ext ) *ext = 0;
    wchar_t* outFile = path + wcslen(path);

    static int iTempFileId = 1;
    // Based on process id (so processes would not fight with each other)
    // Based on some process global id.
    wsprintfW(outFile, L"%s_%d_%d.tmp",p + 1, GetCurrentProcessId(), iTempFileId++ );

    // 'D' - temporary file.
    if ((info->temp_file = _wfopen(path, L"w+bD") ) == NULL)
        ERREXITS(cinfo, JERR_TFILE_CREATE, "");

    info->read_backing_store = read_backing_store;
    info->write_backing_store = write_backing_store;
    info->close_backing_store = close_backing_store;
} //jpeg_open_backing_store


/*
 * These routines take care of any system-dependent initialization and
 * cleanup required.
 */

GLOBAL(long)
jpeg_mem_init (j_common_ptr cinfo)
{
     return DEFAULT_MAX_MEM;    /* default for max_memory_to_use */
}

GLOBAL(void)
jpeg_mem_term (j_common_ptr cinfo)
{
  /* no work */
}

}

Я намеренно игнорирую ошибки некоторых функций - вы когда-нибудь видели сбой GetTempPathW или GetModuleFileNameW?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...