1#ifdef TEST_FTRUNCATE64
2#include <fcntl.h>
3#include <sys/stat.h>
4#endif /* TEST_FTRUNCATE64 */
5
6#include <stdio.h>
7#include <unistd.h>
8#include <io.h>
9#include <stdlib.h>
10#include <errno.h>
11#include <wchar.h>
12#include <windows.h>
13#include <psapi.h>
14
15#if 0
16/* Mutually exclusive methods
17 We check disk space as truncating more than the allowed space results
18 in file getting mysteriously deleted
19 */
20#define _CHECK_SPACE_BY_VOLUME_METHOD_ 1 /* Needs to walk through all volumes */
21#define _CHECK_SPACE_BY_PSAPI_METHOD_ 0 /* Requires psapi.dll */
22#define _CHECK_SPACE_BY_VISTA_METHOD_ 0 /* Won't work on XP */
23
24#if (_CHECK_SPACE_BY_PSAPI_METHOD_ == 1) /* Retrive actual volume path */
25static LPWSTR getdirpath(const LPWSTR __str){
26 int len, walk = 0;
27 LPWSTR dirname;
28 while (__str[walk] != L'\0'){
29 walk++;
30 if (__str[walk] == L'\\') len = walk + 1;
31 }
32 dirname = calloc(len + 1, sizeof(wchar_t));
33 if (!dirname) return dirname; /* memory error */
34 return wcsncpy(dirname,__str,len);
35}
36
37static LPWSTR xp_normalize_fn(const LPWSTR fn) {
38 DWORD len, err, walker, isfound;
39 LPWSTR drives = NULL;
40 LPWSTR target = NULL;
41 LPWSTR ret = NULL;
42 wchar_t tmplt[3] = L" :"; /* Template */
43
44 /*Get list of drive letters */
45 len = GetLogicalDriveStringsW(0,NULL);
46 drives = calloc(len,sizeof(wchar_t));
47 if (!drives) return NULL;
48 len = GetLogicalDriveStringsW(len,drives);
49
50 /*Allocatate memory */
51 target = calloc(MAX_PATH + 1,sizeof(wchar_t));
52 if (!target) {
53 free(drives);
54 return NULL;
55 }
56
57 walker = 0;
58 while ((walker < len) && !(drives[walker] == L'\0' && drives[walker + 1] == L'\0')){
59 /* search through alphabets */
60 if(iswalpha(drives[walker])) {
61 *tmplt = drives[walker]; /* Put drive letter */
62 err = QueryDosDeviceW(tmplt,target,MAX_PATH);
63 if(!err) {
64 free(drives);
65 free(target);
66 return NULL;
67 }
68 if( _wcsnicmp(target,fn,wcslen(target)) == 0) break;
69 wmemset(target,L'\0',MAX_PATH);
70 walker++;
71 } else walker++;
72 }
73
74 if (!iswalpha(*tmplt)) {
75 free(drives);
76 free(target);
77 return NULL; /* Finish walking without finding correct drive */
78 }
79
80 ret = calloc(MAX_PATH + 1,sizeof(wchar_t));
81 if (!ret) {
82 free(drives);
83 free(target);
84 return NULL;
85 }
86 _snwprintf(ret,MAX_PATH,L"%ls%ls",tmplt,fn+wcslen(target));
87
88 return ret;
89}
90
91/* XP method of retrieving filename from handles, based on:
92 http://msdn.microsoft.com/en-us/library/aa366789%28VS.85%29.aspx
93 */
94static LPWSTR xp_getfilepath(const HANDLE f, const LARGE_INTEGER fsize){
95 HANDLE hFileMap = NULL;
96 void* pMem = NULL;
97 LPWSTR temp, ret;
98 DWORD err;
99
100 temp = calloc(MAX_PATH + 1, sizeof(wchar_t));
101 if (!temp) goto errormap;
102
103 /* CreateFileMappingW limitation: Cannot map 0 byte files, so extend it to 1 byte */
104 if (!fsize.QuadPart) {
105 SetFilePointer(f, 1, NULL, FILE_BEGIN);
106 err = SetEndOfFile(f);
107 if(!temp) goto errormap;
108 }
109
110 hFileMap = CreateFileMappingW(f,NULL,PAGE_READONLY,0,1,NULL);
111 if(!hFileMap) goto errormap;
112 pMem = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 1);
113 if(!pMem) goto errormap;
114 err = GetMappedFileNameW(GetCurrentProcess(),pMem,temp,MAX_PATH);
115 if(!err) goto errormap;
116
117 if (pMem) UnmapViewOfFile(pMem);
118 if (hFileMap) CloseHandle(hFileMap);
119 ret = xp_normalize_fn(temp);
120 free(temp);
121 return ret;
122
123 errormap:
124 if (temp) free(temp);
125 if (pMem) UnmapViewOfFile(pMem);
126 if (hFileMap) CloseHandle(hFileMap);
127 errno = EBADF;
128 return NULL;
129}
130#endif /* _CHECK_SPACE_BY_PSAPI_METHOD_ */
131
132static int
133checkfreespace (const HANDLE f, const ULONGLONG requiredspace)
134{
135 LPWSTR dirpath, volumeid, volumepath;
136 ULARGE_INTEGER freespace;
137 LARGE_INTEGER currentsize;
138 DWORD check, volumeserial;
139 BY_HANDLE_FILE_INFORMATION fileinfo;
140 HANDLE vol;
141
142 /* Get current size */
143 check = GetFileSizeEx (f, &currentsize);
144 if (!check)
145 {
146 errno = EBADF;
147 return -1; /* Error checking file size */
148 }
149
150 /* Short circuit disk space check if shrink operation */
151 if ((ULONGLONG)currentsize.QuadPart >= requiredspace)
152 return 0;
153
154 /* We check available space to user before attempting to truncate */
155
156#if (_CHECK_SPACE_BY_VISTA_METHOD_ == 1)
157 /* Get path length */
158 DWORD err;
159 LPWSTR filepath = NULL;
160 check = GetFinalPathNameByHandleW(f,filepath,0,FILE_NAME_NORMALIZED|VOLUME_NAME_GUID);
161 err = GetLastError();
162 if (err == ERROR_PATH_NOT_FOUND || err == ERROR_INVALID_PARAMETER) {
163 errno = EINVAL;
164 return -1; /* IO error */
165 }
166 filepath = calloc(check + 1,sizeof(wchar_t));
167 if (!filepath) {
168 errno = EBADF;
169 return -1; /* Out of memory */
170 }
171 check = GetFinalPathNameByHandleW(f,filepath,check,FILE_NAME_NORMALIZED|VOLUME_NAME_GUID);
172 /* FIXME: last error was set to error 87 (0x57)
173 "The parameter is incorrect." for some reason but works out */
174 if (!check) {
175 errno = EBADF;
176 return -1; /* Error resolving filename */
177 }
178#endif /* _CHECK_SPACE_BY_VISTA_METHOD_ */
179
180#if (_CHECK_SPACE_BY_PSAPI_METHOD_ == 1)
181 LPWSTR filepath = NULL;
182 filepath = xp_getfilepath(f,currentsize);
183
184 /* Get durectory path */
185 dirpath = getdirpath(filepath);
186 free(filepath);
187 filepath = NULL;
188 if (!dirpath) {
189 errno = EBADF;
190 return -1; /* Out of memory */
191 }
192#endif /* _CHECK_SPACE_BY_PSAPI_METHOD_ */
193
194#if _CHECK_SPACE_BY_VOLUME_METHOD_
195 if(!GetFileInformationByHandle(f,&fileinfo)) {
196 errno = EINVAL;
197 return -1; /* Resolution failure */
198 }
199
200 volumeid = calloc(51,sizeof(wchar_t));
201 volumepath = calloc(MAX_PATH+2,sizeof(wchar_t));
202 if(!volumeid || !volumepath) {
203 errno = EBADF;
204 return -1; /* Out of memory */
205 }
206
207 dirpath = NULL;
208
209 vol = FindFirstVolumeW(volumeid,50);
210 /* wprintf(L"%d - %ls\n",wcslen(volumeid),volumeid); */
211 do {
212 check = GetVolumeInformationW(volumeid,volumepath,MAX_PATH+1,&volumeserial,NULL,NULL,NULL,0);
213 /* wprintf(L"GetVolumeInformationW %d id %ls path %ls error %d\n",check,volumeid,volumepath,GetLastError()); */
214 if(volumeserial == fileinfo.dwVolumeSerialNumber) {
215 dirpath = volumeid;
216 break;
217 }
218 } while (FindNextVolumeW(vol,volumeid,50));
219 FindVolumeClose(vol);
220
221 if(!dirpath) free(volumeid); /* we found the volume */
222 free(volumepath);
223#endif /* _CHECK_SPACE_BY_VOLUME_METHOD_ */
224
225 /* Get available free space */
226 check = GetDiskFreeSpaceExW(dirpath,&freespace,NULL,NULL);
227 //wprintf(L"freespace %I64u\n",freespace);
228 free(dirpath);
229 if(!check) {
230 errno = EFBIG;
231 return -1; /* Error getting free space */
232 }
233
234 /* Check space requirements */
235 if ((requiredspace - currentsize.QuadPart) > freespace.QuadPart)
236 {
237 errno = EFBIG; /* File too big for disk */
238 return -1;
239 } /* We have enough space to truncate/expand */
240 return 0;
241}
242#endif
243
244int __cdecl __mingw_ftruncate64(int __fd, _off64_t __length);
245int __cdecl __mingw_ftruncate64(int __fd, _off64_t __length) {
246 HANDLE f;
247 LARGE_INTEGER quad;
248 DWORD check;
249 int ret = 0;
250 __int64 pos;
251
252 /* Sanity check */
253 if (__length < 0) {
254 goto errorout;
255 }
256
257 /* Get Win32 Handle */
258 if(__fd == -1) {
259 goto errorout;
260 }
261
262 f = (HANDLE)_get_osfhandle(__fd);
263 if (f == INVALID_HANDLE_VALUE || (GetFileType(f) != FILE_TYPE_DISK)) {
264 errno = EBADF;
265 return -1;
266 }
267
268
269 /* Save position */
270 if((pos = _telli64(__fd)) == -1LL){
271 goto errorout;
272 }
273
274#if 0
275 /* Check available space */
276 check = checkfreespace(f,__length);
277 if (check != 0) {
278 return -1; /* Error, errno already set */
279 }
280#endif
281
282 quad.QuadPart = __length;
283 check = SetFilePointer(f, (LONG)quad.LowPart, &(quad.HighPart), FILE_BEGIN);
284 if (check == INVALID_SET_FILE_POINTER && quad.LowPart != INVALID_SET_FILE_POINTER) {
285 switch (GetLastError()) {
286 case ERROR_NEGATIVE_SEEK:
287 errno = EFBIG; /* file too big? */
288 return -1;
289 case INVALID_SET_FILE_POINTER:
290 errno = EINVAL; /* shouldn't happen */
291 return -1;
292 default:
293 errno = EINVAL; /* shouldn't happen */
294 return -1;
295 }
296 }
297
298 check = SetEndOfFile(f);
299 if (!check) {
300 goto errorout;
301 }
302
303 if(_lseeki64(__fd,pos,SEEK_SET) == -1LL){
304 goto errorout;
305 }
306
307 return ret;
308
309 errorout:
310 errno = EINVAL;
311 return -1;
312}
313
314#ifdef TEST_FTRUNCATE64
315#define ftruncate64 __mingw_ftruncate64
316#endif
317
318#if (TEST_FTRUNCATE64 == 1)
319int main(){
320 LARGE_INTEGER sz;
321 ULARGE_INTEGER freespace;
322 int f;
323 LPWSTR path, dir;
324 sz.QuadPart = 0LL;
325 f = _open("XXX.tmp", _O_BINARY|_O_CREAT|_O_RDWR, _S_IREAD | _S_IWRITE);
326 wprintf(L"%d\n",ftruncate64(f,12));
327 wprintf(L"%d\n",ftruncate64(f,20));
328 wprintf(L"%d\n",ftruncate64(f,15));
329/* path = xp_getfilepath((HANDLE)_get_osfhandle(f),sz);
330 dir = getdirpath(path);
331 GetDiskFreeSpaceExW(dir,&freespace,NULL,NULL);
332 wprintf(L"fs - %ls\n",path);
333 wprintf(L"dirfs - %ls\n",dir);
334 wprintf(L"free - %I64u\n",freespace.QuadPart);
335 free(dir);
336 free(path);*/
337 _close(f);
338 return 0;
339}
340#endif /* TEST_FTRUNCATE64 */
341
342#if (TEST_FTRUNCATE64 == 2)
343int main() {
344FILE *f;
345int fd;
346char buf[100];
347int cnt;
348unlink("test.out");
349f = fopen("test.out","w+");
350fd = fileno(f);
351write(fd,"abc",3);
352fflush(f);
353printf ("err: %d\n", ftruncate64(fd,10));
354cnt = read(fd,buf,100);
355printf("cnt = %d\n",cnt);
356return 0;
357}
358#endif /* TEST_FTRUNCATE64 */
359
360#if (TEST_FTRUNCATE64 == 3)
361int main() {
362FILE *f;
363int fd;
364char buf[100];
365int cnt;
366unlink("test.out");
367f = fopen("test.out","w+");
368fd = fileno(f);
369write(fd,"abc",3);
370fflush(f);
371ftruncate64(fd,0);
372write(fd,"def",3);
373fclose(f);
374f = fopen("test.out","r");
375cnt = fread(buf,1,100,f);
376printf("cnt = %d\n",cnt);
377return 0;
378}
379#endif /* TEST_FTRUNCATE64 */
380