1 /* ftruncate emulations for native Windows.
2    Copyright (C) 1992-2019 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 #  undef _WIN32_WINNT
34 #  define _WIN32_WINNT _WIN32_WINNT_WIN2K
35 
36 /* Get declarations of the native Windows API functions.  */
37 #  define WIN32_LEAN_AND_MEAN
38 #  include <windows.h>
39 
40 /* Get _get_osfhandle.  */
41 #  if GNULIB_MSVC_NOTHROW
42 #   include "msvc-nothrow.h"
43 #  else
44 #   include <io.h>
45 #  endif
46 
47 static BOOL
SetFileSize(HANDLE h,LONGLONG size)48 SetFileSize (HANDLE h, LONGLONG size)
49 {
50   LARGE_INTEGER old_size;
51 
52   if (!GetFileSizeEx (h, &old_size))
53     return FALSE;
54 
55   if (size != old_size.QuadPart)
56     {
57       /* Duplicate the handle, so we are free to modify its file position.  */
58       HANDLE curr_process = GetCurrentProcess ();
59       HANDLE tmph;
60 
61       if (!DuplicateHandle (curr_process,           /* SourceProcessHandle */
62                             h,                      /* SourceHandle */
63                             curr_process,           /* TargetProcessHandle */
64                             (PHANDLE) &tmph,        /* TargetHandle */
65                             (DWORD) 0,              /* DesiredAccess */
66                             FALSE,                  /* InheritHandle */
67                             DUPLICATE_SAME_ACCESS)) /* Options */
68         return FALSE;
69 
70       if (size < old_size.QuadPart)
71         {
72           /* Reduce the size.  */
73           LONG size_hi = (LONG) (size >> 32);
74           if (SetFilePointer (tmph, (LONG) size, &size_hi, FILE_BEGIN)
75               == INVALID_SET_FILE_POINTER
76               && GetLastError() != NO_ERROR)
77             {
78               CloseHandle (tmph);
79               return FALSE;
80             }
81           if (!SetEndOfFile (tmph))
82             {
83               CloseHandle (tmph);
84               return FALSE;
85             }
86         }
87       else
88         {
89           /* Increase the size by adding zero bytes at the end.  */
90           static char zero_bytes[1024];
91           LONG pos_hi = 0;
92           LONG pos_lo = SetFilePointer (tmph, (LONG) 0, &pos_hi, FILE_END);
93           LONGLONG pos;
94           if (pos_lo == INVALID_SET_FILE_POINTER
95               && GetLastError() != NO_ERROR)
96             {
97               CloseHandle (tmph);
98               return FALSE;
99             }
100           pos = ((LONGLONG) pos_hi << 32) | (ULONGLONG) (ULONG) pos_lo;
101           while (pos < size)
102             {
103               DWORD written;
104               LONGLONG count = size - pos;
105               if (count > sizeof (zero_bytes))
106                 count = sizeof (zero_bytes);
107               if (!WriteFile (tmph, zero_bytes, (DWORD) count, &written, NULL)
108                   || written == 0)
109                 {
110                   CloseHandle (tmph);
111                   return FALSE;
112                 }
113               pos += (ULONGLONG) (ULONG) written;
114             }
115         }
116       /* Close the handle.  */
117       CloseHandle (tmph);
118     }
119   return TRUE;
120 }
121 
122 int
ftruncate(int fd,off_t length)123 ftruncate (int fd, off_t length)
124 {
125   HANDLE handle = (HANDLE) _get_osfhandle (fd);
126 
127   if (handle == INVALID_HANDLE_VALUE)
128     {
129       errno = EBADF;
130       return -1;
131     }
132   if (length < 0)
133     {
134       errno = EINVAL;
135       return -1;
136     }
137   if (!SetFileSize (handle, length))
138     {
139       switch (GetLastError ())
140         {
141         case ERROR_ACCESS_DENIED:
142           errno = EACCES;
143           break;
144         case ERROR_HANDLE_DISK_FULL:
145         case ERROR_DISK_FULL:
146         case ERROR_DISK_TOO_FRAGMENTED:
147           errno = ENOSPC;
148           break;
149         default:
150           errno = EIO;
151           break;
152         }
153       return -1;
154     }
155   return 0;
156 }
157 
158 # else
159 
160 #  include <io.h>
161 
162 #  if HAVE_MSVC_INVALID_PARAMETER_HANDLER
163 #   include "msvc-inval.h"
164 static int
chsize_nothrow(int fd,long length)165 chsize_nothrow (int fd, long length)
166 {
167   int result;
168 
169   TRY_MSVC_INVAL
170     {
171       result = chsize (fd, length);
172     }
173   CATCH_MSVC_INVAL
174     {
175       result = -1;
176       errno = EBADF;
177     }
178   DONE_MSVC_INVAL;
179 
180   return result;
181 }
182 #  else
183 #   define chsize_nothrow chsize
184 #  endif
185 
186 int
ftruncate(int fd,off_t length)187 ftruncate (int fd, off_t length)
188 {
189   return chsize_nothrow (fd, length);
190 }
191 
192 # endif
193 #endif
194