/// <summary>Reads the contents of a symbolic link</summary> /// <param name="path">Path to the symbolic link</param> /// <returns>Contents of the symbolic link</returns> internal static string ReadLink(string path) { IntPtr buf = Marshal.AllocHGlobal(4096); int resultSize; if (DetectOS.Is64Bit) { long result64 = Extern.readlink64(path, buf, 4096); if (result64 <= 0) { return(null); } resultSize = (int)result64; } else { int result = Extern.readlink(path, buf, 4096); if (result <= 0) { return(null); } resultSize = result; } byte[] resultString = new byte[resultSize]; Marshal.Copy(buf, resultString, 0, resultSize); Marshal.FreeHGlobal(buf); return(Encoding.ASCII.GetString(resultString)); }
internal static int ReOpen(string devicePath, int fd, out object newFd) { newFd = -1; int ret = Extern.close(fd); if (ret < 0) { return(Marshal.GetLastWin32Error()); } newFd = Extern.open(devicePath, FileFlags.ReadWrite | FileFlags.NonBlocking | FileFlags.CreateNew); if ((int)newFd >= 0) { return(0); } int error = Marshal.GetLastWin32Error(); if (error != 13 && error != 30) { return(Marshal.GetLastWin32Error()); } newFd = Extern.open(devicePath, FileFlags.Readonly | FileFlags.NonBlocking); return((int)newFd < 0 ? Marshal.GetLastWin32Error() : 0); }
/// <summary>Sends a MMC/SD command</summary> /// <returns>The result of the command.</returns> /// <param name="fd">File handle</param> /// <param name="command">MMC/SD opcode</param> /// <param name="buffer">Buffer for MMC/SD command response</param> /// <param name="timeout">Timeout in seconds</param> /// <param name="duration">Time it took to execute the command in milliseconds</param> /// <param name="sense"><c>True</c> if MMC/SD returned non-OK status</param> /// <param name="write"><c>True</c> if data is sent from host to card</param> /// <param name="isApplication"><c>True</c> if command should be preceded with CMD55</param> /// <param name="flags">Flags indicating kind and place of response</param> /// <param name="blocks">How many blocks to transfer</param> /// <param name="argument">Command argument</param> /// <param name="response">Response registers</param> /// <param name="blockSize">Size of block in bytes</param> internal static int SendMmcCommand(int fd, MmcCommands command, bool write, bool isApplication, MmcFlags flags, uint argument, uint blockSize, uint blocks, ref byte[] buffer, out uint[] response, out double duration, out bool sense, uint timeout = 0) { response = null; duration = 0; sense = false; if (buffer == null) { return(-1); } var ioCmd = new MmcIocCmd(); IntPtr bufPtr = Marshal.AllocHGlobal(buffer.Length); ioCmd.write_flag = write; ioCmd.is_ascmd = isApplication; ioCmd.opcode = (uint)command; ioCmd.arg = argument; ioCmd.flags = flags; ioCmd.blksz = blockSize; ioCmd.blocks = blocks; if (timeout > 0) { ioCmd.data_timeout_ns = timeout * 1000000000; ioCmd.cmd_timeout_ms = timeout * 1000; } ioCmd.data_ptr = (ulong)bufPtr; Marshal.Copy(buffer, 0, bufPtr, buffer.Length); DateTime start = DateTime.UtcNow; int error = Extern.ioctlMmc(fd, LinuxIoctl.MmcIocCmd, ref ioCmd); DateTime end = DateTime.UtcNow; sense |= error < 0; if (error < 0) { error = Marshal.GetLastWin32Error(); } Marshal.Copy(bufPtr, buffer, 0, buffer.Length); response = ioCmd.response; duration = (end - start).TotalMilliseconds; Marshal.FreeHGlobal(bufPtr); return(error); }
/// <summary>Sends a SCSI command</summary> /// <returns>0 if no error occurred, otherwise, errno</returns> /// <param name="fd">File handle</param> /// <param name="cdb">SCSI CDB</param> /// <param name="buffer">Buffer for SCSI command response</param> /// <param name="senseBuffer">Buffer with the SCSI sense</param> /// <param name="timeout">Timeout in seconds</param> /// <param name="direction">SCSI command transfer direction</param> /// <param name="duration">Time it took to execute the command in milliseconds</param> /// <param name="sense"> /// <c>True</c> if SCSI error returned non-OK status and <paramref name="senseBuffer" /> contains SCSI /// sense /// </param> internal static int SendScsiCommand(int fd, byte[] cdb, ref byte[] buffer, out byte[] senseBuffer, uint timeout, ScsiIoctlDirection direction, out double duration, out bool sense) { senseBuffer = null; duration = 0; sense = false; if (buffer == null) { return(-1); } var ioHdr = new SgIoHdrT(); senseBuffer = new byte[32]; ioHdr.interface_id = 'S'; ioHdr.cmd_len = (byte)cdb.Length; ioHdr.mx_sb_len = (byte)senseBuffer.Length; ioHdr.dxfer_direction = direction; ioHdr.dxfer_len = (uint)buffer.Length; ioHdr.dxferp = Marshal.AllocHGlobal(buffer.Length); ioHdr.cmdp = Marshal.AllocHGlobal(cdb.Length); ioHdr.sbp = Marshal.AllocHGlobal(senseBuffer.Length); ioHdr.timeout = timeout * 1000; ioHdr.flags = (uint)SgFlags.DirectIo; Marshal.Copy(buffer, 0, ioHdr.dxferp, buffer.Length); Marshal.Copy(cdb, 0, ioHdr.cmdp, cdb.Length); Marshal.Copy(senseBuffer, 0, ioHdr.sbp, senseBuffer.Length); DateTime start = DateTime.UtcNow; int error = Extern.ioctlSg(fd, LinuxIoctl.SgIo, ref ioHdr); DateTime end = DateTime.UtcNow; if (error < 0) { error = Marshal.GetLastWin32Error(); } Marshal.Copy(ioHdr.dxferp, buffer, 0, buffer.Length); Marshal.Copy(ioHdr.cmdp, cdb, 0, cdb.Length); Marshal.Copy(ioHdr.sbp, senseBuffer, 0, senseBuffer.Length); sense |= (ioHdr.info & SgInfo.OkMask) != SgInfo.Ok; duration = ioHdr.duration > 0 ? ioHdr.duration : (end - start).TotalMilliseconds; Marshal.FreeHGlobal(ioHdr.dxferp); Marshal.FreeHGlobal(ioHdr.cmdp); Marshal.FreeHGlobal(ioHdr.sbp); return(error); }
internal static int BufferedOsRead(int fd, out byte[] buffer, long offset, uint length, out double duration) { buffer = new byte[length]; DateTime start = DateTime.Now; long sense = Extern.lseek(fd, offset, SeekWhence.Begin); DateTime end = DateTime.Now; if (sense < 0) { duration = (end - start).TotalMilliseconds; return(Marshal.GetLastWin32Error()); } sense = DetectOS.Is64Bit ? Extern.read64(fd, buffer, length) : Extern.read(fd, buffer, (int)length); end = DateTime.Now; duration = (end - start).TotalMilliseconds; return(sense < 0 ? Marshal.GetLastWin32Error() : 0); }
internal static int SendMultipleMmcCommands(int fd, Device.MmcSingleCommand[] commands, out double duration, out bool sense, uint timeout = 0) { duration = 0; sense = false; int off = 0; // Create array for buffers IntPtr[] bufferPointers = new IntPtr[commands.Length]; // Allocate memory for the array for commands byte[] ioMultiCmd = new byte[sizeof(ulong) + (Marshal.SizeOf <MmcIocCmd>() * commands.Length)]; // First value of array is uint64 with count of commands Array.Copy(BitConverter.GetBytes((ulong)commands.Length), 0, ioMultiCmd, 0, sizeof(ulong)); off = sizeof(ulong); for (int i = 0; i < commands.Length; i++) { // Create command var ioCmd = new MmcIocCmd(); // Allocate buffer bufferPointers[i] = Marshal.AllocHGlobal(commands[i].buffer.Length); // Define command ioCmd.write_flag = commands[i].write; ioCmd.is_ascmd = commands[i].isApplication; ioCmd.opcode = (uint)commands[i].command; ioCmd.arg = commands[i].argument; ioCmd.flags = commands[i].flags; ioCmd.blksz = commands[i].blockSize; ioCmd.blocks = commands[i].blocks; if (timeout > 0) { ioCmd.data_timeout_ns = timeout * 1000000000; ioCmd.cmd_timeout_ms = timeout * 1000; } ioCmd.data_ptr = (ulong)bufferPointers[i]; // Copy buffer to unmanaged space Marshal.Copy(commands[i].buffer, 0, bufferPointers[i], commands[i].buffer.Length); // Copy command to array byte[] ioCmdBytes = Helpers.Marshal.StructureToByteArrayLittleEndian(ioCmd); Array.Copy(ioCmdBytes, 0, ioMultiCmd, off, Marshal.SizeOf <MmcIocCmd>()); // Advance pointer off += Marshal.SizeOf <MmcIocCmd>(); } // Allocate unmanaged memory for array of commands IntPtr ioMultiCmdPtr = Marshal.AllocHGlobal(ioMultiCmd.Length); // Copy array of commands to unmanaged memory Marshal.Copy(ioMultiCmd, 0, ioMultiCmdPtr, ioMultiCmd.Length); // Send command DateTime start = DateTime.UtcNow; int error = Extern.ioctlMmcMulti(fd, LinuxIoctl.MmcIocMultiCmd, ioMultiCmdPtr); DateTime end = DateTime.UtcNow; sense |= error < 0; if (error < 0) { error = Marshal.GetLastWin32Error(); } duration = (end - start).TotalMilliseconds; off = sizeof(ulong); // Copy array from unmanaged memory Marshal.Copy(ioMultiCmdPtr, ioMultiCmd, 0, ioMultiCmd.Length); // TODO: Use real pointers this is too slow for (int i = 0; i < commands.Length; i++) { byte[] tmp = new byte[Marshal.SizeOf <MmcIocCmd>()]; // Copy command to managed space Array.Copy(ioMultiCmd, off, tmp, 0, tmp.Length); MmcIocCmd command = Helpers.Marshal.ByteArrayToStructureLittleEndian <MmcIocCmd>(tmp); // Copy response commands[i].response = command.response; // Copy buffer to managed space Marshal.Copy(bufferPointers[i], commands[i].buffer, 0, commands[i].buffer.Length); // Free buffer Marshal.FreeHGlobal(bufferPointers[i]); // Advance pointer off += Marshal.SizeOf <MmcIocCmd>(); } // Free unmanaged memory Marshal.FreeHGlobal(ioMultiCmdPtr); return(error); }
/// <summary>Gets a list of all known storage devices on Linux</summary> /// <returns>List of devices</returns> internal static DeviceInfo[] GetList() { string[] sysdevs = Directory.GetFileSystemEntries(PATH_SYS_DEVBLOCK, "*", SearchOption.TopDirectoryOnly); DeviceInfo[] devices = new DeviceInfo[sysdevs.Length]; bool hasUdev; IntPtr udev = IntPtr.Zero; try { udev = Extern.udev_new(); hasUdev = udev != IntPtr.Zero; } catch { hasUdev = false; } for (int i = 0; i < sysdevs.Length; i++) { devices[i] = new DeviceInfo { Path = "/dev/" + Path.GetFileName(sysdevs[i]) }; if (hasUdev) { IntPtr udevDev = Extern.udev_device_new_from_subsystem_sysname(udev, "block", Path.GetFileName(sysdevs[i])); devices[i].Vendor = Extern.udev_device_get_property_value(udevDev, "ID_VENDOR"); devices[i].Model = Extern.udev_device_get_property_value(udevDev, "ID_MODEL"); if (!string.IsNullOrEmpty(devices[i].Model)) { devices[i].Model = devices[i].Model.Replace('_', ' '); } devices[i].Serial = Extern.udev_device_get_property_value(udevDev, "ID_SCSI_SERIAL"); if (string.IsNullOrEmpty(devices[i].Serial)) { devices[i].Serial = Extern.udev_device_get_property_value(udevDev, "ID_SERIAL_SHORT"); } devices[i].Bus = Extern.udev_device_get_property_value(udevDev, "ID_BUS"); } StreamReader sr; if (File.Exists(Path.Combine(sysdevs[i], "device/vendor")) && string.IsNullOrEmpty(devices[i].Vendor)) { sr = new StreamReader(Path.Combine(sysdevs[i], "device/vendor"), Encoding.ASCII); devices[i].Vendor = sr.ReadLine()?.Trim(); } else if (devices[i].Path.StartsWith("/dev/loop", StringComparison.CurrentCulture)) { devices[i].Vendor = "Linux"; } if (File.Exists(Path.Combine(sysdevs[i], "device/model")) && (string.IsNullOrEmpty(devices[i].Model) || devices[i].Bus == "ata")) { sr = new StreamReader(Path.Combine(sysdevs[i], "device/model"), Encoding.ASCII); devices[i].Model = sr.ReadLine()?.Trim(); } else if (devices[i].Path.StartsWith("/dev/loop", StringComparison.CurrentCulture)) { devices[i].Model = "Linux"; } if (File.Exists(Path.Combine(sysdevs[i], "device/serial")) && string.IsNullOrEmpty(devices[i].Serial)) { sr = new StreamReader(Path.Combine(sysdevs[i], "device/serial"), Encoding.ASCII); devices[i].Serial = sr.ReadLine()?.Trim(); } if (string.IsNullOrEmpty(devices[i].Vendor) || devices[i].Vendor == "ATA") { if (devices[i].Model != null) { string[] pieces = devices[i].Model.Split(' '); if (pieces.Length > 1) { devices[i].Vendor = pieces[0]; devices[i].Model = devices[i].Model.Substring(pieces[0].Length + 1); } } } // TODO: Get better device type from sysfs paths if (string.IsNullOrEmpty(devices[i].Bus)) { if (devices[i].Path.StartsWith("/dev/loop", StringComparison.CurrentCulture)) { devices[i].Bus = "loop"; } else if (devices[i].Path.StartsWith("/dev/nvme", StringComparison.CurrentCulture)) { devices[i].Bus = "NVMe"; } else if (devices[i].Path.StartsWith("/dev/mmc", StringComparison.CurrentCulture)) { devices[i].Bus = "MMC/SD"; } } else { devices[i].Bus = devices[i].Bus.ToUpper(); } switch (devices[i].Bus) { case "ATA": case "ATAPI": case "SCSI": case "USB": case "PCMCIA": case "FireWire": case "MMC/SD": devices[i].Supported = true; break; } } return(devices); }