2 * PROJECT: Recycle bin management
3 * LICENSE: GPL v2 - See COPYING in the top level directory
4 * FILE: lib/recyclebin/openclose.c
5 * PURPOSE: Deals with recycle bins of Windows 2000/XP/2003
6 * PROGRAMMERS: Copyright 2006 Hervé Poussineau (hpoussin@reactos.org)
9 #include "recyclebin_v5.h"
13 IN OUT PRECYCLEBIN_CALLBACKS Callbacks
)
15 Callbacks
->CloseHandle
= CloseHandle5
;
16 Callbacks
->DeleteFile
= DeleteFile5
;
17 Callbacks
->EmptyRecycleBin
= EmptyRecycleBin5
;
18 Callbacks
->EnumerateFiles
= EnumerateFiles5
;
19 Callbacks
->GetDetails
= GetDetails5
;
20 Callbacks
->RestoreFile
= RestoreFile5
;
25 IN HANDLE hDeletedFile
)
27 /* Nothing to do, as hDeletedFile is simply a DWORD... */
37 HANDLE hFile
= INVALID_HANDLE_VALUE
;
39 DELETED_FILE_RECORD DeletedFile
;
40 DWORD bytesRead
, bytesWritten
;
41 ULARGE_INTEGER fileSize
;
42 SYSTEMTIME SystemTime
;
44 WCHAR DeletedFileName
[2 * MAX_PATH
];
46 DWORD ClusterSize
, BytesPerSector
, SectorsPerCluster
;
49 if (wcslen(FullPath
) >= MAX_PATH
)
51 /* Unable to store a too long path in recycle bin */
52 SetLastError(ERROR_INVALID_NAME
);
56 hFile
= CreateFileW(FullPath
, 0, 0, NULL
, OPEN_EXISTING
, 0, NULL
);
57 if (hFile
== INVALID_HANDLE_VALUE
)
60 if (SetFilePointer(bin
->hInfo
, 0, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
62 if (!ReadFile(bin
->hInfo
, &Header
, sizeof(INFO2_HEADER
), &bytesRead
, NULL
))
64 if (bytesRead
!= sizeof(INFO2_HEADER
) || Header
.dwRecordSize
== 0)
66 SetLastError(ERROR_GEN_FAILURE
);
70 if (Header
.dwVersion
!= 5 || Header
.dwRecordSize
!= sizeof(DELETED_FILE_RECORD
))
72 SetLastError(ERROR_GEN_FAILURE
);
78 if (!GetFileSizeEx(hFile
, &fileSize
))
81 fileSize
.u
.LowPart
= GetFileSize(hFile
, &fileSize
.u
.HighPart
);
82 if (fileSize
.u
.LowPart
== INVALID_FILE_SIZE
&& GetLastError() != NO_ERROR
)
85 /* Check if file size is > 4Gb */
86 if (fileSize
.u
.HighPart
!= 0)
88 /* FIXME: how to delete files >= 4Gb? */
89 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
92 Header
.dwTotalLogicalSize
+= fileSize
.u
.LowPart
;
94 /* Generate new name */
95 Header
.dwHighestRecordUniqueId
++;
96 Extension
= wcsrchr(FileName
, '.');
97 wsprintfW(DeletedFileName
, L
"%s\\D%c%lu%s", bin
->Folder
, FullPath
[0] - 'A' + 'a', Header
.dwHighestRecordUniqueId
, Extension
);
99 /* Get cluster size */
100 wsprintfW(RootDir
, L
"%c:\\", bin
->Folder
[0]);
101 if (!GetDiskFreeSpaceW(RootDir
, &SectorsPerCluster
, &BytesPerSector
, NULL
, NULL
))
103 ClusterSize
= BytesPerSector
* SectorsPerCluster
;
105 /* Get current time */
106 GetSystemTime(&SystemTime
);
107 if (!SystemTimeToFileTime(&SystemTime
, &DeletedFile
.DeletionTime
))
111 memset(&DeletedFile
, 0, sizeof(DELETED_FILE_RECORD
));
112 if (WideCharToMultiByte(CP_ACP
, 0, FullPath
, -1, DeletedFile
.FileNameA
, MAX_PATH
, NULL
, NULL
) == 0)
114 SetLastError(ERROR_INVALID_NAME
);
117 DeletedFile
.dwRecordUniqueId
= Header
.dwHighestRecordUniqueId
;
118 DeletedFile
.dwDriveNumber
= tolower(bin
->Folder
[0]) - 'a';
119 DeletedFile
.dwPhysicalFileSize
= ROUND_UP(fileSize
.u
.LowPart
, ClusterSize
);
120 wcscpy(DeletedFile
.FileNameW
, FullPath
);
122 if (!SetFilePointer(bin
->hInfo
, 0, NULL
, FILE_END
) == INVALID_SET_FILE_POINTER
)
124 if (!WriteFile(bin
->hInfo
, &DeletedFile
, sizeof(DELETED_FILE_RECORD
), &bytesWritten
, NULL
))
126 if (bytesWritten
!= sizeof(DELETED_FILE_RECORD
))
128 SetLastError(ERROR_GEN_FAILURE
);
131 Header
.dwNumberOfEntries
++;
132 if (SetFilePointer(bin
->hInfo
, 0, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
136 if (!WriteFile(bin
->hInfo
, &Header
, sizeof(INFO2_HEADER
), &bytesWritten
, NULL
))
138 if (bytesWritten
!= sizeof(INFO2_HEADER
))
140 SetLastError(ERROR_GEN_FAILURE
);
145 if (!MoveFileW(FullPath
, DeletedFileName
))
151 if (hFile
!= INVALID_HANDLE_VALUE
)
158 IN PRECYCLE_BIN
* bin
)
160 LPWSTR InfoFile
= NULL
;
163 InfoFile
= HeapAlloc(GetProcessHeap(), 0, wcslen((*bin
)->InfoFile
) * sizeof(WCHAR
) + sizeof(UNICODE_NULL
));
166 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
169 wcscpy(InfoFile
, (*bin
)->InfoFile
);
171 /* Delete all files in the recycle bin */
172 if (!EnumerateFiles5(*bin
, IntEmptyRecycleBinCallback
, *bin
))
176 if (!DereferenceHandle(&(*bin
)->refCount
))
178 if (!DeleteFileW(InfoFile
))
184 HeapFree(GetProcessHeap(), 0, InfoFile
);
191 IN PINT_ENUMERATE_RECYCLEBIN_CALLBACK pFnCallback
,
192 IN PVOID Context OPTIONAL
)
195 DELETED_FILE_RECORD DeletedFile
;
196 DWORD bytesRead
, dwEntries
;
199 if (SetFilePointer(bin
->hInfo
, 0, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
201 if (!ReadFile(bin
->hInfo
, &Header
, sizeof(INFO2_HEADER
), &bytesRead
, NULL
))
203 if (bytesRead
!= sizeof(INFO2_HEADER
) || Header
.dwRecordSize
== 0)
205 SetLastError(ERROR_GEN_FAILURE
);
209 if (Header
.dwVersion
!= 5 || Header
.dwRecordSize
!= sizeof(DELETED_FILE_RECORD
))
211 SetLastError(ERROR_GEN_FAILURE
);
215 SetLastError(ERROR_SUCCESS
);
216 for (dwEntries
= 0; dwEntries
< Header
.dwNumberOfEntries
; dwEntries
++)
218 if (!ReadFile(bin
->hInfo
, &DeletedFile
, Header
.dwRecordSize
, &bytesRead
, NULL
))
220 if (bytesRead
!= Header
.dwRecordSize
)
222 SetLastError(ERROR_GEN_FAILURE
);
225 if (!pFnCallback(Context
, (HANDLE
)(ULONG_PTR
)DeletedFile
.dwRecordUniqueId
))
227 if (SetFilePointer(bin
->hInfo
, sizeof(INFO2_HEADER
) + Header
.dwRecordSize
* dwEntries
, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
240 IN HANDLE hDeletedFile
,
242 IN OUT PDELETED_FILE_DETAILS_W FileDetails OPTIONAL
,
243 OUT LPDWORD RequiredSize OPTIONAL
)
245 DELETED_FILE_RECORD DeletedFile
;
247 LPWSTR FullName
= NULL
;
248 HANDLE hFile
= INVALID_HANDLE_VALUE
;
251 /* Check parameters */
252 if (BufferSize
> 0 && FileDetails
== NULL
)
254 SetLastError(ERROR_INVALID_PARAMETER
);
258 if (!IntSearchRecord(bin
, hDeletedFile
, &DeletedFile
, NULL
))
260 Needed
= FIELD_OFFSET(DELETED_FILE_DETAILS_W
, FileName
) + (wcslen(DeletedFile
.FileNameW
) + 1) * sizeof(WCHAR
);
262 *RequiredSize
= (DWORD
)Needed
;
263 if (Needed
> BufferSize
)
265 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
269 if (!IntGetFullName(bin
, &DeletedFile
, &FullName
))
273 FileDetails
->Attributes
= GetFileAttributesW(FullName
);
274 if (FileDetails
->Attributes
== INVALID_FILE_ATTRIBUTES
)
276 if (FileDetails
->Attributes
& FILE_ATTRIBUTE_DIRECTORY
)
277 hFile
= CreateFileW(FullName
, GENERIC_READ
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
279 hFile
= CreateFileW(FullName
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, NULL
);
280 if (hFile
== INVALID_HANDLE_VALUE
)
283 /* Fill returned structure */
284 if (!GetFileTime(hFile
, NULL
, NULL
, &FileDetails
->LastModification
))
286 memcpy(&FileDetails
->DeletionTime
, &DeletedFile
.DeletionTime
, sizeof(FILETIME
));
287 FileDetails
->FileSize
.u
.LowPart
= GetFileSize(hFile
, &FileDetails
->FileSize
.u
.HighPart
);
288 if (FileDetails
->FileSize
.u
.LowPart
== INVALID_FILE_SIZE
)
290 FileDetails
->PhysicalFileSize
.u
.HighPart
= 0;
291 FileDetails
->PhysicalFileSize
.u
.LowPart
= DeletedFile
.dwPhysicalFileSize
;
292 wcscpy(FileDetails
->FileName
, DeletedFile
.FileNameW
);
297 HeapFree(GetProcessHeap(), 0, FullName
);
298 if (hFile
!= INVALID_HANDLE_VALUE
)
306 IN HANDLE hDeletedFile
)
309 DWORD bytesRead
, bytesWritten
;
310 LARGE_INTEGER Position
;
311 DELETED_FILE_RECORD DeletedFile
, LastFile
;
312 LPWSTR FullName
= NULL
;
315 if (SetFilePointer(bin
->hInfo
, 0, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
317 if (!ReadFile(bin
->hInfo
, &Header
, sizeof(INFO2_HEADER
), &bytesRead
, NULL
))
319 if (bytesRead
!= sizeof(INFO2_HEADER
) || Header
.dwRecordSize
== 0)
321 SetLastError(ERROR_GEN_FAILURE
);
325 if (Header
.dwVersion
!= 5 || Header
.dwRecordSize
!= sizeof(DELETED_FILE_RECORD
))
327 SetLastError(ERROR_GEN_FAILURE
);
331 /* Search deleted entry */
332 if (!IntSearchRecord(bin
, hDeletedFile
, &DeletedFile
, &Position
))
334 /* Get destination full name */
335 if (!IntGetFullName(bin
, &DeletedFile
, &FullName
))
338 if (!MoveFileW(FullName
, DeletedFile
.FileNameW
))
342 /* 1) If not last entry, copy last entry to the current one */
343 if (SetFilePointer(bin
->hInfo
, -sizeof(DELETED_FILE_RECORD
), NULL
, FILE_END
) == INVALID_SET_FILE_POINTER
)
345 if (!ReadFile(bin
->hInfo
, &LastFile
, sizeof(DELETED_FILE_RECORD
), &bytesRead
, NULL
))
347 if (bytesRead
!= sizeof(DELETED_FILE_RECORD
))
349 SetLastError(ERROR_GEN_FAILURE
);
352 if (LastFile
.dwRecordUniqueId
!= DeletedFile
.dwRecordUniqueId
)
354 /* Move the last entry to the current one */
355 if (SetFilePointer(bin
->hInfo
, Position
.u
.LowPart
, &Position
.u
.HighPart
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
357 if (!WriteFile(bin
->hInfo
, &LastFile
, sizeof(DELETED_FILE_RECORD
), &bytesWritten
, NULL
))
359 if (bytesWritten
!= sizeof(DELETED_FILE_RECORD
))
361 SetLastError(ERROR_GEN_FAILURE
);
365 /* 2) Update the header */
366 Header
.dwNumberOfEntries
--;
367 if (SetFilePointer(bin
->hInfo
, 0, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
369 if (!WriteFile(bin
->hInfo
, &Header
, sizeof(INFO2_HEADER
), &bytesWritten
, NULL
))
371 if (bytesWritten
!= sizeof(INFO2_HEADER
))
373 SetLastError(ERROR_GEN_FAILURE
);
376 /* 3) Truncate file */
377 if (SetFilePointer(bin
->hInfo
, -sizeof(DELETED_FILE_RECORD
), NULL
, FILE_END
) == INVALID_SET_FILE_POINTER
)
379 if (!SetEndOfFile(bin
->hInfo
))
384 HeapFree(GetProcessHeap(), 0, FullName
);
392 DWORD RemovableAttributes
= FILE_ATTRIBUTE_READONLY
;
393 DWORD FileAttributes
;
396 FileAttributes
= GetFileAttributesW(FullName
);
397 if (FileAttributes
== INVALID_FILE_ATTRIBUTES
)
399 if (GetLastError() == ERROR_FILE_NOT_FOUND
)
403 if (FileAttributes
& RemovableAttributes
)
405 if (!SetFileAttributesW(FullName
, FileAttributes
& ~RemovableAttributes
))
408 if (FileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
410 /* Recursive deletion */
411 /* FIXME: recursive deletion */
412 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
415 if (!RemoveDirectoryW(FullName
))
420 if (!DeleteFileW(FullName
))
430 IntEmptyRecycleBinCallback(
432 IN HANDLE hDeletedFile
)
434 PRECYCLE_BIN bin
= (PRECYCLE_BIN
)Context
;
435 DELETED_FILE_RECORD DeletedFile
;
436 LPWSTR FullName
= NULL
;
439 if (!IntSearchRecord(bin
, hDeletedFile
, &DeletedFile
, NULL
))
442 if (!IntGetFullName(bin
, &DeletedFile
, &FullName
))
445 if (!IntDeleteRecursive(FullName
))
450 HeapFree(GetProcessHeap(), 0, FullName
);
457 IN PDELETED_FILE_RECORD pDeletedFile
,
458 OUT LPWSTR
* pFullName
)
462 LPWSTR FullName
= NULL
;
466 Extension
= wcsrchr(pDeletedFile
->FileNameW
, '.');
467 if (Extension
< wcsrchr(pDeletedFile
->FileNameW
, '\\'))
469 Needed
= wcslen(bin
->Folder
) + 13;
471 Needed
+= wcslen(Extension
);
472 FullName
= HeapAlloc(GetProcessHeap(), 0, Needed
* sizeof(WCHAR
));
475 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
478 wsprintfW(FullName
, L
"%s\\D%c%lu%s", bin
->Folder
, pDeletedFile
->dwDriveNumber
+ 'a', pDeletedFile
->dwRecordUniqueId
, Extension
);
479 *pFullName
= FullName
;
484 HeapFree(GetProcessHeap(), 0, FullName
);
491 IN HANDLE hDeletedFile
,
492 OUT PDELETED_FILE_RECORD pDeletedFile
,
493 OUT PLARGE_INTEGER Position OPTIONAL
)
496 DELETED_FILE_RECORD DeletedFile
;
497 DWORD bytesRead
, dwEntries
;
500 if (SetFilePointer(bin
->hInfo
, 0, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
502 if (!ReadFile(bin
->hInfo
, &Header
, sizeof(INFO2_HEADER
), &bytesRead
, NULL
))
504 if (bytesRead
!= sizeof(INFO2_HEADER
) || Header
.dwRecordSize
== 0)
506 SetLastError(ERROR_GEN_FAILURE
);
510 if (Header
.dwVersion
!= 5 || Header
.dwRecordSize
!= sizeof(DELETED_FILE_RECORD
))
512 SetLastError(ERROR_GEN_FAILURE
);
516 SetLastError(ERROR_SUCCESS
);
517 for (dwEntries
= 0; dwEntries
< Header
.dwNumberOfEntries
; dwEntries
++)
523 if (!SetFilePointerEx(bin
->hInfo
, Zero
, Position
, FILE_CURRENT
))
526 if (!ReadFile(bin
->hInfo
, &DeletedFile
, Header
.dwRecordSize
, &bytesRead
, NULL
))
528 if (bytesRead
!= Header
.dwRecordSize
)
530 SetLastError(ERROR_GEN_FAILURE
);
533 if (DeletedFile
.dwRecordUniqueId
== (DWORD
)(ULONG_PTR
)hDeletedFile
)
535 memcpy(pDeletedFile
, &DeletedFile
, Header
.dwRecordSize
);
541 /* Entry not found */
542 SetLastError(ERROR_INVALID_HANDLE
);