tenox7 / disktrim

Windows application to send TRIM / UNMAP / DISCARD to SSD. Similar to blkdiscard.
Apache License 2.0
32 stars 6 forks source link

Disk size detected as 0.0GB and only partition table is deleted, not data #2

Open Dwedit opened 4 years ago

Dwedit commented 4 years ago

It appears that this program only deletes the partition table, and not the actual contents of the disk. I can still find data on the hard drive using a hex editor.

Before the Yes/No prompt, it displays the size of the disk drive (238.5GB for my ssd) correctly.

But after the Yes/No prompt, it displays this:

\\.\PhysicalDrive0 LBA: 0, Block: 0, Size: 0.0GB

It appears to be multiplying 0 by 0 to calculate a size of 0, and overwrites only that much.

tenox7 commented 4 years ago

Yes so looks like size detection problem. Can you send the whole output? Thanks!

Dwedit commented 4 years ago

This line seems to receive the incorrect information:

    if (!DeviceIoControl(hDisk, IOCTL_SCSI_PASS_THROUGH, Buffer, BufLen, Buffer, BufLen, &BytesRet, NULL))

Then there are Zeroes sitting in the buffer where the variables are read:

    DiskLbaCount = REVERSE_BYTES_LONG64(pReadCapacity->LBA);
    DiskBlockSize = REVERSE_BYTES_LONG(pReadCapacity->BlockLength);

I examined the ScsiPass struct, and it appears that TargetId became 0, SenseInfoLength became 0, and other data changed at all. I also checked within 256 bytes after the Cdb field, no changes there either.

tenox7 commented 4 years ago

Can you tell me more about the environment so I can try to reproduce it? Disk/machine make/model? Windows version? Thanks.

Dwedit commented 4 years ago

Sure...

I reproduced this on two machines.

One is a Dell laptop, with an M.2 SATA hard drive, 1TB from Crucial. Running on Windows 10 x64 ("2004" version). Made sure all erase code was commented out, so I could try debugging the IOCTRL call.

Other is an Acer laptop that I'm trying to return (want to blank out the SSD first), NVMe HFM256GDJTNG-831. Running Windows 10 PE. For some reason, none of the bootable linux USB sticks could see the drive, so I'm using Windows PE instead.

Perhaps it should try to do IOCTL_STORAGE_READ_CAPACITY instead of SCSI_PASSTHROUGH?

Edit: Tried IOCTL_STORAGE_READ_CAPACITY, and it returned 1953525168 sectors, each 512 bytes, for a size of 931.5GB. Don't know why SCSI_PASSTHROUGH is failing.

Edit 2: Looks like the SCSI_PASSTHROUGH trim command just doesn't work. "ERROR: TRIM didn't seem to work". Perhaps Windows 10 is blocking it?

Edit 3: Looks like IOCTL_STORAGE_READ_CAPACITY can verify if SCSI_PASSTHROUGH works or not. I wonder if ATA_PASSTHROUGH or windows nvme pasthrough would work?

tenox7 commented 4 years ago

NVME is not ATA. But it may be worth trying. Perhaps there is another class for NVME all together? Also could try to use IOCTL_STORAGE_READ_CAPACITY and see if it works?

Dwedit commented 4 years ago

I just did a few tests in VirtualBox using normal disk operations (no SCSI bypass stuff here). Actually getting VirtualBox to support the TRIM command took some command line trickery**, but you can do it.

So it seems the best way to actually trim a disk may be this:

I only tested this on VirtualBox. When VirtualBox sees a trim command, it will shrink the size of the virtual hard disk file, then future reads from those areas of the disk become 00 bytes.

Real SSDs may not necessarily read back 00 bytes after using TRIM, and Windows won't tell you if it actually sent any TRIM commands to the drive successfully.

*To get SetFileValidData to work: (also need to run the program as Admin)

    BOOL okay;
        TOKEN_PRIVILEGES priv = {}, prevPriv = {};
    HANDLE hToken = NULL;
    DWORD returnLength = 0;
    priv.PrivilegeCount = 1;
    priv.Privileges->Attributes = SE_PRIVILEGE_ENABLED;
    okay = LookupPrivilegeValueW(NULL, L"SeManageVolumePrivilege", &priv.Privileges->Luid);
    okay = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
    prevPriv = priv;
    okay = AdjustTokenPrivileges(hToken, false, &priv, sizeof(prevPriv), &prevPriv, &returnLength);

**To get VirtualBox to support the TRIM command:

tenox7 commented 4 years ago

VirtualBox is not really supported here because of unknown SCSI controller type. I also don't want to create/delete any files, as this would be filesystem based trim, where I want a real, SCSI controller UNMAP, hence SCSI passthrough stuff. To me the real issue is zero LBA count, which will need to be fixed.

Dwedit commented 4 years ago

Okay, to fix the zero LBA count thing, just use IOCTL_STORAGE_READ_CAPACITY, and compare against the current passthrough code. If they disagree, the passthrough doesn't work.

tenox7 commented 4 years ago

Do you want to send a PR?

Dwedit commented 4 years ago

Looks like simply checking if it got zeroes and erroring out at that point would be sufficient.

    if (DiskLbaCount == 0 && DiskBlockSize == 0)
    {
        error(1, L"Error: SCSI Passthrough is not supported on this drive.");
    }

Here is the code that uses IOCTL_DISK_GET_LENGTH_INFO instead of SCSI passthrough to get the size. I don't really see a point to including it though, because if scsi passthrough fails on something like getting the drive size, it probably won't succeed on a TRIM command either.

    STORAGE_READ_CAPACITY capacity;
    ZeroMemory(&capacity, sizeof(capacity));

    if (!DeviceIoControl(
        (HANDLE)hDisk,             // handle to device
        IOCTL_STORAGE_READ_CAPACITY,  // dwIoControlCode
        NULL,                         // lpInBuffer
        0,                            // nInBufferSize
        (LPVOID)&capacity,         // output buffer
        (DWORD)sizeof(capacity),       // size of output buffer
        (LPDWORD)&BytesRet,    // number of bytes returned
        NULL  // OVERLAPPED structure 
    ))
    {
        error(1, L"Error on DeviceIoControl IOCTL_DISK_GET_LENGTH_INFO");
    }

    DiskLbaCount = (ULONG64)capacity.NumberOfBlocks.QuadPart;
    DiskBlockSize = (ULONG)capacity.BlockLength;
tenox7 commented 4 years ago

OK but wait have you tried if passthrough for trim works? Perhaps it's just size info that doesn't work. Have you tried to use IOCTL_STORAGE_READ_CAPACITY and feed it to the UNMAP passthrough?

Dwedit commented 4 years ago

When I tried it with a correct LBA count and Block size, none of the DeviceIoControl calls returned false, but when it checked the test pattern, it displayed "TRIM didn't seem to work".

tenox7 commented 4 years ago

I think there may be some problem with the M.2 SATA bus. Perhaps it doesn't support SCSI passthrough? Maybe it comes up as some legacy ATA bus in Windows. Can you see how is it connected in Windows? How about wmic diskdrive for starters. Maybe we could also debug it with busTRACE.

tenox7 commented 4 years ago

Can you download busTRACE capture client from this page: http://www.bustrace.com/downloads/free_utilities.php then do capture an unmodified vanilla version of disktrim and send me the capture file? Thanks.