Example #1
0
    /// <summary>
    /// Create a sparse file.
    /// </summary>
    /// <param name="fileName">The name of the sparse file</param>
    /// <returns></returns>
    public static FileStream Create(string fileName)
    {
        // Create a normal file
        FileStream fs = new FileStream(fileName, FileMode.Create,
                                       FileAccess.Write);

        // Use the DeviceIoControl function with the FSCTL_SET_SPARSE control
        // code to mark the file as sparse. If you don't mark the file as
        // sparse, the FSCTL_SET_ZERO_DATA control code will actually write
        // zero bytes to the file instead of marking the region as sparse
        // zero area.
        int bytesReturned             = 0;
        NativeOverlapped lpOverlapped = new NativeOverlapped();

        NativeMethod.DeviceIoControl(fs.SafeFileHandle,
                                     EIoControlCode.FsctlSetSparse, IntPtr.Zero, 0, IntPtr.Zero, 0,
                                     ref bytesReturned, ref lpOverlapped);

        return(fs);
    }
Example #2
0
    /// <summary>
    /// Converting a file region to A sparse zero area.
    /// </summary>
    /// <param name="hSparseFile">Safe handle of the sparse file</param>
    /// <param name="start">Start address of the sparse zero area</param>
    /// <param name="size">
    /// Size of the sparse zero block. The minimum sparse size is 64KB.
    /// </param>
    /// <remarks>
    /// Note that SetSparseRange does not perform actual file I/O, and unlike
    /// the WriteFile function, it does not move the current file I/O pointer
    /// or sets the end-of-file pointer. That is, if you want to place a
    /// sparse zero block in the end of the file, you must move the file
    /// pointer accordingly using the FileStream.Seek function, otherwise
    /// DeviceIoControl will have no effect.
    /// </remarks>
    public static void SetSparseRange(SafeFileHandle hSparseFile, uint start,
                                      uint size)
    {
        // Specify the starting and the ending address (not the size) of the
        // sparse zero block
        FILE_ZERO_DATA_INFORMATION fzdi;

        fzdi.FileOffset      = start;
        fzdi.BeyondFinalZero = start + size;
        GCHandle hfzdi = GCHandle.Alloc(fzdi, GCHandleType.Pinned);

        // Mark the range as sparse zero block
        int bytesReturned             = 0;
        NativeOverlapped lpOverlapped = new NativeOverlapped();

        NativeMethod.DeviceIoControl(hSparseFile,
                                     EIoControlCode.FsctlSetZeroData, hfzdi.AddrOfPinnedObject(),
                                     Marshal.SizeOf(fzdi), IntPtr.Zero, 0, ref bytesReturned,
                                     ref lpOverlapped);

        hfzdi.Free();
    }
