Check SCSI Device Capacity Safely with sg_readcap

sg_readcap reads a SCSI device's block count and block size, the numbers behind its reported capacity. You will finish with a repeatable way to read them, check the calculated capacity, and decide when to request the extended READ CAPACITY(16) response. Allow about ten minutes. You need the sg3-utils package, a device path, and permission to open that device. The examples only ask the device for capacity data; they do not format, resize or write to it.

1. Confirm the installed command

Start with ordinary, read-only checks. They do not need elevated privileges:

$ command -v sg_readcap
/usr/bin/sg_readcap
$ dpkg-query -W -f='${Package} ${Version}\n' sg3-utils
sg3-utils 1.46-3ubuntu4
$ sg_readcap --version
Version string: 4.05 20200122

Version output can look inconsistent because the Debian package version, the manpage revision and the utility's own version string are separate pieces of information. On this installation the manpage is marked sg3_utils-1.45, the package manager reports 1.46-3ubuntu4, and the binary prints the version string shown above. Use the local help and manpage as the contract for this host:

$ sg_readcap --help
Usage: sg_readcap [--16] [--brief] [--help] [--hex] [--lba=LBA] [--long]
                  [--pmi] [--raw] [--readonly] [--verbose] [--version]
                  [--zbc] DEVICE

Checkpoint: you have identified the binary and recorded the version information before interpreting its output.

2. Identify the correct device

sg_readcap accepts one device. Prefer a stable path that you have already mapped to the intended disk, such as a device under /dev/disk/by-id/. A generic SCSI device such as /dev/sg0 may change after hardware discovery, so do not guess its identity.

Use your normal inventory tools to map the path. For example, if lsscsi is installed, its generic-device column can help:

$ lsscsi -g
[HOST:CHANNEL:TARGET:LUN]  disk  VENDOR  MODEL  ...  /dev/sdX  /dev/sgY

The row and names are host-specific. Replace /dev/disk/by-id/REPLACE_WITH_THE_DEVICE in the next commands with a path you have checked. Never use a partition path when you mean to inspect the whole device.

Opening a device can fail even for a read-only SCSI command. If you see Permission denied, first check ownership and groups:

$ ls -l /dev/sgY
$ id

Only if your system policy allows it, rerun the eventual capacity command with sudo. Elevated privileges are a workaround for device access, not a requirement for the SCSI operation itself.

3. Read the normal capacity report

The default sends READ CAPACITY(10). Add --readonly explicitly when you want the safety boundary visible in a script or review:

$ sg_readcap --readonly /dev/disk/by-id/REPLACE_WITH_THE_DEVICE
Read Capacity results:
   Last logical block address:     0x1234567 (19088743)
   Logical block length:           512 bytes
   Hence:
      Device capacity:            9763 MB, 9.76 GB, 0.00976 TB

The exact formatting and numbers depend on the device. The important detail is that the command reports the address of the last block, not a block count. SCSI blocks start at zero, so the number of blocks is the last logical block address plus one. Capacity is that block count multiplied by the logical block length. This is the common source of an off-by-one calculation.

If your device is too large for READ CAPACITY(10), a compliant device can return 0xffffffff. Without --hex, this utility then requests READ CAPACITY(16) and prints that response. A device that does not respond well to READ CAPACITY(16) is a known compatibility boundary, so keep the first run at the default unless you need the extended response.

Checkpoint: the command exits successfully and you have recorded both the last block address and the block length. Do not turn the displayed decimal or GB value into a partitioning decision until you have confirmed the device identity.

4. Request READ CAPACITY(16) deliberately

Use --long, or its equivalent --16, when you need the 16-byte command response. It removes the READ CAPACITY(10) address limit and can expose protection, logical-block-provisioning and physical-block information:

$ sg_readcap --long --readonly /dev/disk/by-id/REPLACE_WITH_THE_DEVICE
Read Capacity results:
   Protection: prot_en=0, p_type=0, p_i_exponent=0
   Logical block provisioning: lbpme=0, lbprz=0
   Last logical block address: 0x123456789
   Logical block length: 4096 bytes

Field names and values vary with the device and the installed utility. Do not treat a missing protection or provisioning field as proof that the underlying storage lacks those features; first establish which command response you requested.

Some devices have poor READ CAPACITY(16) implementations and can misbehave when sent that command. If the default command works, prefer it for a basic capacity check. If you need the extra fields, test --long against the actual device during a suitable maintenance window and watch the command's exit status.

5. Produce machine-friendly output

For a script that needs only the block count and block size, use --brief. It writes two hexadecimal values to standard output: the number of blocks first, then the size of each block:

$ sg_readcap --brief --readonly /dev/disk/by-id/REPLACE_WITH_THE_DEVICE
0x1234568 0x200

A failed operation writes 0x0 0x0, so a script must check the exit status as well as the values. Keep stderr separate if another program consumes stdout:

device='/dev/disk/by-id/REPLACE_WITH_THE_DEVICE'
if ! capacity=$(sg_readcap --brief --readonly "$device"); then
    printf 'sg_readcap failed for %s\n' "$device" >&2
    exit 1
fi
set -- $capacity
if [ "$1" = 0x0 ] && [ "$2" = 0x0 ]; then
    printf 'capacity response was empty\n' >&2
    exit 1
fi
printf 'blocks=%s block_bytes=%s\n' "$1" "$2"

The shell snippet intentionally does not convert the hexadecimal numbers or change the device. Use a language with checked integer arithmetic if you need to calculate byte totals, especially for large devices.

6. Inspect raw or diagnostic responses only when needed

--hex prints the READ CAPACITY response as ASCII hexadecimal, while --raw writes binary response data. These modes are useful when comparing a device response with a protocol trace or passing it to a tool that expects bytes. They are not suitable for casual terminal output:

$ sg_readcap --hex --readonly /dev/disk/by-id/REPLACE_WITH_THE_DEVICE
$ sg_readcap --raw --readonly /dev/disk/by-id/REPLACE_WITH_THE_DEVICE > response.bin

The second command creates a local file. Remove that file after reviewing it if it contains information you do not need to retain:

$ rm -- response.bin

This is the only state-changing example in the guide, and it changes only your local output file. Do not redirect --raw to a device or feed it into a write-capable utility.

Done means