master
  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 */
 24static 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
 36static 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 */
 93static 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  errno = EBADF;
127  return NULL;
128}
129#endif /* _CHECK_SPACE_BY_PSAPI_METHOD_ */
130
131static int
132checkfreespace (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    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     errno = EINVAL;
163     return -1; /* IO error */
164  }
165  filepath = calloc(check + 1,sizeof(wchar_t));
166  if (!filepath) {
167    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    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    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    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    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    errno = EFBIG;
230    return -1; /* Error getting free space */
231  }
232 
233  /* Check space requirements */
234  if ((requiredspace - currentsize.QuadPart) > freespace.QuadPart)
235  {
236    errno = EFBIG; /* File too big for disk */
237    return -1;
238  } /* We have enough space to truncate/expand */
239  return 0;
240}
241
242int 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    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        errno = EFBIG; /* file too big? */
283        return -1;
284      case INVALID_SET_FILE_POINTER:
285        errno = EINVAL; /* shouldn't happen */
286        return -1;
287      default:
288        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  errno = EINVAL;
306  return -1;
307}
308
309#if (TEST_FTRUNCATE64 == 1)
310int 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)
334int main() {
335FILE *f;
336int fd;
337char buf[100];
338int cnt;
339unlink("test.out");
340f = fopen("test.out","w+");
341fd = fileno(f);
342write(fd,"abc",3);
343fflush(f);
344printf ("err: %d\n", ftruncate64(fd,10));
345cnt = read(fd,buf,100);
346printf("cnt = %d\n",cnt);
347return 0;
348}
349#endif /* TEST_FTRUNCATE64 */
350
351#if (TEST_FTRUNCATE64 == 3)
352int main() {
353FILE *f;
354int fd;
355char buf[100];
356int cnt;
357unlink("test.out");
358f = fopen("test.out","w+");
359fd = fileno(f);
360write(fd,"abc",3);
361fflush(f);
362ftruncate64(fd,0);
363write(fd,"def",3);
364fclose(f);
365f = fopen("test.out","r");
366cnt = fread(buf,1,100,f);
367printf("cnt = %d\n",cnt);
368return 0;
369}
370#endif /* TEST_FTRUNCATE64 */
371