Example #3
0
        /// <summary>
        /// Get disk information by P/Invoke.
        /// </summary>
        /// <param name="physicalDrive">Index number of physical drive</param>
        /// <returns>Disk information</returns>
        internal static DiskInfo GetDiskInfo(int physicalDrive)
        {
            var info = new DiskInfo {
                PhysicalDrive = physicalDrive
            };

            SafeFileHandle hFile = null;

            try
            {
                hFile = NativeMethod.CreateFile(
                    String.Format(@"\\.\PhysicalDrive{0}", physicalDrive),
                    NativeMethod.GENERIC_READ | NativeMethod.GENERIC_WRITE,                     // Administrative privilege is required. GENERIC_WRITE is for IOCTL_ATA_PASS_THROUGH.
                    NativeMethod.FILE_SHARE_READ | NativeMethod.FILE_SHARE_WRITE,
                    IntPtr.Zero,
                    NativeMethod.OPEN_EXISTING,
                    NativeMethod.FILE_ATTRIBUTE_NORMAL,
                    IntPtr.Zero);
                if (hFile == null || hFile.IsInvalid)
                {
                    // This is normal when this application is not run by administrator.
                    throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to get handle to disk.");
                }

                // ---------------------------------
                // Use IOCTL_STORAGE_QUERY_PROPERTY.
                // ---------------------------------
                var storageQuery = new NativeMethod.STORAGE_PROPERTY_QUERY();
                storageQuery.PropertyId = NativeMethod.StorageDeviceProperty;
                storageQuery.QueryType  = NativeMethod.PropertyStandardQuery;

                NativeMethod.STORAGE_DEVICE_DESCRIPTOR storageDescriptor;
                uint bytesReturned1;

                var result1 = NativeMethod.DeviceIoControl(
                    hFile,
                    NativeMethod.IOCTL_STORAGE_QUERY_PROPERTY,
                    ref storageQuery,
                    (uint)Marshal.SizeOf(typeof(NativeMethod.STORAGE_PROPERTY_QUERY)),
                    out storageDescriptor,
                    (uint)Marshal.SizeOf(typeof(NativeMethod.STORAGE_DEVICE_DESCRIPTOR)),
                    out bytesReturned1,
                    IntPtr.Zero);
                if (result1 == false)
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to get disk information.");
                }

                // Convert to byte array.
                var size  = Marshal.SizeOf(storageDescriptor);
                var ptr   = Marshal.AllocHGlobal(size);
                var bytes = new byte[size];

                Marshal.StructureToPtr(storageDescriptor, ptr, true);
                Marshal.Copy(ptr, bytes, 0, size);
                Marshal.FreeHGlobal(ptr);

                // Set values.
                info.Vendor      = ConvertBytesToString(bytes, (int)storageDescriptor.VendorIdOffset).Trim();
                info.Product     = ConvertBytesToString(bytes, (int)storageDescriptor.ProductIdOffset).Trim();
                info.IsRemovable = storageDescriptor.RemovableMedia;
                info.BusType     = ConvertBusTypeToString(storageDescriptor.BusType);

                // -------------------------------
                // Use IOCTL_DISK_GET_LENGTH_INFO.
                // -------------------------------
                long diskSize;
                uint bytesReturned2;

                var result2 = NativeMethod.DeviceIoControl(
                    hFile,
                    NativeMethod.IOCTL_DISK_GET_LENGTH_INFO,
                    IntPtr.Zero,
                    0,
                    out diskSize,
                    (uint)Marshal.SizeOf(typeof(long)),
                    out bytesReturned2,
                    IntPtr.Zero);
                if (result2 == false)
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error(), "Failed to get disk size.");
                }

                // Set value.
                info.SizePInvoke = diskSize;

                // ---------------------------
                // Use IOCTL_ATA_PASS_THROUGH.
                // ---------------------------
                var ataQuery = new NativeMethod.ATAIdentifyDeviceQuery();
                ataQuery.data = new ushort[256];

                ataQuery.header.Length             = (ushort)Marshal.SizeOf(typeof(NativeMethod.ATA_PASS_THROUGH_EX));
                ataQuery.header.AtaFlags           = NativeMethod.ATA_FLAGS.ATA_FLAGS_DATA_IN;
                ataQuery.header.DataTransferLength = (uint)ataQuery.data.Length * 2; // Size of "data" in bytes
                ataQuery.header.TimeOutValue       = 3;                              // Sec
                ataQuery.header.DataBufferOffset   = Marshal.OffsetOf(typeof(NativeMethod.ATAIdentifyDeviceQuery), "data");
                ataQuery.header.PreviousTaskFile   = new byte[8];
                ataQuery.header.CurrentTaskFile    = new byte[8];
                ataQuery.header.CurrentTaskFile[6] = 0xec;                 // ATA IDENTIFY DEVICE

                uint bytesReturned3;

                var result3 = NativeMethod.DeviceIoControl(
                    hFile,
                    NativeMethod.IOCTL_ATA_PASS_THROUGH,
                    ref ataQuery,
                    (uint)Marshal.SizeOf(typeof(NativeMethod.ATAIdentifyDeviceQuery)),
                    ref ataQuery,
                    (uint)Marshal.SizeOf(typeof(NativeMethod.ATAIdentifyDeviceQuery)),
                    out bytesReturned3,
                    IntPtr.Zero);
                if (result3)
                {
                    const int index = 217;                     // Word index of nominal media rotation rate (1 means non-rotating media.)
                    info.NominalMediaRotationRate = ataQuery.data[index];
                }
                else
                {
                    // None. It is normal that IOCTL_ATA_PASS_THROUGH fails when used to external or removable media.
                }
            }
            catch (Win32Exception ex)
            {
                Debug.WriteLine("{0} (Code: {1}).", ex.Message.Substring(0, ex.Message.Length - 1), ex.ErrorCode);
                throw;
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
                throw;
            }
            finally
            {
                if (hFile != null)
                {
                    // CloseHandle is inappropriate to close SafeFileHandle.
                    // Dispose method is not necessary because Close method will call it internally.
                    hFile.Close();
                }
            }

            return(info);
        }
