Newer
Older
using System.Collections.Generic;
using System.Globalization;
using System.IO;
{
public static class FsUtils
{
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
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
/// <summary>
/// Create a directory with the given name if it doesn't exist yet, otherwise
/// (optionally) create a new one using a date suffix to distinguish it from
/// the existing one.
/// </summary>
/// <param name="dirPath">The full path of the directory to be created.</param>
/// <param name="unique">Add a time-suffix to the name if the directory exists.</param>
/// <returns>The name of the (created or pre-existing) directory. This will only
/// differ from the input parameter "dirPath" if the "unique" parameter is set
/// to true (then it will give the newly generated name) or if an error occured
/// (in which case it will return an empty string).</returns>
public static string CreateNewDirectory(string dirPath, bool unique) {
try {
if (Directory.Exists(dirPath)) {
// if unique was not requested, return the name of the existing dir:
if (unique == false)
return dirPath;
dirPath = dirPath + "_" + TimeUtils.Timestamp();
}
Directory.CreateDirectory(dirPath);
Log.Debug("Created directory: [{0}]", dirPath);
return dirPath;
}
catch (Exception ex) {
Log.Error("Error in CreateNewDirectory({0}): {1}", dirPath, ex.Message);
}
return "";
}
/// <summary>
/// Helper method to check if a directory exists, trying to create it if not.
/// </summary>
/// <param name="path">The full path of the directory to check / create.</param>
/// <returns>True if existing or creation was successful, false otherwise.</returns>
public static bool CheckForDirectory(string path) {
if (string.IsNullOrWhiteSpace(path)) {
Log.Error("ERROR: CheckForDirectory() parameter must not be empty!");
return false;
}
return FsUtils.CreateNewDirectory(path, false) == path;
}
/// <summary>
/// Recursively sum up size of all files under a given path.
/// </summary>
/// <param name="path">Full path of the directory.</param>
/// <returns>The total size in bytes.</returns>
public static long GetDirectorySize(string path) {
return new DirectoryInfo(path)
.GetFiles("*", SearchOption.AllDirectories)
.Sum(file => file.Length);
}
/// <summary>
/// Convert the timestamp given by the NAME of a directory into the age in days.
/// </summary>
/// <param name="dir">The DirectoryInfo object to check for its name-age.</param>
/// <param name="baseTime">The DateTime object to compare to.</param>
/// <returns>The age in days, or -1 in case of an error.</returns>
public static int DirNameToAge(DirectoryInfo dir, DateTime baseTime) {
DateTime dirTimestamp;
try {
dirTimestamp = DateTime.ParseExact(dir.Name, "yyyy-MM-dd__HH-mm-ss",
CultureInfo.InvariantCulture);
}
catch (Exception ex) {
// TODO: discuss if this should be an "Error" message to trigger a mail
// notification to the AdminDebug address:
Log.Warn("Unable to parse time from name [{0}], skipping: {1}",
dir.Name, ex.Message);
return -1;
}
return (baseTime - dirTimestamp).Days;
}
/// <summary>
/// Assemble a dictionary with information about expired directories.
/// </summary>
/// <param name="baseDir">The base directory to scan for subdirectories.</param>
/// <param name="thresh">The number of days used as expiration threshold.</param>
/// <returns>A dictionary having usernames as keys (of those users that actually do have
/// expired directories), where the values are lists of tuples with the DirInfo objects,
/// size (in bytes) and age (in days) of the expired directories.</returns>
public static Dictionary<string, List<Tuple<DirectoryInfo, long, int>>>
ExpiredDirs(DirectoryInfo baseDir,int thresh) {
var collection = new Dictionary<string, List<Tuple<DirectoryInfo, long, int>>>();
var now = DateTime.Now;
foreach (var userdir in baseDir.GetDirectories()) {
var expired = new List<Tuple<DirectoryInfo, long, int>>();
foreach (var subdir in userdir.GetDirectories()) {
var age = DirNameToAge(subdir, now);
if (age < thresh)
continue;
long size = -1;
try {
size = GetDirectorySize(subdir.FullName);
}
catch (Exception ex) {
Log.Error("ERROR getting directory size of [{0}]: {1}",
subdir.FullName, ex.Message);
}
expired.Add(new Tuple<DirectoryInfo, long, int>(subdir, size, age));
}
if (expired.Count > 0)
collection.Add(userdir.Name, expired);
}
return collection;
}
Niko Ehrenfeuchter
committed
/// <summary>
/// Generate a report on expired folders in the grace location.
///
/// Check all user-directories in the grace location for subdirectories whose timestamp
/// (the directory name) exceeds the configured grace period and generate a summary
/// containing the age and size of those directories.
/// </summary>
/// <param name="graceLocation">The location to scan for expired folders.</param>
/// <param name="threshold">The number of days used as expiration threshold.</param>
public static string GraceLocationSummary(DirectoryInfo graceLocation, int threshold) {
var expired = ExpiredDirs(graceLocation, threshold);
var report = "";
foreach (var userdir in expired.Keys) {
report += "\n - user '" + userdir + "'\n";
foreach (var subdir in expired[userdir]) {
report += string.Format(" - {0} [age: {2}, size: {1}]\n",
subdir.Item1, Conv.BytesToString(subdir.Item2),
TimeUtils.DaysToHuman(subdir.Item3));
Niko Ehrenfeuchter
committed
}
}
if (string.IsNullOrEmpty(report))
return "";
return "Expired folders in grace location:\n" + report;
}
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
/// <summary>
/// Check if a given directory is empty. If a marker file is set in the config a
/// file with this name will be created inside the given directory and will be
/// skipped itself when checking for files and directories.
/// </summary>
/// <param name="dirInfo">The directory to check.</param>
/// <param name="ignoredName">A filename that will be ignored.</param>
/// <returns>True if access is denied or the dir is empty, false otherwise.</returns>
public static bool DirEmptyExcept(DirectoryInfo dirInfo, string ignoredName) {
try {
var filesInTree = dirInfo.GetFiles("*", SearchOption.AllDirectories);
if (string.IsNullOrEmpty(ignoredName))
return filesInTree.Length == 0;
// check if there is ONLY the marker file:
if (filesInTree.Length == 1 &&
filesInTree[0].Name.Equals(ignoredName))
return true;
// make sure the marker file is there:
var markerFilePath = Path.Combine(dirInfo.FullName, ignoredName);
if (!File.Exists(markerFilePath))
File.Create(markerFilePath);
return filesInTree.Length == 0;
}
catch (Exception e) {
Log.Error("Error accessing directories: {0}", e.Message);
}
// if nothing triggered before, we pretend the dir is empty:
return true;
}
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
/// <summary>
/// Collect individual files in the root of a given directory tree in a specific
/// sub-directory. A file name given as "ignoredName" will be skipped in the checks.
/// </summary>
/// <param name="userDir">The user directory to check for individual files.</param>
/// <param name="ignoredName">A filename that will be ignored.</param>
public static void CollectOrphanedFiles(DirectoryInfo userDir, string ignoredName) {
var fileList = userDir.GetFiles();
var orphanedDir = Path.Combine(userDir.FullName, "orphaned");
try {
if (fileList.Length > 1 ||
(string.IsNullOrEmpty(ignoredName) && fileList.Length > 0)) {
if (Directory.Exists(orphanedDir)) {
Log.Info("Orphaned directory already exists, skipping individual files.");
return;
}
Log.Debug("Found individual files, collecting them in 'orphaned' folder.");
CreateNewDirectory(orphanedDir, false);
}
foreach (var file in fileList) {
if (file.Name.Equals(ignoredName))
continue;
Log.Debug("Collecting orphan: [{0}]", file.Name);
file.MoveTo(Path.Combine(orphanedDir, file.Name));
}
}
catch (Exception ex) {
Log.Error("Error collecting orphaned files: {0}\n{1}", ex.Message, ex.StackTrace);
}
}
/// <summary>
/// Ensure the required spooling directories (managed/incoming) exist.
/// </summary>
/// <param name="incoming">The path to the incoming location.</param>
/// <param name="managed">The path to the managed location.</param>
/// <returns>True if all dirs exist or were created successfully.</returns>
public static bool CheckSpoolingDirectories(string incoming, string managed) {
var retval = CheckForDirectory(incoming);
retval &= CheckForDirectory(managed);
retval &= CheckForDirectory(Path.Combine(managed, "PROCESSING"));
retval &= CheckForDirectory(Path.Combine(managed, "DONE"));
retval &= CheckForDirectory(Path.Combine(managed, "UNMATCHED"));
retval &= CheckForDirectory(Path.Combine(managed, "ERROR"));
/// <summary>
/// Move all subdirectories of a given path into a destination directory. The destination
/// will be created if it doesn't exist yet. If a subdirectory of the same name already
/// exists in the destination, a timestamp-suffix is added to the new one.
/// </summary>
/// <param name="sourceDir">The source path as DirectoryInfo object.</param>
/// <param name="destPath">The destination path as a string.</param>
/// <param name="resetAcls">Whether to reset the ACLs on the moved subdirectories.</param>
/// <returns>True on success, false otherwise.</returns>
public static bool MoveAllSubDirs(DirectoryInfo sourceDir, string destPath, bool resetAcls = false) {
// TODO: check whether _transferState should be adjusted while moving dirs!
Log.Debug("MoveAllSubDirs: [{0}] to [{1}]", sourceDir.FullName, destPath);
try {
// make sure the target directory that should hold all subdirectories to
// be moved is existing:
if (string.IsNullOrEmpty(CreateNewDirectory(destPath, false))) {
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
Log.Warn("WARNING: destination path doesn't exist: {0}", destPath);
return false;
}
foreach (var subDir in sourceDir.GetDirectories()) {
var target = Path.Combine(destPath, subDir.Name);
// make sure NOT to overwrite the subdirectories:
if (Directory.Exists(target))
target += "_" + TimeUtils.Timestamp();
Log.Debug(" - [{0}] > [{1}]", subDir.Name, target);
subDir.MoveTo(target);
if (!resetAcls)
continue;
try {
var acl = Directory.GetAccessControl(target);
acl.SetAccessRuleProtection(false, false);
Directory.SetAccessControl(target, acl);
Log.Debug("Successfully reset inherited ACLs on [{0}]", target);
}
catch (Exception ex) {
Log.Error("Error resetting inherited ACLs on [{0}]:\n{1}",
target, ex.Message);
}
}
}
catch (Exception ex) {
Log.Error("Error moving directories: [{0}] -> [{1}]: {2}",
sourceDir.FullName, destPath, ex.Message);
return false;
}
return true;
}