void RegisterReadOnlyFilesystem(IReadOnlyFilesystem plugin) { if (!ReadOnlyFilesystems.ContainsKey(plugin.Name.ToLower())) { ReadOnlyFilesystems.Add(plugin.Name.ToLower(), plugin); } }
string BuildFile(IReadOnlyFilesystem fs, string path, long length) { byte[] buffer = new byte[length]; fs.Read(path, 0, length, ref buffer); return(Md5Context.Data(buffer, out _)); }
Dictionary <string, FileData> BuildDirectory(IReadOnlyFilesystem fs, string path) { if (path == "/") { path = ""; } Dictionary <string, FileData> children = new Dictionary <string, FileData>(); fs.ReadDir(path, out List <string> contents); foreach (string child in contents) { string childPath = $"{path}/{child}"; fs.Stat(childPath, out FileEntryInfo stat); var data = new FileData { Info = stat }; if (stat.Attributes.HasFlag(FileAttributes.Directory)) { data.Children = BuildDirectory(fs, childPath); } else { data.MD5 = BuildFile(fs, childPath, stat.Length); } children[child] = data; } return(children); }
void TestFile(IReadOnlyFilesystem fs, string path, string md5, long length, string testFile) { byte[] buffer = new byte[length]; Errno ret = fs.Read(path, 0, length, ref buffer); Assert.AreEqual(Errno.NoError, ret, $"Unexpected error {ret} when reading \"{path}\" in {testFile}"); string data = Md5Context.Data(buffer, out _); Assert.AreEqual(md5, data, $"Got MD5 {data} for \"{path}\" in {testFile} but expected {md5}"); }
public void Init() { location = Path.Combine(Consts.TestFilesRoot, "filesystems", "fatx16", "be", "microsoft256mb.img.lz"); filter = new LZip(); filter.Open(location); image = new ZZZRawImage(); Assert.AreEqual(true, image.Open(filter)); fs = new XboxFatPlugin(); List <Partition> partitions = Core.Partitions.GetAll(image); Assert.AreEqual(2, partitions.Count); dataPartition = partitions[1]; Errno error = fs.Mount(image, dataPartition, null, null, null); Assert.AreEqual(Errno.NoError, error); }
FilesystemContentsType Files(IReadOnlyFilesystem filesystem) { var contents = new FilesystemContentsType(); Errno ret = filesystem.ReadDir("/", out List <string> dirents); if (ret != Errno.NoError) { return(null); } List <DirectoryType> directories = new List <DirectoryType>(); List <ContentsFileType> files = new List <ContentsFileType>(); foreach (string dirent in dirents) { ret = filesystem.Stat(dirent, out FileEntryInfo stat); if (ret != Errno.NoError) { AaruConsole.DebugWriteLine("Create-Sidecar command", "Cannot stat {0}", dirent); continue; } if (stat.Attributes.HasFlag(FileAttributes.Directory)) { directories.Add(SidecarDirectory(filesystem, "", dirent, stat)); continue; } files.Add(SidecarFile(filesystem, "", dirent, stat)); } if (files.Count > 0) { contents.File = files.OrderBy(f => f.name).ToArray(); } if (directories.Count > 0) { contents.Directory = directories.OrderBy(d => d.name).ToArray(); } return(contents); }
public void Init() { location = Path.Combine(Consts.TestFilesRoot, "filesystems", "fatx16", "le", "fatx.img.lz"); filter = new LZip(); filter.Open(location); image = new ZZZRawImage(); Assert.AreEqual(true, image.Open(filter)); fs = new XboxFatPlugin(); wholePart = new Partition { Name = "Whole device", Length = image.Info.Sectors, Size = image.Info.Sectors * image.Info.SectorSize }; Errno error = fs.Mount(image, wholePart, null, null, null); Assert.AreEqual(Errno.NoError, error); }
void TestFileXattrs(IReadOnlyFilesystem fs, string path, Dictionary <string, string> xattrs, string testFile) { fs.ListXAttr(path, out List <string> contents); if (xattrs.Count == 0 && contents.Count == 0) { return; } List <string> expectedNotFound = new List <string>(); foreach (KeyValuePair <string, string> xattr in xattrs) { byte[] buffer = new byte[0]; Errno ret = fs.GetXattr(path, xattr.Key, ref buffer); if (ret == Errno.NoSuchExtendedAttribute || !contents.Contains(xattr.Key)) { expectedNotFound.Add(xattr.Key); continue; } contents.Remove(xattr.Key); Assert.AreEqual(Errno.NoError, ret, $"Unexpected error {ret} retrieving extended attributes for \"{path}\" in {testFile}"); string data = Md5Context.Data(buffer, out _); Assert.AreEqual(xattr.Value, data, $"Got MD5 {data} for {xattr.Key} of \"{path}\" in {testFile} but expected {xattr.Value}"); } Assert.IsEmpty(expectedNotFound, $"Could not find the following extended attributes of \"{path}\" in {testFile}: {string.Join(" ", expectedNotFound)}"); Assert.IsEmpty(contents, $"Found the following unexpected extended attributes of \"{path}\" in {testFile}: {string.Join(" ", contents)}"); }
DirectoryType SidecarDirectory(IReadOnlyFilesystem filesystem, string path, string filename, FileEntryInfo stat) { var directory = new DirectoryType(); if (stat.AccessTimeUtc.HasValue) { directory.accessTime = stat.AccessTimeUtc.Value; directory.accessTimeSpecified = true; } directory.attributes = (ulong)stat.Attributes; if (stat.BackupTimeUtc.HasValue) { directory.backupTime = stat.BackupTimeUtc.Value; directory.backupTimeSpecified = true; } if (stat.CreationTimeUtc.HasValue) { directory.creationTime = stat.CreationTimeUtc.Value; directory.creationTimeSpecified = true; } if (stat.DeviceNo.HasValue) { directory.deviceNumber = stat.DeviceNo.Value; directory.deviceNumberSpecified = true; } directory.inode = stat.Inode; if (stat.LastWriteTimeUtc.HasValue) { directory.lastWriteTime = stat.LastWriteTimeUtc.Value; directory.lastWriteTimeSpecified = true; } directory.links = stat.Links; directory.name = filename; if (stat.GID.HasValue) { directory.posixGroupId = stat.GID.Value; directory.posixGroupIdSpecified = true; } if (stat.Mode.HasValue) { directory.posixMode = stat.Mode.Value; directory.posixModeSpecified = true; } if (stat.UID.HasValue) { directory.posixUserId = stat.UID.Value; directory.posixUserIdSpecified = true; } if (stat.StatusChangeTimeUtc.HasValue) { directory.statusChangeTime = stat.StatusChangeTimeUtc.Value; directory.statusChangeTimeSpecified = true; } Errno ret = filesystem.ReadDir(path + "/" + filename, out List <string> dirents); if (ret != Errno.NoError) { return(null); } List <DirectoryType> directories = new List <DirectoryType>(); List <ContentsFileType> files = new List <ContentsFileType>(); foreach (string dirent in dirents) { ret = filesystem.Stat(path + "/" + filename + "/" + dirent, out FileEntryInfo entryStat); if (ret != Errno.NoError) { AaruConsole.DebugWriteLine("Create-Sidecar command", "Cannot stat {0}", dirent); continue; } if (entryStat.Attributes.HasFlag(FileAttributes.Directory)) { directories.Add(SidecarDirectory(filesystem, path + "/" + filename, dirent, entryStat)); continue; } files.Add(SidecarFile(filesystem, path + "/" + filename, dirent, entryStat)); } if (files.Count > 0) { directory.File = files.OrderBy(f => f.name).ToArray(); } if (directories.Count > 0) { directory.Directory = directories.OrderBy(d => d.name).ToArray(); } return(directory); }
ContentsFileType SidecarFile(IReadOnlyFilesystem filesystem, string path, string filename, FileEntryInfo stat) { var file = new ContentsFileType(); var fileChkWorker = new Checksum(); if (stat.AccessTimeUtc.HasValue) { file.accessTime = stat.AccessTimeUtc.Value; file.accessTimeSpecified = true; } file.attributes = (ulong)stat.Attributes; if (stat.BackupTimeUtc.HasValue) { file.backupTime = stat.BackupTimeUtc.Value; file.backupTimeSpecified = true; } if (stat.CreationTimeUtc.HasValue) { file.creationTime = stat.CreationTimeUtc.Value; file.creationTimeSpecified = true; } if (stat.DeviceNo.HasValue) { file.deviceNumber = stat.DeviceNo.Value; file.deviceNumberSpecified = true; } file.inode = stat.Inode; if (stat.LastWriteTimeUtc.HasValue) { file.lastWriteTime = stat.LastWriteTimeUtc.Value; file.lastWriteTimeSpecified = true; } file.length = (ulong)stat.Length; file.links = stat.Links; file.name = filename; if (stat.GID.HasValue) { file.posixGroupId = stat.GID.Value; file.posixGroupIdSpecified = true; } if (stat.Mode.HasValue) { file.posixMode = stat.Mode.Value; file.posixModeSpecified = true; } if (stat.UID.HasValue) { file.posixUserId = stat.UID.Value; file.posixUserIdSpecified = true; } if (stat.StatusChangeTimeUtc.HasValue) { file.statusChangeTime = stat.StatusChangeTimeUtc.Value; file.statusChangeTimeSpecified = true; } byte[] data = new byte[0]; if (stat.Length > 0) { long position = 0; UpdateStatus($"Hashing file {path}/{filename}..."); InitProgress2(); while (position < stat.Length - 1048576) { if (_aborted) { return(file); } data = new byte[1048576]; filesystem.Read(path + "/" + filename, position, 1048576, ref data); UpdateProgress2("Hashing file byte {0} of {1}", position, stat.Length); fileChkWorker.Update(data); position += 1048576; } data = new byte[stat.Length - position]; filesystem.Read(path + "/" + filename, position, stat.Length - position, ref data); UpdateProgress2("Hashing file byte {0} of {1}", position, stat.Length); fileChkWorker.Update(data); EndProgress(); file.Checksums = fileChkWorker.End().ToArray(); } else { file.Checksums = _emptyChecksums; } Errno ret = filesystem.ListXAttr(path + "/" + filename, out List <string> xattrs); if (ret != Errno.NoError) { return(file); } List <ExtendedAttributeType> xattrTypes = new List <ExtendedAttributeType>(); foreach (string xattr in xattrs) { ret = filesystem.GetXattr(path + "/" + filename, xattr, ref data); if (ret != Errno.NoError) { continue; } var xattrChkWorker = new Checksum(); xattrChkWorker.Update(data); xattrTypes.Add(new ExtendedAttributeType { Checksums = xattrChkWorker.End().ToArray(), length = (ulong)data.Length, name = xattr }); } if (xattrTypes.Count > 0) { file.ExtendedAttributes = xattrTypes.OrderBy(x => x.name).ToArray(); } return(file); }
public override int Invoke(IEnumerable <string> arguments) { List <string> extra = Options.Parse(arguments); if (showHelp) { Options.WriteOptionDescriptions(CommandSet.Out); return((int)ErrorNumber.HelpRequested); } MainClass.PrintCopyright(); if (MainClass.Debug) { DicConsole.DebugWriteLineEvent += System.Console.Error.WriteLine; } if (MainClass.Verbose) { DicConsole.VerboseWriteLineEvent += System.Console.WriteLine; } Statistics.AddCommand("extract-files"); if (extra.Count > 1) { DicConsole.ErrorWriteLine("Too many arguments."); return((int)ErrorNumber.UnexpectedArgumentCount); } if (extra.Count == 0) { DicConsole.ErrorWriteLine("Missing input image."); return((int)ErrorNumber.MissingArgument); } inputFile = extra[0]; DicConsole.DebugWriteLine("Extract-Files command", "--debug={0}", MainClass.Debug); DicConsole.DebugWriteLine("Extract-Files command", "--encoding={0}", encodingName); DicConsole.DebugWriteLine("Extract-Files command", "--input={0}", inputFile); DicConsole.DebugWriteLine("Extract-Files command", "--options={0}", pluginOptions); DicConsole.DebugWriteLine("Extract-Files command", "--output={0}", outputDir); DicConsole.DebugWriteLine("Extract-Files command", "--verbose={0}", MainClass.Verbose); DicConsole.DebugWriteLine("Extract-Files command", "--xattrs={0}", extractXattrs); FiltersList filtersList = new FiltersList(); IFilter inputFilter = filtersList.GetFilter(inputFile); Dictionary <string, string> parsedOptions = Core.Options.Parse(pluginOptions); DicConsole.DebugWriteLine("Extract-Files command", "Parsed options:"); foreach (KeyValuePair <string, string> parsedOption in parsedOptions) { DicConsole.DebugWriteLine("Extract-Files command", "{0} = {1}", parsedOption.Key, parsedOption.Value); } parsedOptions.Add("debug", MainClass.Debug.ToString()); if (inputFilter == null) { DicConsole.ErrorWriteLine("Cannot open specified file."); return((int)ErrorNumber.CannotOpenFile); } Encoding encoding = null; if (encodingName != null) { try { encoding = Claunia.Encoding.Encoding.GetEncoding(encodingName); if (MainClass.Verbose) { DicConsole.VerboseWriteLine("Using encoding for {0}.", encoding.EncodingName); } } catch (ArgumentException) { DicConsole.ErrorWriteLine("Specified encoding is not supported."); return((int)ErrorNumber.EncodingUnknown); } } PluginBase plugins = GetPluginBase.Instance; try { IMediaImage imageFormat = ImageFormat.Detect(inputFilter); if (imageFormat == null) { DicConsole.WriteLine("Image format not identified, not proceeding with analysis."); return((int)ErrorNumber.UnrecognizedFormat); } if (MainClass.Verbose) { DicConsole.VerboseWriteLine("Image format identified by {0} ({1}).", imageFormat.Name, imageFormat.Id); } else { DicConsole.WriteLine("Image format identified by {0}.", imageFormat.Name); } if (outputDir == null) { DicConsole.WriteLine("Output directory missing."); return((int)ErrorNumber.MissingArgument); } if (Directory.Exists(outputDir) || File.Exists(outputDir)) { DicConsole.ErrorWriteLine("Destination exists, aborting."); return((int)ErrorNumber.DestinationExists); } Directory.CreateDirectory(outputDir); try { if (!imageFormat.Open(inputFilter)) { DicConsole.WriteLine("Unable to open image format"); DicConsole.WriteLine("No error given"); return((int)ErrorNumber.CannotOpenFormat); } DicConsole.DebugWriteLine("Extract-Files command", "Correctly opened image file."); DicConsole.DebugWriteLine("Extract-Files command", "Image without headers is {0} bytes.", imageFormat.Info.ImageSize); DicConsole.DebugWriteLine("Extract-Files command", "Image has {0} sectors.", imageFormat.Info.Sectors); DicConsole.DebugWriteLine("Extract-Files command", "Image identifies disk type as {0}.", imageFormat.Info.MediaType); Statistics.AddMediaFormat(imageFormat.Format); Statistics.AddMedia(imageFormat.Info.MediaType, false); Statistics.AddFilter(inputFilter.Name); } catch (Exception ex) { DicConsole.ErrorWriteLine("Unable to open image format"); DicConsole.ErrorWriteLine("Error: {0}", ex.Message); return((int)ErrorNumber.CannotOpenFormat); } List <Partition> partitions = Core.Partitions.GetAll(imageFormat); Core.Partitions.AddSchemesToStats(partitions); List <string> idPlugins; IReadOnlyFilesystem plugin; Errno error; if (partitions.Count == 0) { DicConsole.DebugWriteLine("Extract-Files command", "No partitions found"); } else { DicConsole.WriteLine("{0} partitions found.", partitions.Count); for (int i = 0; i < partitions.Count; i++) { DicConsole.WriteLine(); DicConsole.WriteLine("Partition {0}:", partitions[i].Sequence); DicConsole.WriteLine("Identifying filesystem on partition"); Core.Filesystems.Identify(imageFormat, out idPlugins, partitions[i]); if (idPlugins.Count == 0) { DicConsole.WriteLine("Filesystem not identified"); } else if (idPlugins.Count > 1) { DicConsole.WriteLine($"Identified by {idPlugins.Count} plugins"); foreach (string pluginName in idPlugins) { if (plugins.ReadOnlyFilesystems.TryGetValue(pluginName, out plugin)) { DicConsole.WriteLine($"As identified by {plugin.Name}."); IReadOnlyFilesystem fs = (IReadOnlyFilesystem)plugin .GetType() .GetConstructor(Type.EmptyTypes) ?.Invoke(new object[] { }); error = fs.Mount(imageFormat, partitions[i], encoding, parsedOptions, @namespace); if (error == Errno.NoError) { string volumeName = string.IsNullOrEmpty(fs.XmlFsType.VolumeName) ? "NO NAME" : fs.XmlFsType.VolumeName; ExtractFilesInDir("/", fs, volumeName); Statistics.AddFilesystem(fs.XmlFsType.Type); } else { DicConsole.ErrorWriteLine("Unable to mount device, error {0}", error.ToString()); } } } } else { plugins.ReadOnlyFilesystems.TryGetValue(idPlugins[0], out plugin); DicConsole.WriteLine($"Identified by {plugin.Name}."); IReadOnlyFilesystem fs = (IReadOnlyFilesystem)plugin .GetType().GetConstructor(Type.EmptyTypes) ?.Invoke(new object[] { }); error = fs.Mount(imageFormat, partitions[i], encoding, parsedOptions, @namespace); if (error == Errno.NoError) { string volumeName = string.IsNullOrEmpty(fs.XmlFsType.VolumeName) ? "NO NAME" : fs.XmlFsType.VolumeName; ExtractFilesInDir("/", fs, volumeName); Statistics.AddFilesystem(fs.XmlFsType.Type); } else { DicConsole.ErrorWriteLine("Unable to mount device, error {0}", error.ToString()); } } } } Partition wholePart = new Partition { Name = "Whole device", Length = imageFormat.Info.Sectors, Size = imageFormat.Info.Sectors * imageFormat.Info.SectorSize }; Core.Filesystems.Identify(imageFormat, out idPlugins, wholePart); if (idPlugins.Count == 0) { DicConsole.WriteLine("Filesystem not identified"); } else if (idPlugins.Count > 1) { DicConsole.WriteLine($"Identified by {idPlugins.Count} plugins"); foreach (string pluginName in idPlugins) { if (plugins.ReadOnlyFilesystems.TryGetValue(pluginName, out plugin)) { DicConsole.WriteLine($"As identified by {plugin.Name}."); IReadOnlyFilesystem fs = (IReadOnlyFilesystem)plugin .GetType().GetConstructor(Type.EmptyTypes) ?.Invoke(new object[] { }); error = fs.Mount(imageFormat, wholePart, encoding, parsedOptions, @namespace); if (error == Errno.NoError) { string volumeName = string.IsNullOrEmpty(fs.XmlFsType.VolumeName) ? "NO NAME" : fs.XmlFsType.VolumeName; ExtractFilesInDir("/", fs, volumeName); Statistics.AddFilesystem(fs.XmlFsType.Type); } else { DicConsole.ErrorWriteLine("Unable to mount device, error {0}", error.ToString()); } } } } else { plugins.ReadOnlyFilesystems.TryGetValue(idPlugins[0], out plugin); DicConsole.WriteLine($"Identified by {plugin.Name}."); IReadOnlyFilesystem fs = (IReadOnlyFilesystem)plugin.GetType().GetConstructor(Type.EmptyTypes)?.Invoke(new object[] { }); error = fs.Mount(imageFormat, wholePart, encoding, parsedOptions, @namespace); if (error == Errno.NoError) { string volumeName = string.IsNullOrEmpty(fs.XmlFsType.VolumeName) ? "NO NAME" : fs.XmlFsType.VolumeName; ExtractFilesInDir("/", fs, volumeName); Statistics.AddFilesystem(fs.XmlFsType.Type); } else { DicConsole.ErrorWriteLine("Unable to mount device, error {0}", error.ToString()); } } } catch (Exception ex) { DicConsole.ErrorWriteLine($"Error reading file: {ex.Message}"); DicConsole.DebugWriteLine("Extract-Files command", ex.StackTrace); return((int)ErrorNumber.UnexpectedException); } return((int)ErrorNumber.NoError); }
static void ListFilesInDir(string path, IReadOnlyFilesystem fs, bool longFormat) { if (path.StartsWith('/')) { path = path.Substring(1); } AaruConsole.WriteLine(string.IsNullOrEmpty(path) ? "Root directory" : $"Directory: {path}"); Errno error = fs.ReadDir(path, out List <string> directory); if (error != Errno.NoError) { AaruConsole.ErrorWriteLine("Error {0} reading root directory {1}", error.ToString(), path); return; } Dictionary <string, FileEntryInfo> stats = new Dictionary <string, FileEntryInfo>(); foreach (string entry in directory) { fs.Stat(path + "/" + entry, out FileEntryInfo stat); stats.Add(entry, stat); } foreach (KeyValuePair <string, FileEntryInfo> entry in stats.OrderBy(e => e.Value?.Attributes.HasFlag(FileAttributes.Directory) == false)) { if (longFormat) { if (entry.Value != null) { if (entry.Value.Attributes.HasFlag(FileAttributes.Directory)) { AaruConsole.WriteLine("{0, 10:d} {0, 12:T} {1, -20} {2}", entry.Value.CreationTimeUtc, "<DIR>", entry.Key); } else { AaruConsole.WriteLine("{0, 10:d} {0, 12:T} {1, 6}{2, 14:N0} {3}", entry.Value.CreationTimeUtc, entry.Value.Inode, entry.Value.Length, entry.Key); } error = fs.ListXAttr(path + "/" + entry.Key, out List <string> xattrs); if (error != Errno.NoError) { continue; } foreach (string xattr in xattrs) { byte[] xattrBuf = new byte[0]; error = fs.GetXattr(path + "/" + entry.Key, xattr, ref xattrBuf); if (error == Errno.NoError) { AaruConsole.WriteLine("\t\t{0}\t{1:##,#}", xattr, xattrBuf.Length); } } } else { AaruConsole.WriteLine("{0, 47}{1}", string.Empty, entry.Key); } } else { AaruConsole. WriteLine(entry.Value?.Attributes.HasFlag(FileAttributes.Directory) == true ? "{0}/" : "{0}", entry.Key); } } AaruConsole.WriteLine(); foreach (KeyValuePair <string, FileEntryInfo> subdirectory in stats.Where(e => e.Value?.Attributes.HasFlag(FileAttributes.Directory) == true)) { ListFilesInDir(path + "/" + subdirectory.Key, fs, longFormat); } }
public void Destroy() { fs?.Unmount(); fs = null; }
protected void OnMenuOpen(object sender, EventArgs e) { // TODO: Extensions OpenFileDialog dlgOpenImage = new OpenFileDialog { Title = "Choose image to open" }; DialogResult result = dlgOpenImage.ShowDialog(this); if (result != DialogResult.Ok) { return; } FiltersList filtersList = new FiltersList(); IFilter inputFilter = filtersList.GetFilter(dlgOpenImage.FileName); if (inputFilter == null) { MessageBox.Show("Cannot open specified file.", MessageBoxType.Error); return; } try { IMediaImage imageFormat = ImageFormat.Detect(inputFilter); if (imageFormat == null) { MessageBox.Show("Image format not identified.", MessageBoxType.Error); return; } DicConsole.WriteLine("Image format identified by {0} ({1}).", imageFormat.Name, imageFormat.Id); try { if (!imageFormat.Open(inputFilter)) { MessageBox.Show("Unable to open image format", MessageBoxType.Error); DicConsole.ErrorWriteLine("Unable to open image format"); DicConsole.ErrorWriteLine("No error given"); return; } // TODO: SVG Stream logo = ResourceHandler .GetResourceStream($"DiscImageChef.Gui.Assets.Logos.Media.{imageFormat.Info.MediaType}.png"); TreeGridItem imageGridItem = new TreeGridItem { Values = new object[] { logo == null ? null : new Bitmap(logo), $"{Path.GetFileName(dlgOpenImage.FileName)} ({imageFormat.Info.MediaType})", dlgOpenImage.FileName, new pnlImageInfo(dlgOpenImage.FileName, inputFilter, imageFormat), inputFilter, imageFormat } }; List <Partition> partitions = Core.Partitions.GetAll(imageFormat); Core.Partitions.AddSchemesToStats(partitions); bool checkraw = false; List <string> idPlugins; IFilesystem plugin; PluginBase plugins = GetPluginBase.Instance; if (partitions.Count == 0) { DicConsole.DebugWriteLine("Analyze command", "No partitions found"); checkraw = true; } else { DicConsole.WriteLine("{0} partitions found.", partitions.Count); foreach (string scheme in partitions.Select(p => p.Scheme).Distinct().OrderBy(s => s)) { TreeGridItem schemeGridItem = new TreeGridItem { Values = new object[] { nullImage, // TODO: Add icons to partition schemes scheme } }; foreach (Partition partition in partitions .Where(p => p.Scheme == scheme).OrderBy(p => p.Start)) { TreeGridItem partitionGridItem = new TreeGridItem { Values = new object[] { nullImage, // TODO: Add icons to partition schemes $"{partition.Name} ({partition.Type})", null, new pnlPartition(partition) } }; DicConsole.WriteLine("Identifying filesystem on partition"); Core.Filesystems.Identify(imageFormat, out idPlugins, partition); if (idPlugins.Count == 0) { DicConsole.WriteLine("Filesystem not identified"); } else { DicConsole.WriteLine($"Identified by {idPlugins.Count} plugins"); foreach (string pluginName in idPlugins) { if (plugins.PluginsList.TryGetValue(pluginName, out plugin)) { plugin.GetInformation(imageFormat, partition, out string information, null); IReadOnlyFilesystem fsPlugin = plugin as IReadOnlyFilesystem; if (fsPlugin != null) { Errno error = fsPlugin.Mount(imageFormat, partition, null, new Dictionary <string, string>(), null); if (error != Errno.NoError) { fsPlugin = null; } } TreeGridItem filesystemGridItem = new TreeGridItem { Values = new object[] { nullImage, // TODO: Add icons to filesystems plugin.XmlFsType.VolumeName is null ? $"{plugin.XmlFsType.Type}" : $"{plugin.XmlFsType.VolumeName} ({plugin.XmlFsType.Type})", fsPlugin, new pnlFilesystem(plugin.XmlFsType, information) } }; if (fsPlugin != null) { Statistics.AddCommand("ls"); filesystemGridItem.Children.Add(placeholderItem); } Statistics.AddFilesystem(plugin.XmlFsType.Type); partitionGridItem.Children.Add(filesystemGridItem); } } } schemeGridItem.Children.Add(partitionGridItem); } imageGridItem.Children.Add(schemeGridItem); } } if (checkraw) { Partition wholePart = new Partition { Name = "Whole device", Length = imageFormat.Info.Sectors, Size = imageFormat.Info.Sectors * imageFormat.Info.SectorSize }; Core.Filesystems.Identify(imageFormat, out idPlugins, wholePart); if (idPlugins.Count == 0) { DicConsole.WriteLine("Filesystem not identified"); } else { DicConsole.WriteLine($"Identified by {idPlugins.Count} plugins"); foreach (string pluginName in idPlugins) { if (plugins.PluginsList.TryGetValue(pluginName, out plugin)) { plugin.GetInformation(imageFormat, wholePart, out string information, null); IReadOnlyFilesystem fsPlugin = plugin as IReadOnlyFilesystem; if (fsPlugin != null) { Errno error = fsPlugin.Mount(imageFormat, wholePart, null, new Dictionary <string, string>(), null); if (error != Errno.NoError) { fsPlugin = null; } } TreeGridItem filesystemGridItem = new TreeGridItem { Values = new object[] { nullImage, // TODO: Add icons to filesystems plugin.XmlFsType.VolumeName is null ? $"{plugin.XmlFsType.Type}" : $"{plugin.XmlFsType.VolumeName} ({plugin.XmlFsType.Type})", fsPlugin, new pnlFilesystem(plugin.XmlFsType, information) } }; if (fsPlugin != null) { Statistics.AddCommand("ls"); filesystemGridItem.Children.Add(placeholderItem); } Statistics.AddFilesystem(plugin.XmlFsType.Type); imageGridItem.Children.Add(filesystemGridItem); } } } } imagesRoot.Children.Add(imageGridItem); treeImages.ReloadData(); Statistics.AddMediaFormat(imageFormat.Format); Statistics.AddMedia(imageFormat.Info.MediaType, false); Statistics.AddFilter(inputFilter.Name); }
void TestDirectory(IReadOnlyFilesystem fs, string path, Dictionary <string, FileData> children, string testFile) { Errno ret = fs.ReadDir(path, out List <string> contents); Assert.AreEqual(Errno.NoError, ret, $"Unexpected error {ret} when reading directory \"{path}\" of {testFile}."); if (children.Count == 0 && contents.Count == 0) { return; } if (path == "/") { path = ""; } List <string> expectedNotFound = new List <string>(); foreach (KeyValuePair <string, FileData> child in children) { string childPath = $"{path}/{child.Key}"; ret = fs.Stat(childPath, out FileEntryInfo stat); if (ret == Errno.NoSuchFile || !contents.Contains(child.Key)) { expectedNotFound.Add(child.Key); continue; } contents.Remove(child.Key); Assert.AreEqual(Errno.NoError, ret, $"Unexpected error {ret} retrieving stats for \"{childPath}\" in {testFile}"); stat.Should().BeEquivalentTo(child.Value.Info, $"Wrong info for \"{childPath}\" in {testFile}"); byte[] buffer = new byte[0]; if (child.Value.Info.Attributes.HasFlag(FileAttributes.Directory)) { ret = fs.Read(childPath, 0, 1, ref buffer); Assert.AreEqual(Errno.IsDirectory, ret, $"Got wrong data for directory \"{childPath}\" in {testFile}"); Assert.IsNotNull(child.Value.Children, $"Contents for \"{childPath}\" in {testFile} must be defined in unit test declaration!"); if (child.Value.Children != null) { TestDirectory(fs, childPath, child.Value.Children, testFile); } } else if (child.Value.Info.Attributes.HasFlag(FileAttributes.Symlink)) { ret = fs.ReadLink(childPath, out string link); Assert.AreEqual(Errno.NoError, ret, $"Got wrong data for symbolic link \"{childPath}\" in {testFile}"); Assert.AreEqual(child.Value.LinkTarget, link, $"Invalid target for symbolic link \"{childPath}\" in {testFile}"); } else { // This ensure the buffer does not hang for collection TestFile(fs, childPath, child.Value.MD5, child.Value.Info.Length, testFile); } ret = fs.ListXAttr(childPath, out List <string> xattrs); if (ret == Errno.NotSupported) { Assert.IsNull(child.Value.XattrsWithMd5, $"Defined extended attributes for \"{childPath}\" in {testFile} are not supported by filesystem."); continue; } Assert.AreEqual(Errno.NoError, ret, $"Unexpected error {ret} when listing extended attributes for \"{childPath}\" in {testFile}"); if (xattrs.Count > 0) { Assert.IsNotNull(child.Value.XattrsWithMd5, $"Extended attributes for \"{childPath}\" in {testFile} must be defined in unit test declaration!"); } if (xattrs.Count > 0 || child.Value.XattrsWithMd5?.Count > 0) { TestFileXattrs(fs, childPath, child.Value.XattrsWithMd5, testFile); } } Assert.IsEmpty(expectedNotFound, $"Could not find the children of \"{path}\" in {testFile}: {string.Join(" ", expectedNotFound)}"); Assert.IsEmpty(contents, $"Found the following unexpected children of \"{path}\" in {testFile}: {string.Join(" ", contents)}"); }
static void ExtractFilesInDir(string path, IReadOnlyFilesystem fs, string volumeName, string outputDir, bool doXattrs) { if (path.StartsWith('/')) { path = path.Substring(1); } Errno error = fs.ReadDir(path, out List <string> directory); if (error != Errno.NoError) { AaruConsole.ErrorWriteLine("Error {0} reading root directory {0}", error.ToString()); return; } foreach (string entry in directory) { error = fs.Stat(path + "/" + entry, out FileEntryInfo stat); if (error == Errno.NoError) { string outputPath; if (stat.Attributes.HasFlag(FileAttributes.Directory)) { outputPath = Path.Combine(outputDir, fs.XmlFsType.Type, volumeName, path, entry); Directory.CreateDirectory(outputPath); AaruConsole.WriteLine("Created subdirectory at {0}", outputPath); ExtractFilesInDir(path + "/" + entry, fs, volumeName, outputDir, doXattrs); var di = new DirectoryInfo(outputPath); #pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body try { if (stat.CreationTimeUtc.HasValue) { di.CreationTimeUtc = stat.CreationTimeUtc.Value; } } catch { // ignored } try { if (stat.LastWriteTimeUtc.HasValue) { di.LastWriteTimeUtc = stat.LastWriteTimeUtc.Value; } } catch { // ignored } try { if (stat.AccessTimeUtc.HasValue) { di.LastAccessTimeUtc = stat.AccessTimeUtc.Value; } } catch { // ignored } #pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body continue; } FileStream outputFile; if (doXattrs) { error = fs.ListXAttr(path + "/" + entry, out List <string> xattrs); if (error == Errno.NoError) { foreach (string xattr in xattrs) { byte[] xattrBuf = new byte[0]; error = fs.GetXattr(path + "/" + entry, xattr, ref xattrBuf); if (error != Errno.NoError) { continue; } outputPath = Path.Combine(outputDir, fs.XmlFsType.Type, volumeName, path, ".xattrs", xattr); Directory.CreateDirectory(outputPath); outputPath = Path.Combine(outputPath, entry); if (!File.Exists(outputPath)) { outputFile = new FileStream(outputPath, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None); outputFile.Write(xattrBuf, 0, xattrBuf.Length); outputFile.Close(); var fi = new FileInfo(outputPath); #pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body try { if (stat.CreationTimeUtc.HasValue) { fi.CreationTimeUtc = stat.CreationTimeUtc.Value; } } catch { // ignored } try { if (stat.LastWriteTimeUtc.HasValue) { fi.LastWriteTimeUtc = stat.LastWriteTimeUtc.Value; } } catch { // ignored } try { if (stat.AccessTimeUtc.HasValue) { fi.LastAccessTimeUtc = stat.AccessTimeUtc.Value; } } catch { // ignored } #pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body AaruConsole.WriteLine("Written {0} bytes of xattr {1} from file {2} to {3}", xattrBuf.Length, xattr, entry, outputPath); } else { AaruConsole.ErrorWriteLine("Cannot write xattr {0} for {1}, output exists", xattr, entry); } } } } outputPath = Path.Combine(outputDir, fs.XmlFsType.Type, volumeName, path); Directory.CreateDirectory(outputPath); outputPath = Path.Combine(outputPath, entry); if (!File.Exists(outputPath)) { byte[] outBuf = new byte[0]; error = fs.Read(path + "/" + entry, 0, stat.Length, ref outBuf); if (error == Errno.NoError) { outputFile = new FileStream(outputPath, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None); outputFile.Write(outBuf, 0, outBuf.Length); outputFile.Close(); var fi = new FileInfo(outputPath); #pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body try { if (stat.CreationTimeUtc.HasValue) { fi.CreationTimeUtc = stat.CreationTimeUtc.Value; } } catch { // ignored } try { if (stat.LastWriteTimeUtc.HasValue) { fi.LastWriteTimeUtc = stat.LastWriteTimeUtc.Value; } } catch { // ignored } try { if (stat.AccessTimeUtc.HasValue) { fi.LastAccessTimeUtc = stat.AccessTimeUtc.Value; } } catch { // ignored } #pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body AaruConsole.WriteLine("Written {0} bytes of file {1} to {2}", outBuf.Length, entry, outputPath); } else { AaruConsole.ErrorWriteLine("Error {0} reading file {1}", error, entry); } } else { AaruConsole.ErrorWriteLine("Cannot write file {0}, output exists", entry); } } else { AaruConsole.ErrorWriteLine("Error reading file {0}", entry); } } }
internal static void DoLs(LsOptions options) { DicConsole.DebugWriteLine("Ls command", "--debug={0}", options.Debug); DicConsole.DebugWriteLine("Ls command", "--verbose={0}", options.Verbose); DicConsole.DebugWriteLine("Ls command", "--input={0}", options.InputFile); FiltersList filtersList = new FiltersList(); IFilter inputFilter = filtersList.GetFilter(options.InputFile); Dictionary <string, string> parsedOptions = Options.Parse(options.Options); DicConsole.DebugWriteLine("Ls command", "Parsed options:"); foreach (KeyValuePair <string, string> parsedOption in parsedOptions) { DicConsole.DebugWriteLine("Ls command", "{0} = {1}", parsedOption.Key, parsedOption.Value); } parsedOptions.Add("debug", options.Debug.ToString()); if (inputFilter == null) { DicConsole.ErrorWriteLine("Cannot open specified file."); return; } Encoding encoding = null; if (options.EncodingName != null) { try { encoding = Claunia.Encoding.Encoding.GetEncoding(options.EncodingName); if (options.Verbose) { DicConsole.VerboseWriteLine("Using encoding for {0}.", encoding.EncodingName); } } catch (ArgumentException) { DicConsole.ErrorWriteLine("Specified encoding is not supported."); return; } } PluginBase plugins = new PluginBase(); try { IMediaImage imageFormat = ImageFormat.Detect(inputFilter); if (imageFormat == null) { DicConsole.WriteLine("Image format not identified, not proceeding with analysis."); return; } if (options.Verbose) { DicConsole.VerboseWriteLine("Image format identified by {0} ({1}).", imageFormat.Name, imageFormat.Id); } else { DicConsole.WriteLine("Image format identified by {0}.", imageFormat.Name); } try { if (!imageFormat.Open(inputFilter)) { DicConsole.WriteLine("Unable to open image format"); DicConsole.WriteLine("No error given"); return; } DicConsole.DebugWriteLine("Ls command", "Correctly opened image file."); DicConsole.DebugWriteLine("Ls command", "Image without headers is {0} bytes.", imageFormat.Info.ImageSize); DicConsole.DebugWriteLine("Ls command", "Image has {0} sectors.", imageFormat.Info.Sectors); DicConsole.DebugWriteLine("Ls command", "Image identifies disk type as {0}.", imageFormat.Info.MediaType); Core.Statistics.AddMediaFormat(imageFormat.Format); Core.Statistics.AddMedia(imageFormat.Info.MediaType, false); Core.Statistics.AddFilter(inputFilter.Name); } catch (Exception ex) { DicConsole.ErrorWriteLine("Unable to open image format"); DicConsole.ErrorWriteLine("Error: {0}", ex.Message); return; } List <Partition> partitions = Core.Partitions.GetAll(imageFormat); Core.Partitions.AddSchemesToStats(partitions); List <string> idPlugins; IReadOnlyFilesystem plugin; Errno error; if (partitions.Count == 0) { DicConsole.DebugWriteLine("Ls command", "No partitions found"); } else { DicConsole.WriteLine("{0} partitions found.", partitions.Count); for (int i = 0; i < partitions.Count; i++) { DicConsole.WriteLine(); DicConsole.WriteLine("Partition {0}:", partitions[i].Sequence); DicConsole.WriteLine("Identifying filesystem on partition"); Core.Filesystems.Identify(imageFormat, out idPlugins, partitions[i]); if (idPlugins.Count == 0) { DicConsole.WriteLine("Filesystem not identified"); } else if (idPlugins.Count > 1) { DicConsole.WriteLine($"Identified by {idPlugins.Count} plugins"); foreach (string pluginName in idPlugins) { if (plugins.ReadOnlyFilesystems.TryGetValue(pluginName, out plugin)) { DicConsole.WriteLine($"As identified by {plugin.Name}."); IReadOnlyFilesystem fs = (IReadOnlyFilesystem)plugin .GetType() .GetConstructor(Type.EmptyTypes) ?.Invoke(new object[] { }); if (fs == null) { continue; } error = fs.Mount(imageFormat, partitions[i], encoding, parsedOptions); if (error == Errno.NoError) { List <string> rootDir = new List <string>(); error = fs.ReadDir("/", out rootDir); if (error == Errno.NoError) { foreach (string entry in rootDir) { DicConsole.WriteLine("{0}", entry); } } else { DicConsole.ErrorWriteLine("Error {0} reading root directory {0}", error.ToString()); } Core.Statistics.AddFilesystem(fs.XmlFsType.Type); } else { DicConsole.ErrorWriteLine("Unable to mount device, error {0}", error.ToString()); } } } } else { plugins.ReadOnlyFilesystems.TryGetValue(idPlugins[0], out plugin); if (plugin == null) { continue; } DicConsole.WriteLine($"Identified by {plugin.Name}."); IReadOnlyFilesystem fs = (IReadOnlyFilesystem)plugin .GetType().GetConstructor(Type.EmptyTypes) ?.Invoke(new object[] { }); if (fs == null) { continue; } error = fs.Mount(imageFormat, partitions[i], encoding, parsedOptions); if (error == Errno.NoError) { List <string> rootDir = new List <string>(); error = fs.ReadDir("/", out rootDir); if (error == Errno.NoError) { foreach (string entry in rootDir) { DicConsole.WriteLine("{0}", entry); } } else { DicConsole.ErrorWriteLine("Error {0} reading root directory {0}", error.ToString()); } Core.Statistics.AddFilesystem(fs.XmlFsType.Type); } else { DicConsole.ErrorWriteLine("Unable to mount device, error {0}", error.ToString()); } } } } Partition wholePart = new Partition { Name = "Whole device", Length = imageFormat.Info.Sectors, Size = imageFormat.Info.Sectors * imageFormat.Info.SectorSize }; Core.Filesystems.Identify(imageFormat, out idPlugins, wholePart); if (idPlugins.Count == 0) { DicConsole.WriteLine("Filesystem not identified"); } else if (idPlugins.Count > 1) { DicConsole.WriteLine($"Identified by {idPlugins.Count} plugins"); foreach (string pluginName in idPlugins) { if (plugins.ReadOnlyFilesystems.TryGetValue(pluginName, out plugin)) { DicConsole.WriteLine($"As identified by {plugin.Name}."); IReadOnlyFilesystem fs = (IReadOnlyFilesystem)plugin .GetType().GetConstructor(Type.EmptyTypes) ?.Invoke(new object[] { }); if (fs == null) { continue; } error = fs.Mount(imageFormat, wholePart, encoding, parsedOptions); if (error == Errno.NoError) { List <string> rootDir = new List <string>(); error = fs.ReadDir("/", out rootDir); if (error == Errno.NoError) { foreach (string entry in rootDir) { DicConsole.WriteLine("{0}", entry); } } else { DicConsole.ErrorWriteLine("Error {0} reading root directory {0}", error.ToString()); } Core.Statistics.AddFilesystem(fs.XmlFsType.Type); } else { DicConsole.ErrorWriteLine("Unable to mount device, error {0}", error.ToString()); } } } } else { plugins.ReadOnlyFilesystems.TryGetValue(idPlugins[0], out plugin); if (plugin != null) { DicConsole.WriteLine($"Identified by {plugin.Name}."); IReadOnlyFilesystem fs = (IReadOnlyFilesystem)plugin .GetType().GetConstructor(Type.EmptyTypes)?.Invoke(new object[] { }); if (fs != null) { error = fs.Mount(imageFormat, wholePart, encoding, parsedOptions); if (error == Errno.NoError) { List <string> rootDir = new List <string>(); error = fs.ReadDir("/", out rootDir); if (error == Errno.NoError) { foreach (string entry in rootDir) { if (options.Long) { FileEntryInfo stat = new FileEntryInfo(); List <string> xattrs = new List <string>(); error = fs.Stat(entry, out stat); if (error == Errno.NoError) { DicConsole.WriteLine("{0}\t{1}\t{2} bytes\t{3}", stat.CreationTimeUtc, stat.Inode, stat.Length, entry); error = fs.ListXAttr(entry, out xattrs); if (error != Errno.NoError) { continue; } foreach (string xattr in xattrs) { byte[] xattrBuf = new byte[0]; error = fs.GetXattr(entry, xattr, ref xattrBuf); if (error == Errno.NoError) { DicConsole.WriteLine("\t\t{0}\t{1} bytes", xattr, xattrBuf.Length); } } } else { DicConsole.WriteLine("{0}", entry); } } else { DicConsole.WriteLine("{0}", entry); } } } else { DicConsole.ErrorWriteLine("Error {0} reading root directory {0}", error.ToString()); } Core.Statistics.AddFilesystem(fs.XmlFsType.Type); } else { DicConsole.ErrorWriteLine("Unable to mount device, error {0}", error.ToString()); } } } } } catch (Exception ex) { DicConsole.ErrorWriteLine($"Error reading file: {ex.Message}"); DicConsole.DebugWriteLine("Ls command", ex.StackTrace); } Core.Statistics.AddCommand("ls"); }
/// <summary> /// Initializes the plugins lists /// </summary> public PluginBase() { PluginsList = new SortedDictionary <string, IFilesystem>(); ReadOnlyFilesystems = new SortedDictionary <string, IReadOnlyFilesystem>(); PartPluginsList = new SortedDictionary <string, IPartition>(); ImagePluginsList = new SortedDictionary <string, IMediaImage>(); WritableImages = new SortedDictionary <string, IWritableImage>(); Assembly assembly = Assembly.GetAssembly(typeof(IMediaImage)); foreach (Type type in assembly.GetTypes().Where(t => t.GetInterfaces().Contains(typeof(IMediaImage))) .Where(t => t.IsClass)) { try { IMediaImage plugin = (IMediaImage)type.GetConstructor(Type.EmptyTypes)?.Invoke(new object[] { }); RegisterImagePlugin(plugin); } catch (Exception exception) { Console.WriteLine("Exception {0}", exception); } } assembly = Assembly.GetAssembly(typeof(IPartition)); foreach (Type type in assembly.GetTypes().Where(t => t.GetInterfaces().Contains(typeof(IPartition))) .Where(t => t.IsClass)) { try { IPartition plugin = (IPartition)type.GetConstructor(Type.EmptyTypes)?.Invoke(new object[] { }); RegisterPartPlugin(plugin); } catch (Exception exception) { Console.WriteLine("Exception {0}", exception); } } assembly = Assembly.GetAssembly(typeof(IFilesystem)); foreach (Type type in assembly.GetTypes().Where(t => t.GetInterfaces().Contains(typeof(IFilesystem))) .Where(t => t.IsClass)) { try { IFilesystem plugin = (IFilesystem)type.GetConstructor(Type.EmptyTypes)?.Invoke(new object[] { }); RegisterPlugin(plugin); } catch (Exception exception) { Console.WriteLine("Exception {0}", exception); } } assembly = Assembly.GetAssembly(typeof(IReadOnlyFilesystem)); foreach (Type type in assembly.GetTypes().Where(t => t.GetInterfaces().Contains(typeof(IReadOnlyFilesystem))) .Where(t => t.IsClass)) { try { IReadOnlyFilesystem plugin = (IReadOnlyFilesystem)type.GetConstructor(Type.EmptyTypes)?.Invoke(new object[] { }); RegisterReadOnlyFilesystem(plugin); } catch (Exception exception) { Console.WriteLine("Exception {0}", exception); } } assembly = Assembly.GetAssembly(typeof(IWritableImage)); foreach (Type type in assembly.GetTypes().Where(t => t.GetInterfaces().Contains(typeof(IWritableImage))) .Where(t => t.IsClass)) { try { IWritableImage plugin = (IWritableImage)type.GetConstructor(Type.EmptyTypes)?.Invoke(new object[] { }); RegisterWritableMedia(plugin); } catch (Exception exception) { Console.WriteLine("Exception {0}", exception); } } }
static void ExtractFilesInDir(string path, IReadOnlyFilesystem fs, bool doXattrs, VolumeData volumeData) { if (path.StartsWith('/')) { path = path[1..];
public pnlListFiles(IReadOnlyFilesystem filesystem, Dictionary <string, FileEntryInfo> files, string parentPath) { this.filesystem = filesystem; XamlReader.Load(this); entries = new ObservableCollection <EntryForGrid>(); nameColumn = new GridColumn { DataCell = new TextBoxCell { Binding = Binding.Property <EntryForGrid, string>(r => r.Name) }, HeaderText = "Name", Sortable = true }; sizeColumn = new GridColumn { DataCell = new TextBoxCell { Binding = Binding.Property <EntryForGrid, string>(r => $"{r.Stat.Length}") }, HeaderText = "Size", Sortable = true }; createdColumn = new GridColumn { DataCell = new TextBoxCell { Binding = Binding.Property <EntryForGrid, string>(r => r.Stat.CreationTime == default(DateTime) ? "" : $"{r.Stat.CreationTime:G}") }, HeaderText = "Created", Sortable = true }; accessColumn = new GridColumn { DataCell = new TextBoxCell { Binding = Binding.Property <EntryForGrid, string>(r => r.Stat.AccessTime == default(DateTime) ? "" : $"{r.Stat.AccessTime:G}") }, HeaderText = "Last access", Sortable = true }; changedColumn = new GridColumn { DataCell = new TextBoxCell { Binding = Binding.Property <EntryForGrid, string>(r => r.Stat.StatusChangeTime == default(DateTime) ? "" : $"{r.Stat.StatusChangeTime:G}") }, HeaderText = "Changed", Sortable = true }; backupColumn = new GridColumn { DataCell = new TextBoxCell { Binding = Binding.Property <EntryForGrid, string>(r => r.Stat.BackupTime == default(DateTime) ? "" : $"{r.Stat.BackupTime:G}") }, HeaderText = "Last backup", Sortable = true }; writeColumn = new GridColumn { DataCell = new TextBoxCell { Binding = Binding.Property <EntryForGrid, string>(r => r.Stat.LastWriteTime == default(DateTime) ? "" : $"{r.Stat.LastWriteTime:G}") }, HeaderText = "Last write", Sortable = true }; attributesColumn = new GridColumn { DataCell = new TextBoxCell { Binding = Binding.Property <EntryForGrid, string>(r => $"{r.Stat.Attributes}") }, HeaderText = "Attributes", Sortable = true }; gidColumn = new GridColumn { DataCell = new TextBoxCell { Binding = Binding.Property <EntryForGrid, string>(r => $"{r.Stat.GID}") }, HeaderText = "GID", Sortable = true }; uidColumn = new GridColumn { DataCell = new TextBoxCell { Binding = Binding.Property <EntryForGrid, string>(r => $"{r.Stat.UID}") }, HeaderText = "UID", Sortable = true }; inodeColumn = new GridColumn { DataCell = new TextBoxCell { Binding = Binding.Property <EntryForGrid, string>(r => $"{r.Stat.Inode}") }, HeaderText = "Inode", Sortable = true }; linksColumn = new GridColumn { DataCell = new TextBoxCell { Binding = Binding.Property <EntryForGrid, string>(r => $"{r.Stat.Links}") }, HeaderText = "Links", Sortable = true }; modeColumn = new GridColumn { DataCell = new TextBoxCell { Binding = Binding.Property <EntryForGrid, string>(r => $"{r.Stat.Mode}") }, HeaderText = "Mode", Sortable = true }; grdFiles.Columns.Add(nameColumn); grdFiles.Columns.Add(sizeColumn); grdFiles.Columns.Add(createdColumn); grdFiles.Columns.Add(accessColumn); grdFiles.Columns.Add(changedColumn); grdFiles.Columns.Add(backupColumn); grdFiles.Columns.Add(writeColumn); grdFiles.Columns.Add(attributesColumn); grdFiles.Columns.Add(gidColumn); grdFiles.Columns.Add(uidColumn); grdFiles.Columns.Add(inodeColumn); grdFiles.Columns.Add(linksColumn); grdFiles.Columns.Add(modeColumn); grdFiles.AllowColumnReordering = true; grdFiles.AllowDrop = false; grdFiles.AllowMultipleSelection = true; grdFiles.ShowHeader = true; foreach (KeyValuePair <string, FileEntryInfo> file in files) { entries.Add(new EntryForGrid { Name = file.Key, Stat = file.Value, ParentPath = parentPath }); } grdFiles.DataStore = entries; sortedColumn = null; grdFiles.ColumnHeaderClick += OnGrdFilesOnColumnHeaderClick; ascendingSort = true; grdFiles.ContextMenu = new ContextMenu(); saveFilesMenuItem = new ButtonMenuItem { Text = "Extract to...", Enabled = false }; saveFilesMenuItem.Click += OnSaveFilesMenuItemClick; grdFiles.ContextMenu.Items.Add(saveFilesMenuItem); grdFiles.SelectionChanged += OnGrdFilesSelectionChanged; }