Example #4
0
    /// <summary>
    /// Query the sparse file layout.
    /// </summary>
    /// <param name="fileName">File name</param>
    /// <returns></returns>
    public static bool GetSparseRanges(string fileName)
    {
        // Open the file for read
        using (FileStream fs = new FileStream(fileName, FileMode.Open,
                                              FileAccess.Read))
        {
            // Set the range to be examined (the whole file)
            FILE_ALLOCATED_RANGE_BUFFER queryRange;
            queryRange.FileOffset = 0;
            queryRange.Length     = fs.Length;
            GCHandle hQueryRange = GCHandle.Alloc(queryRange, GCHandleType.Pinned);

            // Allocated areas info
            // DeviceIoControl will return as many results as fit into this
            // buffer and will report error code ERROR_MORE_DATA as long as
            // more data is available
            FILE_ALLOCATED_RANGE_BUFFER[] allocRanges = new
                                                        FILE_ALLOCATED_RANGE_BUFFER[1024];
            GCHandle hAllocRanges = GCHandle.Alloc(allocRanges, GCHandleType.Pinned);

            int  nbytes    = 0;
            bool bFinished = false;
            Console.WriteLine("\nAllocated ranges in the file:");
            do
            {
                NativeOverlapped lpOverlapped = new NativeOverlapped();
                bFinished = NativeMethod.DeviceIoControl(fs.SafeFileHandle,
                                                         EIoControlCode.FsctlQueryAllocatedRanges,
                                                         hQueryRange.AddrOfPinnedObject(),
                                                         Marshal.SizeOf(queryRange),
                                                         hAllocRanges.AddrOfPinnedObject(),
                                                         Marshal.SizeOf(typeof(FILE_ALLOCATED_RANGE_BUFFER)) * 1024,
                                                         ref nbytes, ref lpOverlapped);

                if (!bFinished)
                {
                    int error = Marshal.GetLastWin32Error();

                    // ERROR_MORE_DATA is the only error that is normal
                    if (error != NativeMethod.ERROR_MORE_DATA)
                    {
                        Console.WriteLine("DeviceIoControl failed w/err 0x{0:X}",
                                          error);
                        return(false);
                    }
                }

                // Calculate the number of records returned
                int allocRangeCount = nbytes /
                                      Marshal.SizeOf(typeof(FILE_ALLOCATED_RANGE_BUFFER));

                // Print each allocated range
                for (int i = 0; i < allocRangeCount; i++)
                {
                    Console.WriteLine("allocated range: {0} {1}",
                                      allocRanges[i].FileOffset, allocRanges[i].Length);
                }

                // Set starting address and size for the next query
                if (!bFinished && allocRangeCount > 0)
                {
                    queryRange.FileOffset =
                        allocRanges[allocRangeCount - 1].FileOffset +
                        allocRanges[allocRangeCount - 1].Length;

                    queryRange.Length = fs.Length - queryRange.FileOffset;
                }
            } while (!bFinished);

            // Release the pinned GC handles
            hAllocRanges.Free();
            hQueryRange.Free();
        }

        return(true);
    }