4

I have read many "how do I write an iso to USB" questions. I am looking for a way to reverse the process. My question is about taking a USB stick containing a Windows installer or Mac installer and using dd to read only those parts of the USB that are needed to recreate the original iso.

Scenario:
I have a Windows 10 October 2020 edition USB that I burned back in November. I want to back that up to disk using dd. Something along the lines of:

dd if=/dev/sda of=backup-windows.iso bs=4m

I would like to be able to compare this with the original iso and get a match. In other words, I don't want to have to make a 64 GB image of a USB drive when the ISO is only 7-8GB.

2

1 Answer 1

3

Preliminary note

This answer introduces commands valid in POSIX-compliant or almost POSIX-compliant operating systems, in particular in Linux or macOS. Commands are not for pure Windows (but I suppose the answer can be adjusted to work in WSL or Cygwin).


Analysis

OK, you have copied some image to a USB drive /dev/sdx. In general you cannot exactly reverse the process basing solely on the content and properties of the drive because you don't know how many bytes to read. The upper bound is the size of the drive (64 GB in your case), everything else is a guess. The drive contains a stream of bytes and there is no mark saying "stop reading here to get the original image".

By analyzing the partition table (if any) it's possible to take an educative guess. But even then you cannot be sure the original image was not larger (with garbage or non-garbage at the end).

However you want to compare the result to some other image, let's call it the_other_file. I guess you hope the two images will be identical. To be identical they must be of the same size. This means you know how many bytes to read from /dev/sdx: the exact number of bytes the_other_file has. You can get this number by invoking

wc -c /path/to/the_other_file

or

</path/to/the_other_file wc -c

Note the former is a standalone command while the latter requires a shell (to handle <). From now on I assume you're in a shell.


Copying

If your ultimate goal is to compare then you don't need to copy to a regular file. Read the whole answer before you decide. Still the explicit question is:

How can I recreate an installer iso from a usb […] using dd?

It seems dd is the only POSIX way to read an exact number of bytes from a file. The way requires ibs=1, it can be slow. To read N bytes:

dd ibs=1 count=N if=/dev/sdx of=image_file

The resulting file is image_file.

The original image, as an image designed to be written to a block device, most likely was of a size that is multiple of 512 bytes, possibly a multiple of 4096 bytes. You probably can use some bigger ibs then.

If N is an exact multiple of some bigger ibs you want to use then you can use the bigger ibs with count adjusted. The number after count= specifies how many blocks of the size ibs to read. Notes:

  • bs specifies both ibs and obs (see man 1 dd).
  • Neither bs=4m (from your example) nor bs=4M belong to the POSIX specification of dd. The valid suffixes are k and b. You may be surprised the specification also allows strings in a form of AxB. To get 4 MiB use bs=4kx1k; to get 4 MB use bs=4000000 or bs=4000x1000 etc.
  • In general keep in mind dd is a cranky tool which is hard to use correctly. There are pitfalls. See this and this.

If N is not an exact multiple of some bigger ibs you want to use, it's possible to read the majority of what you want to read with the large ibs and append the missing part while reading with ibs=1. This is cumbersome, requires some calculations, still it's technically possible; hardly ever useful though. If you are not limited to POSIX, there are better ways.

GNU dd supports iflag=count_bytes that makes count count bytes regardless of ibs. Along with iflag=fullblock it makes dd a decent tool to read an exact number of bytes. In your case the command could be:

dd bs=4M iflag=fullblock,count_bytes count=N if=/dev/sdx of=image_file

or (to avoid typing N manually):

dd bs=4M iflag=fullblock,count_bytes count="$(</path/to/the_other_file wc -c)" if=/dev/sdx of=image_file

If I were you I would use head -c:

head -c "$(</path/to/the_other_file wc -c)" /dev/sdx >image_file

Note wc -c is portable (specified by POSIX) but head -c is not (although I believe head in macOS supports -c).


Comparing

You can compare directly, without creating any image_file. The following command is portable:

cmp /dev/sdx /path/to/the_other_file

In your case /dev/sdx is a device larger than the_other_file. You will get either a message (to stdout) the files differ, or a message (to stderr) EOF on the_other_file. In either case the exit status will be 1. The latter message means the beginning of /dev/sdx is identical to the_other_file. This is what you would expect after copying the_other_file to /dev/sdx.

If you want to judge by the exit status (useful in scripting) then you need to make sure you're comparing files of equal sizes. This is still possible without creating any image_file:

head -c "$(</path/to/the_other_file wc -c)" /dev/sdx | cmp - /path/to/the_other_file

(The exit status of a pipeline comes from the last command; here: from cmp.) Note head -c is (still :)) not portable. You can replace it with portable dd … if needed:

dd ibs=1 count="$(</path/to/the_other_file wc -c)" if=/dev/sdx | cmp - /path/to/the_other_file

Alternatively after creating image_file as shown above, you can straightforwardly compare image_file to the_other_file:

cmp image_file the_other_file

Exit status 0 indicates the files are identical; exit status 1 indicates the files are not identical.

While working interactively you can pay attention to the output instead (I mean stdout + stderr, i.e. all what you normally see in a terminal). Empty output from cmp indicates the files are identical; non-empty output from cmp indicates whatever it says. Note dd (but not head) prints to stderr even if there was no error. I deliberately wrote "empty output from cmp", not just "empty output". When in doubt, check the exit status anyway: run echo $? just after cmp (or after a pipeline ending with cmp).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .