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