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
,
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
,
243 OUT LPDWORD RequiredSize OPTIONAL
)
245 DELETED_FILE_RECORD DeletedFile
;
247 LPWSTR FullName
= NULL
;
248 HANDLE hFile
= INVALID_HANDLE_VALUE
;
251 if (!IntSearchRecord(bin
, hDeletedFile
, &DeletedFile
, NULL
))
253 Needed
= (DWORD
)FIELD_OFFSET(DELETED_FILE_DETAILS_W
, FileName
) + (wcslen(DeletedFile
.FileNameW
) + 1) * sizeof(WCHAR
);
255 *RequiredSize
= (DWORD
)Needed
;
256 if (Needed
> BufferSize
)
258 SetLastError(ERROR_INSUFFICIENT_BUFFER
);
262 if (!IntGetFullName(bin
, &DeletedFile
, &FullName
))
266 FileDetails
->Attributes
= GetFileAttributesW(FullName
);
267 if (FileDetails
->Attributes
== INVALID_FILE_ATTRIBUTES
)
269 if (FileDetails
->Attributes
& FILE_ATTRIBUTE_DIRECTORY
)
270 hFile
= CreateFileW(FullName
, GENERIC_READ
, FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
272 hFile
= CreateFileW(FullName
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, NULL
);
273 if (hFile
== INVALID_HANDLE_VALUE
)
276 /* Fill returned structure */
277 if (!GetFileTime(hFile
, NULL
, NULL
, &FileDetails
->LastModification
))
279 memcpy(&FileDetails
->DeletionTime
, &DeletedFile
.DeletionTime
, sizeof(FILETIME
));
280 FileDetails
->FileSize
.u
.LowPart
= GetFileSize(hFile
, &FileDetails
->FileSize
.u
.HighPart
);
281 if (FileDetails
->FileSize
.u
.LowPart
== INVALID_FILE_SIZE
)
283 FileDetails
->PhysicalFileSize
.u
.HighPart
= 0;
284 FileDetails
->PhysicalFileSize
.u
.LowPart
= DeletedFile
.dwPhysicalFileSize
;
285 wcscpy(FileDetails
->FileName
, DeletedFile
.FileNameW
);
290 HeapFree(GetProcessHeap(), 0, FullName
);
291 if (hFile
!= INVALID_HANDLE_VALUE
)
299 IN HANDLE hDeletedFile
)
302 DWORD bytesRead
, bytesWritten
;
303 LARGE_INTEGER Position
;
304 DELETED_FILE_RECORD DeletedFile
, LastFile
;
305 LPWSTR FullName
= NULL
;
308 if (SetFilePointer(bin
->hInfo
, 0, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
310 if (!ReadFile(bin
->hInfo
, &Header
, sizeof(INFO2_HEADER
), &bytesRead
, NULL
))
312 if (bytesRead
!= sizeof(INFO2_HEADER
) || Header
.dwRecordSize
== 0)
314 SetLastError(ERROR_GEN_FAILURE
);
318 if (Header
.dwVersion
!= 5 || Header
.dwRecordSize
!= sizeof(DELETED_FILE_RECORD
))
320 SetLastError(ERROR_GEN_FAILURE
);
324 /* Search deleted entry */
325 if (!IntSearchRecord(bin
, hDeletedFile
, &DeletedFile
, &Position
))
327 /* Get destination full name */
328 if (!IntGetFullName(bin
, &DeletedFile
, &FullName
))
331 if (!MoveFileW(FullName
, DeletedFile
.FileNameW
))
335 /* 1) If not last entry, copy last entry to the current one */
336 if (SetFilePointer(bin
->hInfo
, -sizeof(DELETED_FILE_RECORD
), NULL
, FILE_END
) == INVALID_SET_FILE_POINTER
)
338 if (!ReadFile(bin
->hInfo
, &LastFile
, sizeof(DELETED_FILE_RECORD
), &bytesRead
, NULL
))
340 if (bytesRead
!= sizeof(DELETED_FILE_RECORD
))
342 SetLastError(ERROR_GEN_FAILURE
);
345 if (LastFile
.dwRecordUniqueId
!= DeletedFile
.dwRecordUniqueId
)
347 /* Move the last entry to the current one */
348 if (SetFilePointer(bin
->hInfo
, Position
.u
.LowPart
, &Position
.u
.HighPart
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
350 if (!WriteFile(bin
->hInfo
, &LastFile
, sizeof(DELETED_FILE_RECORD
), &bytesWritten
, NULL
))
352 if (bytesWritten
!= sizeof(DELETED_FILE_RECORD
))
354 SetLastError(ERROR_GEN_FAILURE
);
358 /* 2) Update the header */
359 Header
.dwNumberOfEntries
--;
360 if (SetFilePointer(bin
->hInfo
, 0, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
362 if (!WriteFile(bin
->hInfo
, &Header
, sizeof(INFO2_HEADER
), &bytesWritten
, NULL
))
364 if (bytesWritten
!= sizeof(INFO2_HEADER
))
366 SetLastError(ERROR_GEN_FAILURE
);
369 /* 3) Truncate file */
370 if (SetFilePointer(bin
->hInfo
, -sizeof(DELETED_FILE_RECORD
), NULL
, FILE_END
) == INVALID_SET_FILE_POINTER
)
372 if (!SetEndOfFile(bin
->hInfo
))
377 HeapFree(GetProcessHeap(), 0, FullName
);
385 DWORD RemovableAttributes
= FILE_ATTRIBUTE_READONLY
;
386 DWORD FileAttributes
;
389 FileAttributes
= GetFileAttributesW(FullName
);
390 if (FileAttributes
== INVALID_FILE_ATTRIBUTES
)
392 if (GetLastError() == ERROR_FILE_NOT_FOUND
)
396 if (FileAttributes
& RemovableAttributes
)
398 if (!SetFileAttributesW(FullName
, FileAttributes
& ~RemovableAttributes
))
401 if (FileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
403 /* Recursive deletion */
404 /* FIXME: recursive deletion */
405 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
408 if (!RemoveDirectoryW(FullName
))
413 if (!DeleteFileW(FullName
))
423 IntEmptyRecycleBinCallback(
425 IN HANDLE hDeletedFile
)
427 PRECYCLE_BIN bin
= (PRECYCLE_BIN
)Context
;
428 DELETED_FILE_RECORD DeletedFile
;
429 LPWSTR FullName
= NULL
;
432 if (!IntSearchRecord(bin
, hDeletedFile
, &DeletedFile
, NULL
))
435 if (!IntGetFullName(bin
, &DeletedFile
, &FullName
))
438 if (!IntDeleteRecursive(FullName
))
443 HeapFree(GetProcessHeap(), 0, FullName
);
450 IN PDELETED_FILE_RECORD pDeletedFile
,
451 OUT LPWSTR
* pFullName
)
455 LPWSTR FullName
= NULL
;
459 Extension
= wcsrchr(pDeletedFile
->FileNameW
, '.');
460 if (Extension
< wcsrchr(pDeletedFile
->FileNameW
, '\\'))
462 Needed
= wcslen(bin
->Folder
) + 13;
464 Needed
+= wcslen(Extension
);
465 FullName
= HeapAlloc(GetProcessHeap(), 0, Needed
* sizeof(WCHAR
));
468 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
471 wsprintfW(FullName
, L
"%s\\D%c%lu%s", bin
->Folder
, pDeletedFile
->dwDriveNumber
+ 'a', pDeletedFile
->dwRecordUniqueId
, Extension
);
472 *pFullName
= FullName
;
477 HeapFree(GetProcessHeap(), 0, FullName
);
484 IN HANDLE hDeletedFile
,
485 OUT PDELETED_FILE_RECORD pDeletedFile
,
486 OUT PLARGE_INTEGER Position OPTIONAL
)
489 DELETED_FILE_RECORD DeletedFile
;
490 DWORD bytesRead
, dwEntries
;
493 if (SetFilePointer(bin
->hInfo
, 0, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
495 if (!ReadFile(bin
->hInfo
, &Header
, sizeof(INFO2_HEADER
), &bytesRead
, NULL
))
497 if (bytesRead
!= sizeof(INFO2_HEADER
) || Header
.dwRecordSize
== 0)
499 SetLastError(ERROR_GEN_FAILURE
);
503 if (Header
.dwVersion
!= 5 || Header
.dwRecordSize
!= sizeof(DELETED_FILE_RECORD
))
505 SetLastError(ERROR_GEN_FAILURE
);
509 SetLastError(ERROR_SUCCESS
);
510 for (dwEntries
= 0; dwEntries
< Header
.dwNumberOfEntries
; dwEntries
++)
516 if (!SetFilePointerEx(bin
->hInfo
, Zero
, Position
, FILE_CURRENT
))
519 if (!ReadFile(bin
->hInfo
, &DeletedFile
, Header
.dwRecordSize
, &bytesRead
, NULL
))
521 if (bytesRead
!= Header
.dwRecordSize
)
523 SetLastError(ERROR_GEN_FAILURE
);
526 if (DeletedFile
.dwRecordUniqueId
== (DWORD
)(ULONG_PTR
)hDeletedFile
)
528 memcpy(pDeletedFile
, &DeletedFile
, Header
.dwRecordSize
);
534 /* Entry not found */
535 SetLastError(ERROR_INVALID_HANDLE
);