rm_/lib/ftruncate.c
2020-09-02 16:47:03 +08:00

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