1 /* ftruncate emulations for native Windows.
2    Copyright (C) 1992-2021 Free Software Foundation, Inc.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3, or (at your option)
7    any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License along
15    with this program; if not, see <https://www.gnu.org/licenses/>.  */
16 
17 #include <config.h>
18 
19 /* Specification.  */
20 #include <unistd.h>
21 
22 #if HAVE__CHSIZE
23 /* A native Windows platform.  */
24 
25 # include <errno.h>
26 
27 # if _GL_WINDOWS_64_BIT_OFF_T
28 
29 /* Large File Support: off_t is 64-bit, but _chsize() takes only a 32-bit
30    argument.  So, define a 64-bit safe SetFileSize function ourselves.  */
31 
32 /* Ensure that <windows.h> declares GetFileSizeEx.  */
33 #  if !defined _WIN32_WINNT || (_WIN32_WINNT < _WIN32_WINNT_WIN2K)
34 #   undef _WIN32_WINNT
35 #   define _WIN32_WINNT _WIN32_WINNT_WIN2K
36 #  endif
37 
38 /* Get declarations of the native Windows API functions.  */
39 #  define WIN32_LEAN_AND_MEAN
40 #  include <windows.h>
41 
42 /* Get _get_osfhandle.  */
43 #  if GNULIB_MSVC_NOTHROW
44 #   include "msvc-nothrow.h"
45 #  else
46 #   include <io.h>
47 #  endif
48 
49 static BOOL
SetFileSize(HANDLE h,LONGLONG size)50 SetFileSize (HANDLE h, LONGLONG size)
51 {
52   LARGE_INTEGER old_size;
53 
54   if (!GetFileSizeEx (h, &old_size))
55     return FALSE;
56 
57   if (size != old_size.QuadPart)
58     {
59       /* Duplicate the handle, so we are free to modify its file position.  */
60       HANDLE curr_process = GetCurrentProcess ();
61       HANDLE tmph;
62 
63       if (!DuplicateHandle (curr_process,           /* SourceProcessHandle */
64                             h,                      /* SourceHandle */
65                             curr_process,           /* TargetProcessHandle */
66                             (PHANDLE) &tmph,        /* TargetHandle */
67                             (DWORD) 0,              /* DesiredAccess */
68                             FALSE,                  /* InheritHandle */
69                             DUPLICATE_SAME_ACCESS)) /* Options */
70         return FALSE;
71 
72       if (size < old_size.QuadPart)
73         {
74           /* Reduce the size.  */
75           LONG size_hi = (LONG) (size >> 32);
76           if (SetFilePointer (tmph, (LONG) size, &size_hi, FILE_BEGIN)
77               == INVALID_SET_FILE_POINTER
78               && GetLastError() != NO_ERROR)
79             {
80               CloseHandle (tmph);
81               return FALSE;
82             }
83           if (!SetEndOfFile (tmph))
84             {
85               CloseHandle (tmph);
86               return FALSE;
87             }
88         }
89       else
90         {
91           /* Increase the size by adding zero bytes at the end.  */
92           static char zero_bytes[1024];
93           LONG pos_hi = 0;
94           LONG pos_lo = SetFilePointer (tmph, (LONG) 0, &pos_hi, FILE_END);
95           LONGLONG pos;
96           if (pos_lo == INVALID_SET_FILE_POINTER
97               && GetLastError() != NO_ERROR)
98             {
99               CloseHandle (tmph);
100               return FALSE;
101             }
102           pos = ((LONGLONG) pos_hi << 32) | (ULONGLONG) (ULONG) pos_lo;
103           while (pos < size)
104             {
105               DWORD written;
106               LONGLONG count = size - pos;
107               if (count > sizeof (zero_bytes))
108                 count = sizeof (zero_bytes);
109               if (!WriteFile (tmph, zero_bytes, (DWORD) count, &written, NULL)
110                   || written == 0)
111                 {
112                   CloseHandle (tmph);
113                   return FALSE;
114                 }
115               pos += (ULONGLONG) (ULONG) written;
116             }
117         }
118       /* Close the handle.  */
119       CloseHandle (tmph);
120     }
121   return TRUE;
122 }
123 
124 int
ftruncate(int fd,off_t length)125 ftruncate (int fd, off_t length)
126 {
127   HANDLE handle = (HANDLE) _get_osfhandle (fd);
128 
129   if (handle == INVALID_HANDLE_VALUE)
130     {
131       errno = EBADF;
132       return -1;
133     }
134   if (length < 0)
135     {
136       errno = EINVAL;
137       return -1;
138     }
139   if (!SetFileSize (handle, length))
140     {
141       switch (GetLastError ())
142         {
143         case ERROR_ACCESS_DENIED:
144           errno = EACCES;
145           break;
146         case ERROR_HANDLE_DISK_FULL:
147         case ERROR_DISK_FULL:
148         case ERROR_DISK_TOO_FRAGMENTED:
149           errno = ENOSPC;
150           break;
151         default:
152           errno = EIO;
153           break;
154         }
155       return -1;
156     }
157   return 0;
158 }
159 
160 # else
161 
162 #  include <io.h>
163 
164 #  if HAVE_MSVC_INVALID_PARAMETER_HANDLER
165 #   include "msvc-inval.h"
166 static int
chsize_nothrow(int fd,long length)167 chsize_nothrow (int fd, long length)
168 {
169   int result;
170 
171   TRY_MSVC_INVAL
172     {
173       result = _chsize (fd, length);
174     }
175   CATCH_MSVC_INVAL
176     {
177       result = -1;
178       errno = EBADF;
179     }
180   DONE_MSVC_INVAL;
181 
182   return result;
183 }
184 #  else
185 #   define chsize_nothrow _chsize
186 #  endif
187 
188 int
ftruncate(int fd,off_t length)189 ftruncate (int fd, off_t length)
190 {
191   return chsize_nothrow (fd, length);
192 }
193 
194 # endif
195 #endif
196