ACL2readonly
/
Program.cs
551 строка · 18.4 Кб
1#region License2//------------------------------------------------------------------------------
3// Copyright (c) Dmitrii Evdokimov
4// Source https://github.com/diev/
5//
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9// http://www.apache.org/licenses/LICENSE-2.0
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//------------------------------------------------------------------------------
16#endregion17
18using System;19using System.Configuration;20using System.Diagnostics;21using System.IO;22using System.Linq;23using System.Reflection;24using System.Runtime.InteropServices;25using System.Security.AccessControl;26using System.Security.Cryptography;27using System.Security.Principal;28using System.Text;29using System.Xml.Linq;30
31namespace ACL2readonly32{
33//:: Установка прав на архив34//icacls "path" /deny "principal":(DE) /t /c35
36internal class Program37{38//counters39private static int _totalDirs = 0;40private static int _totalFiles = 0;41private static int _newDirs = 0;42private static int _newFiles = 0;43private static int _renDirs = 0;44private static int _renFiles = 0;45private static int _renDirsE = 0;46private static int _renFilesE = 0;47private static int _level = 0;48private static int _maxLevel = 0;49private static int _errors = 0;50
51//app.config52private static readonly string _path = ConfigurationManager.AppSettings["Path"];53private static readonly string _deny = ConfigurationManager.AppSettings["Deny"];54private static string _log = ConfigurationManager.AppSettings["Log"];55private static string _logE;56
57//deny rights58private static readonly FileSystemRights _dirDeny = FileSystemRights.Delete | FileSystemRights.DeleteSubdirectoriesAndFiles;59private static readonly FileSystemRights _fileDeny = FileSystemRights.Delete | FileSystemRights.Write;60
61//cut basepath62private static readonly int _cut = _path.LastIndexOf(Path.DirectorySeparatorChar) + 1;63
64//switch logic65private static readonly bool _doRights = !string.IsNullOrEmpty(_deny);66
67private static void Main(string[] args)68{69//try70{71Console.WriteLine(Banner());72
73var logs = Directory.CreateDirectory(Path.Combine(_log, $"{DateTime.Now:yyyy}"));74
75_log = Path.Combine(logs.FullName, $"{DateTime.Now:yyyyMMdd}.log");76_logE = Path.Combine(logs.FullName, $"{DateTime.Now:yyyyMMdd}.err");77
78if (!Directory.Exists(_path))79{80Console.WriteLine($"Path \"{_path}\" not exists!");81Environment.Exit(2);82}83
84var watch = Stopwatch.StartNew();85
86Console.WriteLine("Wait...");87File.AppendAllText(_log, $"[{DateTime.Now:yyyy-MM-dd HH:mm}]\n");88ProcessDir(_path);89
90watch.Stop();91
92StringBuilder report = new StringBuilder();93report.AppendLine($"Execution Time: {watch.ElapsedMilliseconds} ms.");94report.Append("Total: ");95report.Append($"{_totalDirs} folders (+{_newDirs}, ren {_renDirs}/E{_renDirsE}), ");96report.Append($"{_totalFiles} files (+{_newFiles}, ren {_renFiles}/E{_renFilesE}), ");97report.Append($"{_maxLevel} levels, ");98report.Append($"{_errors} errors");99
100if (_errors > 0)101{102report.Append($" (see \"{_logE}\")");103}104
105report.AppendLine(".");106
107string s = report.ToString();108Console.WriteLine($"\n{s}");109File.AppendAllText(_log, $"{s}\n");110
111Environment.Exit(0);112}113//catch (Exception e)114//{115// Console.WriteLine(e.Message);116// File.AppendAllText(_logE, $"{e}\n\n");117
118// Environment.Exit(1);119//}120}121
122/// <summary>123/// Returns a banner text for this application.124/// </summary>125/// <returns>String of text.</returns>126private static string Banner()127{128var assembly = Assembly.GetCallingAssembly();129var assemblyName = assembly.GetName();130var name = assemblyName.Name;131var version = assemblyName.Version; // Major.Minor.Build.Revision132string build = (version.Revision > 0) ? $" build {version.Revision}" : "";133var ver = version.ToString(3);134var d = Attribute.GetCustomAttribute(assembly, typeof(AssemblyDescriptionAttribute)) as AssemblyDescriptionAttribute;135var c = Attribute.GetCustomAttribute(assembly, typeof(AssemblyCopyrightAttribute)) as AssemblyCopyrightAttribute;136string C = c.Copyright.Replace("\u00a9", "(c)");137
138return $"{name} v{ver}{build} - {d.Description}\n{C}\n";139}140
141/// <summary>142/// Processes a specified directory.143/// </summary>144/// <param name="file">Directory name.</param>145private static void ProcessDir(string dir)146{147_totalDirs++;148
149if (_level > _maxLevel)150{151_maxLevel = _level;152}153
154string sdir = dir.Substring(_cut) + Path.DirectorySeparatorChar;155
156string d1 = Path.GetFileName(dir);157string d2 = d1.Trim();158bool safe = true;159
160while (d2.Contains(".."))161{162d2 = d2.Replace("..", ".");163}164
165while (d2.Contains(" ."))166{167d2 = d2.Replace(" .", ".");168}169
170while (d2.Contains(" ,"))171{172d2 = d2.Replace(" ,", ",");173}174
175while (d2.Contains("( "))176{177d2 = d2.Replace("( ", "(");178}179
180while (d2.Contains(" )"))181{182d2 = d2.Replace(" )", ")");183}184
185while (d2.Contains(" "))186{187d2 = d2.Replace(" ", " ");188}189
190while (d2.EndsWith("."))191{192d2 = d2.Substring(0, d2.Length - 1);193safe = false;194}195
196if (!d2.Equals(d1))197{198string dir2 = Path.Combine(Path.GetDirectoryName(dir), d2);199
200if (Directory.Exists(dir2))201{202_renDirsE++;203File.AppendAllText(_logE, $"D2! \"{sdir}\"\n");204
205d2 = GetFreeDirname(d2);206dir2 = Path.Combine(Path.GetDirectoryName(dir), d2);207}208
209_renDirs++;210File.AppendAllText(_log, $"ren \"{sdir}\" \"{d2}\"\n");211
212if (safe)213{214Directory.Move(dir, dir2);215}216else if (!RenameDirectoryWithSideWhiteSpaces(dir, dir2))217{218_renDirsE++;219File.AppendAllText(_logE, $"DX! \"{sdir}\"\n");220
221return; //TODO bad dir!222}223
224dir = dir2;225sdir = dir.Substring(_cut) + Path.DirectorySeparatorChar;226
227//_renDirsE++;228//File.AppendAllText(_logE, $"D1! \"{sdir}\"\n");229}230
231if (_doRights)232{233DirectorySecurity dirSecurity;234
235try236{237if (_doRights)238{239dirSecurity = Directory.GetAccessControl(dir);240
241if (NewEntry(dirSecurity))242{243_newDirs++;244dirSecurity.AddAccessRule(new FileSystemAccessRule(_deny, _dirDeny, AccessControlType.Deny));245Directory.SetAccessControl(dir, dirSecurity);246
247Console.WriteLine(sdir);248File.AppendAllText(_log, $"{sdir}\n");249}250}251}252catch (ArgumentException)253{254_errors++;255Console.WriteLine("Error dirname! " + sdir);256File.AppendAllText(_logE, $"Error dirname! \"{sdir}\"\n");257}258catch (DirectoryNotFoundException)259{260_errors++;261Console.WriteLine("Error dirname_! " + sdir);262File.AppendAllText(_logE, $"Error dirname_! \"{sdir}\"\n");263}264catch (Exception e)265{266_errors++;267Console.WriteLine("Error dir! " + sdir);268File.AppendAllText(_logE, $"Error dir! \"{sdir}\"\n{e}\n\n");269}270}271
272var files = Directory.EnumerateFiles(dir);273
274foreach (string file in files)275{276ProcessFile(file);277}278
279_level++;280var subdirs = Directory.EnumerateDirectories(dir);281
282foreach (string subdir in subdirs)283{284ProcessDir(subdir);285}286
287_level--;288}289
290/// <summary>291/// Processes a specified file.292/// </summary>293/// <param name="file">Filename.</param>294private static void ProcessFile(string file)295{296_totalFiles++;297
298string sfile = file.Substring(_cut);299
300string f1 = Path.GetFileName(file);301string f2 = f1.Trim();302
303while (f2.Contains(".."))304{305f2 = f2.Replace("..", ".");306}307
308while (f2.Contains(" ."))309{310f2 = f2.Replace(" .", ".");311}312
313while (f2.Contains(" ,"))314{315f2 = f2.Replace(" ,", ",");316}317
318while (f2.Contains("( "))319{320f2 = f2.Replace("( ", "(");321}322
323while (f2.Contains(" )"))324{325f2 = f2.Replace(" )", ")");326}327
328while (f2.Contains(" "))329{330f2 = f2.Replace(" ", " ");331}332
333if (!f2.Equals(f1))334{335string file2 = Path.Combine(Path.GetDirectoryName(file), f2);336try337{338if (File.Exists(file2))339{340if (FileHashesEqual(file, file2))341{342_renFiles++;343File.AppendAllText(_log, $"ren(h) \"{sfile}\"\n");344File.Delete(file2);345File.Move(file, file2);346file = file2;347sfile = file.Substring(_cut);348}349else350{351//_renFilesE++;352//File.AppendAllText(_logE, $"F2! \"{sfile}\"\n");353
354_renFiles++;355File.AppendAllText(_log, $"ren(n) \"{sfile}\"\n");356file2 = GetFreeFilename(file2);357File.Move(file, file2);358file = file2;359sfile = file.Substring(_cut);360
361}362}363else364{365_renFiles++;366//File.AppendAllText(_log, $"ren \"{sfile}\"\n");367File.Move(file, file2);368file = file2;369sfile = file.Substring(_cut);370}371}372catch (Exception)373{374File.AppendAllText(_log, $"use \"{sfile}\"\n");375}376}377
378if (_doRights)379{380FileSecurity fileSecurity;381
382try383{384fileSecurity = File.GetAccessControl(file);385
386if (NewEntry(fileSecurity))387{388_newFiles++;389fileSecurity.AddAccessRule(new FileSystemAccessRule(_deny, _fileDeny, AccessControlType.Deny));390File.SetAccessControl(file, fileSecurity);391
392Console.WriteLine(sfile);393File.AppendAllText(_log, $"{sfile}\n");394}395}396catch (ArgumentException)397{398_errors++;399Console.WriteLine("Error filename! " + sfile);400File.AppendAllText(_logE, $"Error filename! \"{sfile}\"\n");401}402catch (Exception e)403{404_errors++;405Console.WriteLine("Error file! " + sfile);406File.AppendAllText(_logE, $"Error file! \"{sfile}\"\n{e}\n\n");407}408}409}410
411/// <summary>412/// Check if a new directory requires to change rights.413/// </summary>414/// <param name="security"></param>415/// <returns>True if this directory requires.</returns>416private static bool NewEntry(in DirectorySecurity security)417{418var rules = security.GetAccessRules(true, true, typeof(NTAccount));419
420foreach (FileSystemAccessRule rule in rules)421{422if (rule.AccessControlType.HasFlag(AccessControlType.Deny) &&423rule.IdentityReference.Value.Equals(_deny, StringComparison.OrdinalIgnoreCase) &&424rule.FileSystemRights.HasFlag(_dirDeny))425{426return false;427}428}429
430return true;431}432
433/// <summary>434/// Check if a new file requires to change rights.435/// </summary>436/// <param name="security"></param>437/// <returns>True if this file requires.</returns>438private static bool NewEntry(in FileSecurity security)439{440var rules = security.GetAccessRules(true, true, typeof(NTAccount));441
442foreach (FileSystemAccessRule rule in rules)443{444if (rule.AccessControlType.HasFlag(AccessControlType.Deny) &&445rule.IdentityReference.Value.Equals(_deny, StringComparison.OrdinalIgnoreCase) &&446rule.FileSystemRights.HasFlag(_fileDeny))447{448return false;449}450}451
452return true;453}454
455/// <summary>456/// Compares contents of two files if they are equal.457/// </summary>458/// <param name="file1">Filename 1.</param>459/// <param name="file2">Filename 2.</param>460/// <returns>True if contents are equal.</returns>461private static bool FileHashesEqual(string file1, string file2)462{463byte[] hash1, hash2;464
465using (var hasher = MD5.Create())466{467using (var stream = File.OpenRead(file1))468{469hash1 = hasher.ComputeHash(stream);470}471
472using (var stream = File.OpenRead(file2))473{474hash2 = hasher.ComputeHash(stream);475}476}477
478return hash1.Equals(hash2);479}480
481/// <summary>482/// Looks for a free dirname with (++counter) if there is same used.483/// </summary>484/// <param name="file">Dirname to look.</param>485/// <returns>New unused dirname.</returns>486private static string GetFreeDirname(string dir)487{488string ext = Path.GetExtension(dir);489string name = Path.ChangeExtension(dir, null);490int n = 1;491
492while (Directory.Exists($"{name} ({++n}){ext}"))493{494}495
496return $"{name} ({n}){ext}";497}498
499/// <summary>500/// Looks for a free filename with (++counter) if there is same used.501/// </summary>502/// <param name="file">Filename to look.</param>503/// <returns>New unused filename.</returns>504private static string GetFreeFilename(string file)505{506string ext = Path.GetExtension(file);507string name = Path.ChangeExtension(file, null);508int n = 1;509
510while (File.Exists($"{name} ({++n}){ext}"))511{512}513
514return $"{name} ({n}){ext}";515}516
517// https://social.msdn.microsoft.com/Forums/en-US/92f8813f-5dbf-4e67-b8eb-2c91de2a6696/directorynotfoundexception-when-directory-name-ends-with-white-space?forum=csharpgeneral518
519[DllImport("kernel32.dll", EntryPoint = "MoveFileW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]520private static extern bool MoveFile(string lpExistingFileName, string lpNewFileName);521
522private static bool RenameDirectoryWithSideWhiteSpaces(string path, string newPath)523{524var oldPath = path.StartsWith(@"\\?\") ? path : @"\\?\" + path;525var result = MoveFile(oldPath, newPath);526
527return result;528}529
530private static string RemoveSideWhiteSpaces(string path)531{532//var parent = Path.GetDirectoryName(path);533//var name = Path.GetFileName(path);534//var result = Path.Combine(parent ?? Path.GetPathRoot(path), name.Trim());535
536var breadcrumbs = path.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar).Select(x => x.Trim()).ToArray();537
538for (int i = 0; i < breadcrumbs.Length; i++)539{540while (breadcrumbs[i].EndsWith("."))541{542breadcrumbs[i] = breadcrumbs[i].Substring(0, breadcrumbs[i].Length - 1);543}544}545
546var result = string.Join(@"\", breadcrumbs);547
548return result;549}550}551}
552