194 lines
4.9 KiB
C
194 lines
4.9 KiB
C
|
/* ftruncate emulations for native Windows.
|
||
|
Copyright (C) 1992-2020 Free Software Foundation, Inc.
|
||
|
|
||
|
This program is free software; you can redistribute it and/or modify
|
||
|
it under the terms of the GNU General Public License as published by
|
||
|
the Free Software Foundation; either version 3, or (at your option)
|
||
|
any 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
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License along
|
||
|
with this program; if not, see <https://www.gnu.org/licenses/>. */
|
||
|
|
||
|
#include <config.h>
|
||
|
|
||
|
/* Specification. */
|
||
|
#include <unistd.h>
|
||
|
|
||
|
#if HAVE_CHSIZE
|
||
|
/* A native Windows platform. */
|
||
|
|
||
|
# include <errno.h>
|
||
|
|
||
|
# if _GL_WINDOWS_64_BIT_OFF_T
|
||
|
|
||
|
/* Large File Support: off_t is 64-bit, but chsize() takes only a 32-bit
|
||
|
argument. So, define a 64-bit safe SetFileSize function ourselves. */
|
||
|
|
||
|
/* Ensure that <windows.h> declares GetFileSizeEx. */
|
||
|
# undef _WIN32_WINNT
|
||
|
# define _WIN32_WINNT _WIN32_WINNT_WIN2K
|
||
|
|
||
|
/* Get declarations of the native Windows API functions. */
|
||
|
# define WIN32_LEAN_AND_MEAN
|
||
|
# include <windows.h>
|
||
|
|
||
|
/* Get _get_osfhandle. */
|
||
|
# if GNULIB_MSVC_NOTHROW
|
||
|
# include "msvc-nothrow.h"
|
||
|
# else
|
||
|
# include <io.h>
|
||
|
# endif
|
||
|
|
||
|
static BOOL
|
||
|
SetFileSize (HANDLE h, LONGLONG size)
|
||
|
{
|
||
|
LARGE_INTEGER old_size;
|
||
|
|
||
|
if (!GetFileSizeEx (h, &old_size))
|
||
|
return FALSE;
|
||
|
|
||
|
if (size != old_size.QuadPart)
|
||
|
{
|
||
|
/* Duplicate the handle, so we are free to modify its file position. */
|
||
|
HANDLE curr_process = GetCurrentProcess ();
|
||
|
HANDLE tmph;
|
||
|
|
||
|
if (!DuplicateHandle (curr_process, /* SourceProcessHandle */
|
||
|
h, /* SourceHandle */
|
||
|
curr_process, /* TargetProcessHandle */
|
||
|
(PHANDLE) &tmph, /* TargetHandle */
|
||
|
(DWORD) 0, /* DesiredAccess */
|
||
|
FALSE, /* InheritHandle */
|
||
|
DUPLICATE_SAME_ACCESS)) /* Options */
|
||
|
return FALSE;
|
||
|
|
||
|
if (size < old_size.QuadPart)
|
||
|
{
|
||
|
/* Reduce the size. */
|
||
|
LONG size_hi = (LONG) (size >> 32);
|
||
|
if (SetFilePointer (tmph, (LONG) size, &size_hi, FILE_BEGIN)
|
||
|
== INVALID_SET_FILE_POINTER
|
||
|
&& GetLastError() != NO_ERROR)
|
||
|
{
|
||
|
CloseHandle (tmph);
|
||
|
return FALSE;
|
||
|
}
|
||
|
if (!SetEndOfFile (tmph))
|
||
|
{
|
||
|
CloseHandle (tmph);
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Increase the size by adding zero bytes at the end. */
|
||
|
static char zero_bytes[1024];
|
||
|
LONG pos_hi = 0;
|
||
|
LONG pos_lo = SetFilePointer (tmph, (LONG) 0, &pos_hi, FILE_END);
|
||
|
LONGLONG pos;
|
||
|
if (pos_lo == INVALID_SET_FILE_POINTER
|
||
|
&& GetLastError() != NO_ERROR)
|
||
|
{
|
||
|
CloseHandle (tmph);
|
||
|
return FALSE;
|
||
|
}
|
||
|
pos = ((LONGLONG) pos_hi << 32) | (ULONGLONG) (ULONG) pos_lo;
|
||
|
while (pos < size)
|
||
|
{
|
||
|
DWORD written;
|
||
|
LONGLONG count = size - pos;
|
||
|
if (count > sizeof (zero_bytes))
|
||
|
count = sizeof (zero_bytes);
|
||
|
if (!WriteFile (tmph, zero_bytes, (DWORD) count, &written, NULL)
|
||
|
|| written == 0)
|
||
|
{
|
||
|
CloseHandle (tmph);
|
||
|
return FALSE;
|
||
|
}
|
||
|
pos += (ULONGLONG) (ULONG) written;
|
||
|
}
|
||
|
}
|
||
|
/* Close the handle. */
|
||
|
CloseHandle (tmph);
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
ftruncate (int fd, off_t length)
|
||
|
{
|
||
|
HANDLE handle = (HANDLE) _get_osfhandle (fd);
|
||
|
|
||
|
if (handle == INVALID_HANDLE_VALUE)
|
||
|
{
|
||
|
errno = EBADF;
|
||
|
return -1;
|
||
|
}
|
||
|
if (length < 0)
|
||
|
{
|
||
|
errno = EINVAL;
|
||
|
return -1;
|
||
|
}
|
||
|
if (!SetFileSize (handle, length))
|
||
|
{
|
||
|
switch (GetLastError ())
|
||
|
{
|
||
|
case ERROR_ACCESS_DENIED:
|
||
|
errno = EACCES;
|
||
|
break;
|
||
|
case ERROR_HANDLE_DISK_FULL:
|
||
|
case ERROR_DISK_FULL:
|
||
|
case ERROR_DISK_TOO_FRAGMENTED:
|
||
|
errno = ENOSPC;
|
||
|
break;
|
||
|
default:
|
||
|
errno = EIO;
|
||
|
break;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
# else
|
||
|
|
||
|
# include <io.h>
|
||
|
|
||
|
# if HAVE_MSVC_INVALID_PARAMETER_HANDLER
|
||
|
# include "msvc-inval.h"
|
||
|
static int
|
||
|
chsize_nothrow (int fd, long length)
|
||
|
{
|
||
|
int result;
|
||
|
|
||
|
TRY_MSVC_INVAL
|
||
|
{
|
||
|
result = chsize (fd, length);
|
||
|
}
|
||
|
CATCH_MSVC_INVAL
|
||
|
{
|
||
|
result = -1;
|
||
|
errno = EBADF;
|
||
|
}
|
||
|
DONE_MSVC_INVAL;
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
# else
|
||
|
# define chsize_nothrow chsize
|
||
|
# endif
|
||
|
|
||
|
int
|
||
|
ftruncate (int fd, off_t length)
|
||
|
{
|
||
|
return chsize_nothrow (fd, length);
|
||
|
}
|
||
|
|
||
|
# endif
|
||
|
#endif
|