-
Niko Ehrenfeuchter authoredNiko Ehrenfeuchter authored
FsUtils.cs 5.04 KiB
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using NLog;
namespace ATXCommon
{
public static class FsUtils
{
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
/// <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 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) / Conv.MegaBytes;
}
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;
}
/// <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;
}
}
}