Line | Count | Source (jump to first uncovered line) |
1 | | #include "rar.hpp" |
2 | | |
3 | | MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr) |
4 | 0 | { |
5 | | #ifdef _WIN_ALL |
6 | | // Windows automatically removes dots and spaces in the end of directory |
7 | | // name. So we detect such names and process them with \\?\ prefix. |
8 | | wchar *LastChar=PointToLastChar(Name); |
9 | | bool Special=*LastChar=='.' || *LastChar==' '; |
10 | | BOOL RetCode=Special ? FALSE : CreateDirectory(Name,NULL); |
11 | | if (RetCode==0 && !FileExist(Name)) |
12 | | { |
13 | | wchar LongName[NM]; |
14 | | if (GetWinLongPath(Name,LongName,ASIZE(LongName))) |
15 | | RetCode=CreateDirectory(LongName,NULL); |
16 | | } |
17 | | if (RetCode!=0) // Non-zero return code means success for CreateDirectory. |
18 | | { |
19 | | if (SetAttr) |
20 | | SetFileAttr(Name,Attr); |
21 | | return MKDIR_SUCCESS; |
22 | | } |
23 | | int ErrCode=GetLastError(); |
24 | | if (ErrCode==ERROR_FILE_NOT_FOUND || ErrCode==ERROR_PATH_NOT_FOUND) |
25 | | return MKDIR_BADPATH; |
26 | | return MKDIR_ERROR; |
27 | | #elif defined(_UNIX) |
28 | 0 | char NameA[NM]; |
29 | 0 | WideToChar(Name,NameA,ASIZE(NameA)); |
30 | 0 | mode_t uattr=SetAttr ? (mode_t)Attr:0777; |
31 | 0 | int ErrCode=mkdir(NameA,uattr); |
32 | 0 | if (ErrCode==-1) |
33 | 0 | return errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR; |
34 | 0 | return MKDIR_SUCCESS; |
35 | | #else |
36 | | return MKDIR_ERROR; |
37 | | #endif |
38 | 0 | } |
39 | | |
40 | | |
41 | | bool CreatePath(const wchar *Path,bool SkipLastName,bool Silent) |
42 | 0 | { |
43 | 0 | if (Path==NULL || *Path==0) |
44 | 0 | return false; |
45 | | |
46 | | #if defined(_WIN_ALL) || defined(_EMX) |
47 | | uint DirAttr=0; |
48 | | #else |
49 | 0 | uint DirAttr=0777; |
50 | 0 | #endif |
51 | | |
52 | 0 | bool Success=true; |
53 | |
|
54 | 0 | for (const wchar *s=Path;*s!=0;s++) |
55 | 0 | { |
56 | 0 | wchar DirName[NM]; |
57 | 0 | if (s-Path>=ASIZE(DirName)) |
58 | 0 | break; |
59 | | |
60 | | // Process all kinds of path separators, so user can enter Unix style |
61 | | // path in Windows or Windows in Unix. s>Path check avoids attempting |
62 | | // creating an empty directory for paths starting from path separator. |
63 | 0 | if (IsPathDiv(*s) && s>Path) |
64 | 0 | { |
65 | | #ifdef _WIN_ALL |
66 | | // We must not attempt to create "D:" directory, because first |
67 | | // CreateDirectory will fail, so we'll use \\?\D:, which forces Wine |
68 | | // to create "D:" directory. |
69 | | if (s==Path+2 && Path[1]==':') |
70 | | continue; |
71 | | #endif |
72 | 0 | wcsncpy(DirName,Path,s-Path); |
73 | 0 | DirName[s-Path]=0; |
74 | |
|
75 | 0 | Success=MakeDir(DirName,true,DirAttr)==MKDIR_SUCCESS; |
76 | 0 | if (Success && !Silent) |
77 | 0 | { |
78 | 0 | mprintf(St(MCreatDir),DirName); |
79 | 0 | mprintf(L" %s",St(MOk)); |
80 | 0 | } |
81 | 0 | } |
82 | 0 | } |
83 | 0 | if (!SkipLastName && !IsPathDiv(*PointToLastChar(Path))) |
84 | 0 | Success=MakeDir(Path,true,DirAttr)==MKDIR_SUCCESS; |
85 | 0 | return Success; |
86 | 0 | } |
87 | | |
88 | | |
89 | | void SetDirTime(const wchar *Name,RarTime *ftm,RarTime *ftc,RarTime *fta) |
90 | 0 | { |
91 | | #if defined(_WIN_ALL) |
92 | | bool sm=ftm!=NULL && ftm->IsSet(); |
93 | | bool sc=ftc!=NULL && ftc->IsSet(); |
94 | | bool sa=fta!=NULL && fta->IsSet(); |
95 | | |
96 | | uint DirAttr=GetFileAttr(Name); |
97 | | bool ResetAttr=(DirAttr!=0xffffffff && (DirAttr & FILE_ATTRIBUTE_READONLY)!=0); |
98 | | if (ResetAttr) |
99 | | SetFileAttr(Name,0); |
100 | | |
101 | | HANDLE hFile=CreateFile(Name,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE, |
102 | | NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL); |
103 | | if (hFile==INVALID_HANDLE_VALUE) |
104 | | { |
105 | | wchar LongName[NM]; |
106 | | if (GetWinLongPath(Name,LongName,ASIZE(LongName))) |
107 | | hFile=CreateFile(LongName,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE, |
108 | | NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL); |
109 | | } |
110 | | |
111 | | if (hFile==INVALID_HANDLE_VALUE) |
112 | | return; |
113 | | FILETIME fm,fc,fa; |
114 | | if (sm) |
115 | | ftm->GetWinFT(&fm); |
116 | | if (sc) |
117 | | ftc->GetWinFT(&fc); |
118 | | if (sa) |
119 | | fta->GetWinFT(&fa); |
120 | | SetFileTime(hFile,sc ? &fc:NULL,sa ? &fa:NULL,sm ? &fm:NULL); |
121 | | CloseHandle(hFile); |
122 | | if (ResetAttr) |
123 | | SetFileAttr(Name,DirAttr); |
124 | | #endif |
125 | 0 | #if defined(_UNIX) || defined(_EMX) |
126 | 0 | File::SetCloseFileTimeByName(Name,ftm,fta); |
127 | 0 | #endif |
128 | 0 | } |
129 | | |
130 | | |
131 | | bool IsRemovable(const wchar *Name) |
132 | 0 | { |
133 | | #if defined(_WIN_ALL) |
134 | | wchar Root[NM]; |
135 | | GetPathRoot(Name,Root,ASIZE(Root)); |
136 | | int Type=GetDriveType(*Root!=0 ? Root:NULL); |
137 | | return Type==DRIVE_REMOVABLE || Type==DRIVE_CDROM; |
138 | | #else |
139 | 0 | return false; |
140 | 0 | #endif |
141 | 0 | } |
142 | | |
143 | | |
144 | | #ifndef SFX_MODULE |
145 | | int64 GetFreeDisk(const wchar *Name) |
146 | 0 | { |
147 | | #ifdef _WIN_ALL |
148 | | wchar Root[NM]; |
149 | | GetFilePath(Name,Root,ASIZE(Root)); |
150 | | |
151 | | ULARGE_INTEGER uiTotalSize,uiTotalFree,uiUserFree; |
152 | | uiUserFree.u.LowPart=uiUserFree.u.HighPart=0; |
153 | | if (GetDiskFreeSpaceEx(*Root!=0 ? Root:NULL,&uiUserFree,&uiTotalSize,&uiTotalFree) && |
154 | | uiUserFree.u.HighPart<=uiTotalFree.u.HighPart) |
155 | | return INT32TO64(uiUserFree.u.HighPart,uiUserFree.u.LowPart); |
156 | | return 0; |
157 | | #elif defined(_UNIX) |
158 | 0 | wchar Root[NM]; |
159 | 0 | GetFilePath(Name,Root,ASIZE(Root)); |
160 | 0 | char RootA[NM]; |
161 | 0 | WideToChar(Root,RootA,ASIZE(RootA)); |
162 | 0 | struct statvfs sfs; |
163 | 0 | if (statvfs(*RootA!=0 ? RootA:".",&sfs)!=0) |
164 | 0 | return 0; |
165 | 0 | int64 FreeSize=sfs.f_bsize; |
166 | 0 | FreeSize=FreeSize*sfs.f_bavail; |
167 | 0 | return FreeSize; |
168 | | #else |
169 | | return 0; |
170 | | #endif |
171 | 0 | } |
172 | | #endif |
173 | | |
174 | | |
175 | | #if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) |
176 | | // Return 'true' for FAT and FAT32, so we can adjust the maximum supported |
177 | | // file size to 4 GB for these file systems. |
178 | | bool IsFAT(const wchar *Name) |
179 | | { |
180 | | wchar Root[NM]; |
181 | | GetPathRoot(Name,Root,ASIZE(Root)); |
182 | | wchar FileSystem[MAX_PATH+1]; |
183 | | if (GetVolumeInformation(Root,NULL,0,NULL,NULL,NULL,FileSystem,ASIZE(FileSystem))) |
184 | | return wcscmp(FileSystem,L"FAT")==0 || wcscmp(FileSystem,L"FAT32")==0; |
185 | | return false; |
186 | | } |
187 | | #endif |
188 | | |
189 | | |
190 | | bool FileExist(const wchar *Name) |
191 | 1 | { |
192 | | #ifdef _WIN_ALL |
193 | | return GetFileAttr(Name)!=0xffffffff; |
194 | | #elif defined(ENABLE_ACCESS) |
195 | 1 | char NameA[NM]; |
196 | 1 | WideToChar(Name,NameA,ASIZE(NameA)); |
197 | 1 | return access(NameA,0)==0; |
198 | | #else |
199 | | FindData FD; |
200 | | return FindFile::FastFind(Name,&FD); |
201 | | #endif |
202 | 1 | } |
203 | | |
204 | | |
205 | | bool WildFileExist(const wchar *Name) |
206 | 0 | { |
207 | 0 | if (IsWildcard(Name)) |
208 | 0 | { |
209 | 0 | FindFile Find; |
210 | 0 | Find.SetMask(Name); |
211 | 0 | FindData fd; |
212 | 0 | return Find.Next(&fd); |
213 | 0 | } |
214 | 0 | return FileExist(Name); |
215 | 0 | } |
216 | | |
217 | | |
218 | | bool IsDir(uint Attr) |
219 | 2 | { |
220 | | #ifdef _WIN_ALL |
221 | | return Attr!=0xffffffff && (Attr & FILE_ATTRIBUTE_DIRECTORY)!=0; |
222 | | #endif |
223 | 2 | #if defined(_UNIX) |
224 | 2 | return (Attr & 0xF000)==0x4000; |
225 | 2 | #endif |
226 | 2 | } |
227 | | |
228 | | |
229 | | bool IsUnreadable(uint Attr) |
230 | 0 | { |
231 | 0 | #if defined(_UNIX) && defined(S_ISFIFO) && defined(S_ISSOCK) && defined(S_ISCHR) |
232 | 0 | return S_ISFIFO(Attr) || S_ISSOCK(Attr) || S_ISCHR(Attr); |
233 | 0 | #endif |
234 | 0 | return false; |
235 | 0 | } |
236 | | |
237 | | |
238 | | bool IsLink(uint Attr) |
239 | 2 | { |
240 | 2 | #ifdef _UNIX |
241 | 2 | return (Attr & 0xF000)==0xA000; |
242 | | #elif defined(_WIN_ALL) |
243 | | return (Attr & FILE_ATTRIBUTE_REPARSE_POINT)!=0; |
244 | | #else |
245 | | return false; |
246 | | #endif |
247 | 2 | } |
248 | | |
249 | | |
250 | | |
251 | | |
252 | | |
253 | | |
254 | | bool IsDeleteAllowed(uint FileAttr) |
255 | 0 | { |
256 | | #ifdef _WIN_ALL |
257 | | return (FileAttr & (FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN))==0; |
258 | | #else |
259 | 0 | return (FileAttr & (S_IRUSR|S_IWUSR))==(S_IRUSR|S_IWUSR); |
260 | 0 | #endif |
261 | 0 | } |
262 | | |
263 | | |
264 | | void PrepareToDelete(const wchar *Name) |
265 | 0 | { |
266 | | #if defined(_WIN_ALL) || defined(_EMX) |
267 | | SetFileAttr(Name,0); |
268 | | #endif |
269 | 0 | #ifdef _UNIX |
270 | 0 | if (Name!=NULL) |
271 | 0 | { |
272 | 0 | char NameA[NM]; |
273 | 0 | WideToChar(Name,NameA,ASIZE(NameA)); |
274 | 0 | chmod(NameA,S_IRUSR|S_IWUSR|S_IXUSR); |
275 | 0 | } |
276 | 0 | #endif |
277 | 0 | } |
278 | | |
279 | | |
280 | | uint GetFileAttr(const wchar *Name) |
281 | 0 | { |
282 | | #ifdef _WIN_ALL |
283 | | DWORD Attr=GetFileAttributes(Name); |
284 | | if (Attr==0xffffffff) |
285 | | { |
286 | | wchar LongName[NM]; |
287 | | if (GetWinLongPath(Name,LongName,ASIZE(LongName))) |
288 | | Attr=GetFileAttributes(LongName); |
289 | | } |
290 | | return Attr; |
291 | | #else |
292 | 0 | char NameA[NM]; |
293 | 0 | WideToChar(Name,NameA,ASIZE(NameA)); |
294 | 0 | struct stat st; |
295 | 0 | if (stat(NameA,&st)!=0) |
296 | 0 | return 0; |
297 | 0 | return st.st_mode; |
298 | 0 | #endif |
299 | 0 | } |
300 | | |
301 | | |
302 | | bool SetFileAttr(const wchar *Name,uint Attr) |
303 | 0 | { |
304 | | #ifdef _WIN_ALL |
305 | | bool Success=SetFileAttributes(Name,Attr)!=0; |
306 | | if (!Success) |
307 | | { |
308 | | wchar LongName[NM]; |
309 | | if (GetWinLongPath(Name,LongName,ASIZE(LongName))) |
310 | | Success=SetFileAttributes(LongName,Attr)!=0; |
311 | | } |
312 | | return Success; |
313 | | #elif defined(_UNIX) |
314 | 0 | char NameA[NM]; |
315 | 0 | WideToChar(Name,NameA,ASIZE(NameA)); |
316 | 0 | return chmod(NameA,(mode_t)Attr)==0; |
317 | | #else |
318 | | return false; |
319 | | #endif |
320 | 0 | } |
321 | | |
322 | | |
323 | | #if 0 |
324 | | wchar *MkTemp(wchar *Name,size_t MaxSize) |
325 | | { |
326 | | size_t Length=wcslen(Name); |
327 | | |
328 | | RarTime CurTime; |
329 | | CurTime.SetCurrentTime(); |
330 | | |
331 | | // We cannot use CurTime.GetWin() as is, because its lowest bits can |
332 | | // have low informational value, like being a zero or few fixed numbers. |
333 | | uint Random=(uint)(CurTime.GetWin()/100000); |
334 | | |
335 | | // Using PID we guarantee that different RAR copies use different temp names |
336 | | // even if started in exactly the same time. |
337 | | uint PID=0; |
338 | | #ifdef _WIN_ALL |
339 | | PID=(uint)GetCurrentProcessId(); |
340 | | #elif defined(_UNIX) |
341 | | PID=(uint)getpid(); |
342 | | #endif |
343 | | |
344 | | for (uint Attempt=0;;Attempt++) |
345 | | { |
346 | | uint Ext=Random%50000+Attempt; |
347 | | wchar RndText[50]; |
348 | | swprintf(RndText,ASIZE(RndText),L"%u.%03u",PID,Ext); |
349 | | if (Length+wcslen(RndText)>=MaxSize || Attempt==1000) |
350 | | return NULL; |
351 | | wcsncpyz(Name+Length,RndText,MaxSize-Length); |
352 | | if (!FileExist(Name)) |
353 | | break; |
354 | | } |
355 | | return Name; |
356 | | } |
357 | | #endif |
358 | | |
359 | | |
360 | | #if !defined(SFX_MODULE) |
361 | | void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size,uint Flags) |
362 | 0 | { |
363 | 0 | int64 SavePos=SrcFile->Tell(); |
364 | | #ifndef SILENT |
365 | | int64 FileLength=Size==INT64NDF ? SrcFile->FileLength() : Size; |
366 | | #endif |
367 | |
|
368 | 0 | if ((Flags & (CALCFSUM_SHOWTEXT|CALCFSUM_SHOWPERCENT))!=0) |
369 | 0 | uiMsg(UIEVENT_FILESUMSTART); |
370 | |
|
371 | 0 | if ((Flags & CALCFSUM_CURPOS)==0) |
372 | 0 | SrcFile->Seek(0,SEEK_SET); |
373 | |
|
374 | 0 | const size_t BufSize=0x100000; |
375 | 0 | Array<byte> Data(BufSize); |
376 | | |
377 | |
|
378 | 0 | DataHash HashCRC,HashBlake2; |
379 | 0 | HashCRC.Init(HASH_CRC32,Threads); |
380 | 0 | HashBlake2.Init(HASH_BLAKE2,Threads); |
381 | |
|
382 | 0 | int64 BlockCount=0; |
383 | 0 | int64 TotalRead=0; |
384 | 0 | while (true) |
385 | 0 | { |
386 | 0 | size_t SizeToRead; |
387 | 0 | if (Size==INT64NDF) // If we process the entire file. |
388 | 0 | SizeToRead=BufSize; // Then always attempt to read the entire buffer. |
389 | 0 | else |
390 | 0 | SizeToRead=(size_t)Min((int64)BufSize,Size); |
391 | 0 | int ReadSize=SrcFile->Read(&Data[0],SizeToRead); |
392 | 0 | if (ReadSize==0) |
393 | 0 | break; |
394 | 0 | TotalRead+=ReadSize; |
395 | |
|
396 | 0 | if ((++BlockCount & 0xf)==0) |
397 | 0 | { |
398 | | #ifndef SILENT |
399 | | if ((Flags & CALCFSUM_SHOWPROGRESS)!=0) |
400 | | { |
401 | | // Update only the current file progress in WinRAR, set the total to 0 |
402 | | // to keep it as is. It looks better for WinRAR, |
403 | | uiExtractProgress(TotalRead,FileLength,0,0); |
404 | | } |
405 | | else |
406 | | { |
407 | | if ((Flags & CALCFSUM_SHOWPERCENT)!=0) |
408 | | uiMsg(UIEVENT_FILESUMPROGRESS,ToPercent(TotalRead,FileLength)); |
409 | | } |
410 | | #endif |
411 | 0 | Wait(); |
412 | 0 | } |
413 | |
|
414 | 0 | if (CRC32!=NULL) |
415 | 0 | HashCRC.Update(&Data[0],ReadSize); |
416 | 0 | if (Blake2!=NULL) |
417 | 0 | HashBlake2.Update(&Data[0],ReadSize); |
418 | |
|
419 | 0 | if (Size!=INT64NDF) |
420 | 0 | Size-=ReadSize; |
421 | 0 | } |
422 | 0 | SrcFile->Seek(SavePos,SEEK_SET); |
423 | |
|
424 | 0 | if ((Flags & CALCFSUM_SHOWPERCENT)!=0) |
425 | 0 | uiMsg(UIEVENT_FILESUMEND); |
426 | |
|
427 | 0 | if (CRC32!=NULL) |
428 | 0 | *CRC32=HashCRC.GetCRC32(); |
429 | 0 | if (Blake2!=NULL) |
430 | 0 | { |
431 | 0 | HashValue Result; |
432 | 0 | HashBlake2.Result(&Result); |
433 | 0 | memcpy(Blake2,Result.Digest,sizeof(Result.Digest)); |
434 | 0 | } |
435 | 0 | } |
436 | | #endif |
437 | | |
438 | | |
439 | | bool RenameFile(const wchar *SrcName,const wchar *DestName) |
440 | 0 | { |
441 | | #ifdef _WIN_ALL |
442 | | bool Success=MoveFile(SrcName,DestName)!=0; |
443 | | if (!Success) |
444 | | { |
445 | | wchar LongName1[NM],LongName2[NM]; |
446 | | if (GetWinLongPath(SrcName,LongName1,ASIZE(LongName1)) && |
447 | | GetWinLongPath(DestName,LongName2,ASIZE(LongName2))) |
448 | | Success=MoveFile(LongName1,LongName2)!=0; |
449 | | } |
450 | | return Success; |
451 | | #else |
452 | 0 | char SrcNameA[NM],DestNameA[NM]; |
453 | 0 | WideToChar(SrcName,SrcNameA,ASIZE(SrcNameA)); |
454 | 0 | WideToChar(DestName,DestNameA,ASIZE(DestNameA)); |
455 | 0 | bool Success=rename(SrcNameA,DestNameA)==0; |
456 | 0 | return Success; |
457 | 0 | #endif |
458 | 0 | } |
459 | | |
460 | | |
461 | | bool DelFile(const wchar *Name) |
462 | 0 | { |
463 | | #ifdef _WIN_ALL |
464 | | bool Success=DeleteFile(Name)!=0; |
465 | | if (!Success) |
466 | | { |
467 | | wchar LongName[NM]; |
468 | | if (GetWinLongPath(Name,LongName,ASIZE(LongName))) |
469 | | Success=DeleteFile(LongName)!=0; |
470 | | } |
471 | | return Success; |
472 | | #else |
473 | 0 | char NameA[NM]; |
474 | 0 | WideToChar(Name,NameA,ASIZE(NameA)); |
475 | 0 | bool Success=remove(NameA)==0; |
476 | 0 | return Success; |
477 | 0 | #endif |
478 | 0 | } |
479 | | |
480 | | |
481 | | bool DelDir(const wchar *Name) |
482 | 0 | { |
483 | | #ifdef _WIN_ALL |
484 | | bool Success=RemoveDirectory(Name)!=0; |
485 | | if (!Success) |
486 | | { |
487 | | wchar LongName[NM]; |
488 | | if (GetWinLongPath(Name,LongName,ASIZE(LongName))) |
489 | | Success=RemoveDirectory(LongName)!=0; |
490 | | } |
491 | | return Success; |
492 | | #else |
493 | 0 | char NameA[NM]; |
494 | 0 | WideToChar(Name,NameA,ASIZE(NameA)); |
495 | 0 | bool Success=rmdir(NameA)==0; |
496 | 0 | return Success; |
497 | 0 | #endif |
498 | 0 | } |
499 | | |
500 | | |
501 | | #if defined(_WIN_ALL) && !defined(SFX_MODULE) |
502 | | bool SetFileCompression(const wchar *Name,bool State) |
503 | | { |
504 | | HANDLE hFile=CreateFile(Name,FILE_READ_DATA|FILE_WRITE_DATA, |
505 | | FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING, |
506 | | FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL); |
507 | | if (hFile==INVALID_HANDLE_VALUE) |
508 | | { |
509 | | wchar LongName[NM]; |
510 | | if (GetWinLongPath(Name,LongName,ASIZE(LongName))) |
511 | | hFile=CreateFile(LongName,FILE_READ_DATA|FILE_WRITE_DATA, |
512 | | FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING, |
513 | | FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL); |
514 | | } |
515 | | if (hFile==INVALID_HANDLE_VALUE) |
516 | | return false; |
517 | | SHORT NewState=State ? COMPRESSION_FORMAT_DEFAULT:COMPRESSION_FORMAT_NONE; |
518 | | DWORD Result; |
519 | | int RetCode=DeviceIoControl(hFile,FSCTL_SET_COMPRESSION,&NewState, |
520 | | sizeof(NewState),NULL,0,&Result,NULL); |
521 | | CloseHandle(hFile); |
522 | | return RetCode!=0; |
523 | | } |
524 | | |
525 | | |
526 | | void ResetFileCache(const wchar *Name) |
527 | | { |
528 | | // To reset file cache in Windows it is enough to open it with |
529 | | // FILE_FLAG_NO_BUFFERING and then close it. |
530 | | HANDLE hSrc=CreateFile(Name,GENERIC_READ, |
531 | | FILE_SHARE_READ|FILE_SHARE_WRITE, |
532 | | NULL,OPEN_EXISTING,FILE_FLAG_NO_BUFFERING,NULL); |
533 | | if (hSrc!=INVALID_HANDLE_VALUE) |
534 | | CloseHandle(hSrc); |
535 | | } |
536 | | #endif |
537 | | |
538 | | |
539 | | |
540 | | |
541 | | |
542 | | |
543 | | |
544 | | |
545 | | |
546 | | |