| /* ********************************************************** |
| * Copyright (c) 2007 VMware, Inc. All rights reserved. |
| * **********************************************************/ |
| |
| /* |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * * Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * * Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * * Neither the name of VMware, Inc. nor the names of its contributors may be |
| * used to endorse or promote products derived from this software without |
| * specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
| * DAMAGE. |
| */ |
| |
| /* DRdel - clean ASLR or persistent cache files and directories */ |
| |
| /* most of the functionality used in nodemgr should be in share/ while |
| * this tool may be a good place for experimentation and test |
| * scaffolding |
| */ |
| #include <windows.h> |
| #include <assert.h> |
| #include <stdio.h> |
| |
| #include "share.h" |
| |
| #define DEBUG |
| |
| |
| #ifdef X64 |
| enum {LAST_STATUS_VALUE_OFFSET = 0x1250}; |
| #else |
| enum {LAST_STATUS_VALUE_OFFSET = 0xbf4}; /* on Win2000+ case 6789 */ |
| #endif |
| |
| int |
| get_last_status() |
| { |
| /* get_own_teb()->LastStatusValue */ |
| #ifdef X64 |
| return __readgsdword(LAST_STATUS_VALUE_OFFSET); |
| #else |
| return __readfsdword(LAST_STATUS_VALUE_OFFSET); |
| #endif |
| } |
| |
| |
| #ifdef DEBUG |
| int verbose = 0; |
| |
| void |
| print_status(bool ok) |
| { |
| printf("%s 0x%08x %s %d\n", |
| ok ? "" : "NTSTATUS", |
| ok ? 0 : get_last_status(), |
| ok ? "success:" : "GLE", GetLastError()); |
| } |
| #endif |
| |
| |
| int usage(char *us) |
| { |
| fprintf(stderr, "\ |
| Usage: %s\n\ |
| drdel -f <file> -d <directory> -r \n\ |
| -ms <size> target min size available\n\ |
| -us <size> target max size used\n\ |
| -f <file> work on one file only\n\ |
| -d <directory> work on a specified directory\n\ |
| -r use cache directories from registry\n\ |
| \n\ |
| -c check if in use and skip\n\ |
| -m mark for deletion when closed\n\ |
| -t .tmp renaming\n\ |
| -o on close delete\n\ |
| -b delete on next boot\n\ |
| \n\ |
| -v verbose\n\ |
| \n", us); |
| return 0; |
| } |
| |
| /* FIXME: should move this to share/utils.c */ |
| /* and merge/reuse or at least cf. with */ |
| /* file_exists() */ |
| /* get_unique_filename() */ |
| /* delete_file_rename_in_use() */ |
| /* copy_file_permissions() |
| * delete_tree() |
| */ |
| |
| DWORD |
| is_file_in_use(WCHAR *filename) |
| { |
| HANDLE hFile; |
| |
| hFile = CreateFile(filename, // file to open |
| GENERIC_READ, // open for reading |
| 0, // EXCLUSIVE access, should fail if in use |
| // note will fail anyone trying to use at this time |
| NULL, // default security |
| OPEN_EXISTING, // existing file only |
| FILE_ATTRIBUTE_NORMAL, // normal file |
| NULL); // no attr. template |
| |
| /* FIXME: is there a better way to check if a file is currently in |
| * use that doesn't get in the way of others. Admittedly very |
| * short race. Can we count on the handle properties, e.g. if |
| * QueryObject SYSTEM_OBJECT_INFORMATION.HandleCount and |
| * PointerCount are one number vs another. |
| */ |
| |
| /* |
| 0:000> !handle 10 f |
| Handle 10 |
| Type File |
| Attributes 0 |
| GrantedAccess 0x120089: |
| ReadControl,Synch |
| Read/List,ReadEA,ReadAttr |
| HandleCount 2 |
| PointerCount 3 |
| No Object Specific Information available |
| */ |
| |
| if (verbose) { |
| print_status(hFile != INVALID_HANDLE_VALUE); |
| } |
| |
| if (hFile == INVALID_HANDLE_VALUE) |
| return true; /* in use */ |
| CloseHandle(hFile); |
| return false; |
| } |
| |
| |
| /* see in utils.c file_exists() that one cannot |
| * open the root directory (and in fact "\\remote\share" as well) |
| */ |
| bool |
| is_file_present(WCHAR *filename) |
| { |
| HANDLE hFile; |
| |
| hFile = CreateFile(filename, // file to open |
| 0, // just existence check |
| FILE_SHARE_READ, // share for reading FIXME: do we need this |
| NULL, // default security |
| OPEN_EXISTING, // existing file only |
| FILE_ATTRIBUTE_NORMAL, // normal file |
| NULL); // no attr. template |
| if (verbose) { |
| print_status(hFile != INVALID_HANDLE_VALUE); |
| } |
| |
| if (hFile == INVALID_HANDLE_VALUE) |
| return false; |
| CloseHandle(hFile); |
| return true; |
| } |
| |
| |
| bool |
| delete_file_on_close(WCHAR *filename) |
| { |
| HANDLE hFile; |
| |
| hFile = CreateFile(filename, // file to open |
| 0, // just existence check |
| FILE_SHARE_READ|FILE_SHARE_DELETE, // share for reading FIXME: do we need this |
| NULL, // default security |
| OPEN_EXISTING, // existing file only |
| FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, // normal file, delete on close |
| NULL); // no attr. template |
| if (verbose) { |
| print_status(hFile != INVALID_HANDLE_VALUE); |
| } |
| |
| if (hFile == INVALID_HANDLE_VALUE) |
| return false; |
| CloseHandle(hFile); |
| return true; |
| } |
| |
| DWORD |
| delete_file(WCHAR *filename) |
| { |
| BOOL success = DeleteFile(filename); |
| if (verbose) { |
| print_status(success); |
| } |
| |
| if (success) |
| return ERROR_SUCCESS; |
| else { |
| return GetLastError(); |
| /* |
| * For memory mapped files - e.g. after an NtCreateSection() we get |
| * (NTSTATUS) 0xc0000121 (3221225761) - An attempt has been made to |
| * remove a file or directory that cannot be deleted. |
| */ |
| } |
| } |
| |
| int check_in_use = 0; |
| int delete = 0; |
| int temprename = 0; |
| int onboot = 0; |
| int onclose = 0; |
| |
| /* |
| * Possible states of a file |
| * {existing, not existing} x {in use, or not} x {DELETED, or not} |
| * x {.used, or not} x {pending reboot removal, or not} |
| * |
| reboot removal - MoveFileEx(MOVEFILE_DELAY_UNTIL_REBOOT) adds to PendingFileRenameOperations |
| HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations |
| |
| * - not existing |
| * - existing, hard link x {in use, not in use} |
| * - existing, sym link x {in use, not in use} (Longhorn) |
| * - existing, not in use |
| * - existing, not in use, previously renamed to .used |
| * - existing, in use |
| * - existing, DELETED, still in use |
| * - existing, in use, renamed to .used |
| * - existing, in use, DELETED, renamed to .used |
| * - existing, in use, DELETED, listed for reboot removal |
| * - existing, in use, DELETED, listed for reboot removal, renamed to .used |
| */ |
| |
| int |
| process_file(WCHAR *filename) |
| { |
| int in_use = 0; |
| int deleted = 0; |
| |
| if (verbose) { |
| printf("processing %S\n", filename); |
| |
| if (is_file_present(filename)) { |
| if (is_file_in_use(filename)) { |
| printf("file %ls is in use\n", filename); |
| } else { |
| printf("file %ls exists and is not in use\n", filename); |
| } |
| } else { |
| printf("file %ls doesn't exists\n", filename); |
| } |
| } |
| |
| if (check_in_use) { |
| in_use = is_file_in_use(filename); |
| if (verbose && in_use) { |
| printf("file %ls is in use\n", filename); |
| } |
| return 1; |
| } |
| |
| if (delete) { |
| deleted = delete_file(filename) == ERROR_SUCCESS; |
| } |
| |
| if (onboot) { |
| delete_file_on_boot(filename); |
| } |
| |
| if (onclose) { |
| delete_file_on_close(filename); |
| } |
| |
| if (!deleted) { |
| if (temprename) { |
| delete_file_rename_in_use(filename); |
| /* note this will also put in PendingFileRenameOperations */ |
| } |
| } |
| |
| return 0; |
| } |
| |
| int |
| process_directory(WCHAR *dirname) |
| { |
| |
| if (verbose) { |
| printf("delete_tree %S\n", dirname); |
| } |
| |
| delete_tree(dirname); |
| return 0; |
| } |
| |
| int |
| main(int argc, char *argv[]) |
| { |
| LPVOID *pv = 0; |
| LPVOID *pc = 0; |
| |
| int argidx = 1; |
| |
| int file = 0; |
| int dir = 0; |
| WCHAR filebuf[MAX_PATH]; |
| WCHAR dirbuf[MAX_PATH]; |
| |
| if (argc < 2) |
| usage(argv[0]); |
| |
| while (argidx < argc) { |
| if (!strcmp(argv[argidx], "-help")) { |
| usage(argv[0]); |
| exit(0); |
| } |
| else if (!strcmp(argv[argidx], "-f")) { |
| _snwprintf(filebuf, MAX_PATH, L"%S", argv[++argidx]); |
| file = 1; |
| } |
| else if (!strcmp(argv[argidx], "-d")) { |
| _snwprintf(dirbuf, MAX_PATH, L"%S", argv[++argidx]); |
| dir = 1; |
| } |
| else if (!strcmp(argv[argidx], "-m")) { |
| delete = 1; |
| } |
| else if (!strcmp(argv[argidx], "-o")) { |
| onclose = 1; |
| } |
| else if (!strcmp(argv[argidx], "-b")) { |
| onboot = 1; |
| } |
| else if (!strcmp(argv[argidx], "-t")) { |
| temprename = 1; |
| } |
| else if (!strcmp(argv[argidx], "-c")) { |
| check_in_use = 1; |
| } |
| else if (!strcmp(argv[argidx], "-v")) { |
| verbose = 1; |
| } |
| else { |
| fprintf(stderr, "Unknown option: %s\n", argv[argidx]); |
| usage(argv[0]); |
| exit(0); |
| } |
| argidx++; |
| } |
| |
| if (file) { |
| process_file(filebuf); |
| } |
| if (dir) { |
| process_directory(dirbuf); |
| } |
| } |