public void CreateFileTest() { var fileinfo = new OS9FileInfo("list", 0x4f, OS9FileAttributes.Execute|OS9FileAttributes.Read, null, null, 0, 0, null); byte[] data = { 0x87, 0xcd, 0x00, 0x4f, 0x00, 0x0d, 0x11, 0x81, 0x67, 0x00, 0x12, 0x02, 0x8d, 0x4c, 0x69, 0x73, 0xf4, 0x05, 0x9f, 0x01, 0x86, 0x01, 0x10, 0x3f, 0x84, 0x25, 0x2e, 0x97, 0x00, 0x9f, 0x01, 0x96, 0x00, 0x30, 0x43, 0x10, 0x8e, 0x00, 0xc8, 0x10, 0x3f, 0x8b, 0x25, 0x09, 0x86, 0x01, 0x10, 0x3f, 0x8c, 0x24, 0xec, 0x20, 0x14, 0xc1, 0xd3, 0x26, 0x10, 0x96, 0x00, 0x10, 0x3f, 0x8f, 0x25, 0x09, 0x9e, 0x01, 0xa6, 0x84, 0x81, 0x0d, 0x26, 0xca, 0x5f, 0x10, 0x3f, 0x06, 0x58, 0xbc, 0x12 }; var file = (OS9File) OS9File.CreateFile(fileinfo, data); Assert.IsTrue(file is OS9ModuleFile); var module = (OS9ModuleFile) file; Assert.AreEqual("List", module.ModuleName); Assert.AreEqual(OS9ModuleType.Program, module.ModuleType); Assert.AreEqual(1, module.ModuleLanguage); Assert.AreEqual(8, module.ModuleAttributes); Assert.AreEqual(1, module.ModuleRevision); Assert.AreEqual(0x67, module.HeaderParity); Assert.AreEqual(0x58bc12, module.ModuleCRC); }
/// <summary> /// Reads a file from the system and returns it, without parsing or removing any file headers or other file meta-infromation. /// The file is identified by passing the information from the file description sector. /// </summary> /// <param name="fileinfo">File description sector.</param> /// <returns>The raw data associated with this file.</returns> byte[] ReadFile(OS9FileInfo fileinfo) { var data = new byte[fileinfo.Size]; if (fileinfo.Size == 0) { return(data); } int offset = 0; foreach (var segment in fileinfo.Segments) { int sector = segment.Lsn; for (int i = 0; i < segment.Size; i++) { var sectordata = ReadSector(sector++); int sectorsize = Math.Min(sectordata.Length, data.Length - offset); Array.Copy(sectordata, 0, data, offset, sectorsize); offset += sectorsize; if (offset == fileinfo.Size) { return(data); } } } return(data); }
/// <summary> /// Reads the file descriptor sector for a given file and verified that all sectors in the file segment list (and the file descriptor sector itself) /// are marked as free in the passed cluster allocation map. It then marks the clusters as marked. /// </summary> /// <param name="map">Cluster allocation map.</param> /// <param name="filedesc">Logical sector number of the file descriptor sector for the file.</param> /// <param name="clustersize">Cluster size for this filesystem.</param> /// <exception cref="FilesystemConsistencyException">Thrown if any cluster used for this file is already allocated.</exception> private void VerifyAndUpdateAllocationMap(OS9AllocationMap map, int filedesc, int clustersize) { var fileinfo = OS9FileInfo.Parse(ReadSector(filedesc), null); /* Verify that all sectors used by this file are marked as free in the cluster allocation map. */ if (map.IsAllocated(filedesc / clustersize)) { throw new FilesystemConsistencyException(String.Format("Sector {0} is in use by multiple files", filedesc)); } foreach (var segment in fileinfo.Segments) { int sector = segment.Lsn; for (int i = 0; i < segment.Size; i++) { if (map.IsAllocated(sector / clustersize)) { throw new FilesystemConsistencyException(String.Format("Sector {0} is in use by multiple files", sector)); } sector++; } } /* Mark all sectors as allocated. */ map.SetAllocated(filedesc / clustersize, true); foreach (var segment in fileinfo.Segments) { int sector = segment.Lsn; for (int i = 0; i < segment.Size; i++) { map.SetAllocated(sector / clustersize, true); sector++; } } }
/// <summary> /// Create an OS-9 file by parsing raw file data. /// </summary> /// <param name="fileinfo">File directory information.</param> /// <param name="data">Raw file data.</param> /// <returns></returns> public static OS9File CreateFile(OS9FileInfo fileinfo, byte[] data) { if (OS9ModuleFile.IsModuleFile(data)) { return(new OS9ModuleFile(fileinfo, data)); } return(new OS9DataFile(fileinfo, data)); }
/// <summary> /// Create an OS-9 file by parsing raw file data. /// </summary> /// <param name="fileinfo">File directory information.</param> /// <param name="data">Raw file data.</param> /// <returns></returns> internal static OS9File CreateFile(OS9FileInfo fileinfo, byte[] data) { if (OS9ModuleFile.IsModuleFile(data)) { return new OS9ModuleFile(fileinfo, data); } return new OS9DataFile(fileinfo, data); }
/// <summary> /// Checks the filesystem consistency. /// </summary> /// <exception cref="FilesystemConsistencyException">Thrown if the filesystem is not consistent in a manner that makes write operations unsafe.</exception> public void Check() { if (IsDisposed) { throw new ObjectDisposedException(GetType().FullName); } ReadDiskHeader(); var allocmap = new OS9AllocationMap(DiskInfo.AllocationMapSize); // mirror the allocation map allocmap.SetAllocated(IdentificationSector / DiskInfo.ClusterSize, true); allocmap.SetAllocated(AllocationMapSector / DiskInfo.ClusterSize, true); var dirs = new Queue <int>(); // LSN of all directories found while traversing the file structure dirs.Enqueue(DiskInfo.RootDirectory); VerifyAndUpdateAllocationMap(allocmap, DiskInfo.RootDirectory, DiskInfo.ClusterSize); /* Traverse the directory hierarchy and update the cluster allocation map for all files encountered. */ while (dirs.Count > 0) { int lsn = dirs.Dequeue(); var files = ReadDirectory(lsn); foreach (var file in files) { if (file.IsValid && !String.Equals(file.Filename, ".") && !String.Equals(file.Filename, "..")) { VerifyAndUpdateAllocationMap(allocmap, file.Sector, DiskInfo.ClusterSize); var fileinfo = OS9FileInfo.Parse(ReadSector(file.Sector), file.Filename); if (fileinfo.IsDirectory) { dirs.Enqueue(file.Sector); } } } } /* Finally, verify that all clusters used by files found are actually marked as allocated in the disk cluster allocation map. */ for (int i = 0; i < AllocationMap.AllocationMapSize; i++) { if (allocmap.IsAllocated(i) && !AllocationMap.IsAllocated(i)) { throw new FilesystemConsistencyException(String.Format("Cluster {0} is in use by a file but not marked as allocated", i)); } } }
/// <summary> /// Create a OS9 module file object. /// </summary> /// <param name="fileinfo">Directory information for this file, or <value>null</value> if no directory information is available.</param> /// <param name="filedata">Raw file data, including headers and payload.</param> /// <param name="validate">If cleared, an invalid module file can be created, i.e. header and file checksum values are not validated.</param> internal OS9ModuleFile(OS9FileInfo fileinfo, byte[] filedata, bool validate = true) : base(fileinfo) { if (filedata == null) { throw new ArgumentNullException("filedata"); } var filename = fileinfo == null ? null : fileinfo.Name; if (filedata.Length < StandardModuleHeaderSize) { throw new InvalidFileException(filename, String.Format("A module file must contain a {0} byte header. This file is only {1} bytes long.", StandardModuleHeaderSize, filedata.Length)); } if (filedata[0] != 0x87 || filedata[1] != 0xcd) { throw new InvalidFileException(filename, "Module file header does not contain valid sync sequence."); } data = (byte[])filedata.Clone(); if (ModuleSize > data.Length) { throw new InvalidFileException(filename, String.Format("The module header specifies a module size of {0} bytes but the file is only {1} bytes.", ModuleSize, data.Length)); } int nameoffset = (data[4] << 8) | data[5]; if (nameoffset > data.Length) { throw new InvalidFileException(filename, "Module filename offset is outside the file data."); } ModuleName = OS9Utils.ParseString(data, nameoffset); if (validate) { if (HeaderParity != CalculateHeaderParity(data, StandardModuleHeaderSize - 1)) { throw new InvalidFileException(filename, "Header parity error"); } if (ModuleCRC != CalculateModuleCRC(data, ModuleSize - 3)) { throw new InvalidFileException(filename, "Invalid module CRC"); } } }
/// <summary> /// Reads a directory starting at a given disk sector. /// </summary> /// <param name="sector">First sector of the directory file.</param> /// <returns>A list of (filename, sector) tuples for the entries in the directory.</returns> internal List <OS9DirectoryEntry> ReadDirectory(int sector) { var header = OS9FileInfo.Parse(ReadSector(sector), null); if (!header.IsDirectory) { throw new InvalidFileException(); } var dir = new List <OS9DirectoryEntry>(); var raw = ReadFile(header); int direntries = raw.Length / OS9DirectoryEntry.RawEntrySize; for (int i = 0; i < direntries; i++) { dir.Add(new OS9DirectoryEntry(raw, i)); } return(dir); }
/// <summary> /// Returns meta-information for a named file. /// </summary> /// <param name="filename">Name of file</param> /// <returns>File meta-information object.</returns> public IFileInfo GetFileInfo(string filename) { if (IsDisposed) { throw new ObjectDisposedException(GetType().FullName); } if (filename == null) { throw new ArgumentNullException(); } int sector = FindPath(filename); if (sector == -1) { throw new FileNotFoundException(filename); } var raw = ReadSector(sector); return(OS9FileInfo.Parse(raw, filename)); }
internal OS9File(OS9FileInfo fileinfo) { FileInfo = fileinfo; }
/// <summary> /// Reads a file from the system and returns it, without parsing or removing any file headers or other file meta-infromation. /// The file is identified by passing the information from the file description sector. /// </summary> /// <param name="fileinfo">File description sector.</param> /// <returns>The raw data associated with this file.</returns> byte[] ReadFile(OS9FileInfo fileinfo) { var data = new byte[fileinfo.Size]; if (fileinfo.Size == 0) return data; int offset = 0; foreach (var segment in fileinfo.Segments) { int sector = segment.Lsn; for (int i=0; i<segment.Size; i++) { var sectordata = ReadSector(sector++); int sectorsize = Math.Min(sectordata.Length, data.Length - offset); Array.Copy(sectordata, 0, data, offset, sectorsize); offset += sectorsize; if (offset == fileinfo.Size) return data; } } return data; }
internal OS9DataFile(OS9FileInfo fileinfo, byte[] data) : base(fileinfo) { this.data = data; }
/// <summary> /// Create a OS9 module file object. /// </summary> /// <param name="fileinfo">Directory information for this file, or <value>null</value> if no directory information is available.</param> /// <param name="filedata">Raw file data, including headers and payload.</param> /// <param name="validate">If cleared, an invalid module file can be created, i.e. header and file checksum values are not validated.</param> internal OS9ModuleFile(OS9FileInfo fileinfo, byte[] filedata, bool validate=true) : base(fileinfo) { if (filedata == null) throw new ArgumentNullException("filedata"); var filename = fileinfo == null ? null : fileinfo.Name; if (filedata.Length < StandardModuleHeaderSize) throw new InvalidFileException(filename, String.Format("A module file must contain a {0} byte header. This file is only {1} bytes long.", StandardModuleHeaderSize, filedata.Length)); if (filedata[0] != 0x87 || filedata[1] != 0xcd) throw new InvalidFileException(filename, "Module file header does not contain valid sync sequence."); data = (byte[]) filedata.Clone(); if (ModuleSize > data.Length) throw new InvalidFileException(filename, String.Format("The module header specifies a module size of {0} bytes but the file is only {1} bytes.", ModuleSize, data.Length)); int nameoffset = (data[4] << 8) | data[5]; if (nameoffset > data.Length) throw new InvalidFileException(filename, "Module filename offset is outside the file data."); ModuleName = OS9Utils.ParseString(data, nameoffset); if (validate) { if (HeaderParity != CalculateHeaderParity(data, StandardModuleHeaderSize-1)) throw new InvalidFileException(filename, "Header parity error"); if (ModuleCRC != CalculateModuleCRC(data, ModuleSize-3)) throw new InvalidFileException(filename, "Invalid module CRC"); } }