-
Notifications
You must be signed in to change notification settings - Fork 1
/
CommandClean.cs
218 lines (205 loc) · 8.71 KB
/
CommandClean.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
using System;
using System.ComponentModel;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace de.intronik.backup
{
[Command("clean", Syntax = "folders to scan", Description = "Removes unused hash entries from the hash folder.", MinParameterCount = 1, MaxParameterCount = int.MaxValue)]
public class CommandClean : CommandBase
{
Dictionary<HashEntry, bool> usedHashs = new Dictionary<HashEntry, bool>();
int typeIndex;
long deleteDirCount;
long deleteFileCount;
long totalBytesDeleted;
[Option(EmptyValue = "true", ShortDescription = "Enable delete operation")]
public bool EnableDelete { get; set; }
string PromptInput(string prompt, params object[] args)
{
return PromptInput(null, prompt, args);
}
string PromptInput(Func<string, bool> inputOkPredicate, string prompt, params object[] args)
{
while (true)
{
Console.Write(String.Format(prompt, args));
var answer = Console.ReadLine().Trim();
if (inputOkPredicate == null || inputOkPredicate(answer))
return answer;
}
}
bool MarkHashEntryAsUsed(string hashEntryPath, int level = 0)
{
bool marked = true;
if (hashEntryPath == null) return true;
if (!hashEntryPath.StartsWith(this.HashFolder, StringComparison.InvariantCultureIgnoreCase))
{
Console.WriteLine("\"{0}\" does not target hash folder \"{1}\" and it is ignored!", hashEntryPath, HashFolder);
return false;
}
var hashEntry = new PathHashEntry(hashEntryPath, this.typeIndex);
if (hashEntry.IsDirectory)
{
//
// directory entry
//
if (!this.usedHashs.ContainsKey(hashEntry))
{
this.usedHashs[hashEntry] = true;
foreach (var fileEntry in Directory.GetFileSystemEntries(hashEntryPath))
marked &= MarkHashEntryAsUsed(Win32.GetJunctionTarget(fileEntry), level + 1);
}
return marked;
}
else
{
//
// file entry
//
this.usedHashs[hashEntry] = true;
return true;
}
}
public void CheckDirectories(DirectoryInfo directory, int level = 0)
{
if (directory == null) return;
try
{
foreach (var entry in directory.GetFileSystemInfos())
{
if (level < MaxLevel)
Console.WriteLine(entry.FullName);
// ignore hash folder
if (entry.FullName.StartsWith(this.HashFolder, StringComparison.InvariantCultureIgnoreCase))
continue;
if ((entry.Attributes & FileAttributes.System) == FileAttributes.System)
continue;
if ((entry.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
continue;
// check if the folder is a symbolic link
var t = Win32.GetJunctionTarget(entry.FullName);
if (t != null)
// yes mark as used and we are done, when it could be marked
if (MarkHashEntryAsUsed(t, level + 1))
continue;
// descend in directories
CheckDirectories(entry as DirectoryInfo, level + 1);
}
}
catch
{
}
}
public void CheckDirectories(string path)
{
CheckDirectories(new DirectoryInfo(path), 0);
}
void DeleteUnused(DirectoryInfo directory, int typeIndex)
{
foreach (var entry in directory.GetFileSystemInfos())
{
if (!entry.FullName.StartsWith(this.HashFolder, StringComparison.InvariantCultureIgnoreCase))
{
Console.WriteLine("\"{0}\" does not target hash folder \"{1}\" and it is ignored!", entry.FullName, HashFolder);
return;
}
var h = new PathHashEntry(entry.FullName, typeIndex);
if (!this.usedHashs.ContainsKey(h))
{
if (h.IsDirectory)
{
if (EnableDelete)
{
foreach (var subEntry in ((DirectoryInfo)entry).GetFileSystemInfos())
subEntry.Delete();
((DirectoryInfo)entry).Delete();
}
this.deleteDirCount++;
}
else
{
this.deleteFileCount++;
this.totalBytesDeleted += ((FileInfo)entry).Length;
if (EnableDelete)
entry.Delete();
}
}
}
}
public void DeleteUnused()
{
for (var i = 0; i < 4096; i++)
DeleteUnused(new DirectoryInfo(Path.Combine(this.HashFolder, "d", i.ToString("x3"))), typeIndex);
for (var i = 0; i < 4096; i++)
DeleteUnused(new DirectoryInfo(Path.Combine(this.HashFolder, "f", i.ToString("x3"))), typeIndex);
}
protected override void SetParameters(string[] value)
{
// use current dir as default argument
base.SetParameters(value.Length > 0 ? value : new string[] {
Directory.GetCurrentDirectory(),
});
// set default hash folder
this.HashFolder = GetDefaultHashDir(this.Parameters.First());
}
protected override int DoOperation()
{
// update hash folder
if (!Directory.Exists(HashFolder))
throw new ArgumentException(String.Format("Invalid hashfolder \"{0}\"!", HashFolder));
this.typeIndex = this.HashFolder.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar).Length + 1;
// check on hash dir
foreach (var dir in this.Parameters)
{
Console.WriteLine("Scanning: \"{0}\"", dir);
CheckDirectories(dir);
}
print("Used hashs", this.usedHashs.Count);
if (this.usedHashs.Count == 0)
{
Console.WriteLine("No has files where marked as used! There is propably an error in the parameters!");
EnableDelete = false;
if (String.Compare(this.PromptInput("Continue anyway (this will erase the entire Hash directory)? [yes/NO] "), "yes", true) != 0)
{
Console.WriteLine("aborted");
return 1;
}
}
if (!EnableDelete)
Console.WriteLine("Counting unused files and directories in hash folder...");
else
Console.WriteLine("Deleting files and directories from hash folder...");
DeleteUnused();
print(EnableDelete ? "deleted files" : "unused files", deleteFileCount);
print(EnableDelete ? "deleted folders" : "unused folders", deleteDirCount);
print(EnableDelete ? "space gained" : "possible space", FormatBytes(totalBytesDeleted));
if ((deleteDirCount > 0 || deleteFileCount > 0) && !EnableDelete)
{
if (String.Compare(this.PromptInput("Do you want to delete these data (type \"yes\" completely or use command line option --enableDelete)? [yes/NO] "), "yes", true) != 0)
{
Console.WriteLine("aborted");
return 1;
}
EnableDelete = true;
Console.WriteLine("Deleting marked files and directories from hash folder...");
DeleteUnused();
if (this.usedHashs.Count == 0)
{
Console.WriteLine("Deleting hash root folder!");
Directory.Delete(HashFolder, true);
}
}
Console.WriteLine("done");
return 0;
}
protected override void ShowStatistics()
{
base.ShowStatistics();
print("deleted files", deleteFileCount);
print("deleted folders", deleteDirCount);
print("space gained", FormatBytes(totalBytesDeleted));
}
}
}