简体   繁体   中英

tmpfile() on windows 7 x64

Running the following code on 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;
}

Gives errno 13 (Permission denied), on the 637th and 1004th call, it works fine on XP (haven't tried 7 x86). Am I missing something or is this a bug?

A bit of a refresher from the manpage of on tmpfile() , which returns a FILE* :

The file will be automatically deleted when it is closed or the program terminates.

My verdict for this issue: Deleting a file on Windows is weird.

When you delete a file on Windows, for as long as something holds a handle, you can't call CreateFile on something with the same absolute path, otherwise it will fail with the NT error code STATUS_DELETE_PENDING , which gets mapped to the Win32 code ERROR_ACCESS_DENIED . This is probably where EPERM in errno is coming from. You can confirm this with a tool like Sysinternals Process Monitor.

My guess is that CRT somehow wound up creating a file that has the same name as something it's used before. I've sometimes witnessed that deleting files on Windows can appear asynchronous because some other process (sometimes even an antivirus product, in reaction to the fact that you've just closed a delete-on-close handle...) will leave a handle open to the file, so for some timing window you will see a visible file that you can't get a handle to without hitting delete pending/access denied. Or, it could be that tmpfile has simply chosen a filename that some other process is working on.

To avoid this sort of thing you might want to consider another mechanism for temp files... For example a function like Win32 GetTempFileName allows you to create your own prefix which might make a collision less likely. That function appears to resolve race conditions by retrying if a create fails with "already exists", so be careful about deleting the temp filenames that thing generates - deleting the file cancels your rights to use it concurrently with other processes/threads.

I've got similar problem on Windows 8 - tmpfile() was causing win32 ERROR_ACCESS_DENIED error code - and yes, if you run application with administrator privileges - then it works fine.

I guess problem is mentioned over here: https://lists.gnu.org/archive/html/bug-gnulib/2007-02/msg00162.html

Under Windows, the tmpfile function is defined to always create its temporary file in the root directory. Most users don't have permission to do that, so it will often fail.

I would suspect that this is kinda incomplete windows port issue - so this should be an error reported to Microsoft. (Why to code tmpfile function if it's useless?)

But who have time to fight with Microsoft wind mills?: :-)

I've coded similar implementation using GetTempPathW / GetModuleFileNameW / _wfopen. Code where I've encountered this problem came from libjpeg - I'm attaching whole source code here, but you can pick up code from 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 */
}

}

I'm intentionally ignoring errors from some of functions - have you ever seen GetTempPathW or GetModuleFileNameW failing?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM