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); }
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:##,#} {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 { if (entry.Value != null && entry.Value.Attributes.HasFlag(FileAttributes.Directory)) { AaruConsole.WriteLine("{0}/", entry.Key); } else { AaruConsole.WriteLine("{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); } }
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 = GetPluginBase.Instance; 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) { error = fs.ReadDir("/", out List <string> 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) { error = fs.ReadDir("/", out List <string> 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) { error = fs.ReadDir("/", out List <string> 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) { error = fs.ReadDir("/", out List <string> rootDir); if (error == Errno.NoError) { foreach (string entry in rootDir) { if (options.Long) { error = fs.Stat(entry, out FileEntryInfo 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 List <string> 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"); }
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) { DicConsole.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)) { Directory.CreateDirectory(Path.Combine(outputDir, fs.XmlFsType.Type, volumeName)); outputPath = Path.Combine(outputDir, fs.XmlFsType.Type, volumeName, path, entry); Directory.CreateDirectory(outputPath); DicConsole.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; } Directory.CreateDirectory(Path.Combine(outputDir, fs.XmlFsType.Type, volumeName, ".xattrs", xattr)); outputPath = Path.Combine(outputDir, fs.XmlFsType.Type, volumeName, ".xattrs", xattr, path, 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 DicConsole.WriteLine("Written {0} bytes of xattr {1} from file {2} to {3}", xattrBuf.Length, xattr, entry, outputPath); } else { DicConsole.ErrorWriteLine("Cannot write xattr {0} for {1}, output exists", xattr, entry); } } } } Directory.CreateDirectory(Path.Combine(outputDir, fs.XmlFsType.Type, volumeName)); outputPath = Path.Combine(outputDir, fs.XmlFsType.Type, volumeName, path, 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 DicConsole.WriteLine("Written {0} bytes of file {1} to {2}", outBuf.Length, entry, outputPath); } else { DicConsole.ErrorWriteLine("Error {0} reading file {1}", error, entry); } } else { DicConsole.ErrorWriteLine("Cannot write file {0}, output exists", entry); } } else { DicConsole.ErrorWriteLine("Error reading file {0}", entry); } } }