blob: cea1bac26de0ba21ae5789dfc4d514e5c50b4035 [file] [log] [blame]
/* **********************************************************
* 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);
}
}