coreutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

inconsistent behaviour of dd with seek= and oflag=seek_bytes


From: Rasmus Villemoes
Subject: inconsistent behaviour of dd with seek= and oflag=seek_bytes
Date: Wed, 20 Sep 2023 10:09:40 +0200
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.15.1

Consider this:

$ echo aaa > x
$ echo aaa > y
$ echo bbb > b
$ dd if=b of=x seek=600 oflag=seek_bytes
0+1 records in
0+1 records out
4 bytes copied, 0,000187014 s, 21,4 kB/s
$ dd if=b of=y seek=100 oflag=seek_bytes
0+1 records in
0+1 records out
4 bytes copied, 0,000359168 s, 11,1 kB/s
$ hexdump -C x
00000000  61 61 61 0a 00 00 00 00  00 00 00 00 00 00 00 00
|aaa.............|
00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
|................|
*
00000250  00 00 00 00 00 00 00 00  62 62 62 0a              |........bbb.|
0000025c
$ hexdump -C y
00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
|................|
*
00000060  00 00 00 00 62 62 62 0a                           |....bbb.|
00000068

In the latter case, the old contents of y are gone. This is easily
traced to the code that decides whether to include O_TRUNC in the open
flags:

      int opts
        = (output_flags
           | (conversions_mask & C_NOCREAT ? 0 : O_CREAT)
           | (conversions_mask & C_EXCL ? O_EXCL : 0)
           | (seek_records || (conversions_mask & C_NOTRUNC) ? 0 :
O_TRUNC));

So when the seek value is smaller than a block (so seek_records is 0 and
seek_bytes has seek= value), we end up passing O_TRUNC. That is, IMO,
quite unexpected and inconsistent.

So one could pass conv=notrunc to be explicit. However, another case
where this could hit is when one tries to use dd to extend a file to a
specific size, where passing conv=notrunc is not an option (because then
dd wouldn't do ftruncate). One could do

  dd if=/dev/null of=file seek=$somevalue oflag=seek_bytes

and that will work fine for $somevalue large enough, but then break when
it happens to be < 512 (leaving the file empty). Of course, a more
portable way to do this is to write it

  dd if=/dev/null of=file seek=$somevalue bs=1

but I think the current behavior of seek_bytes with wildly different
results depending on whether the seek= value is below the block size or
not is a footgun.

I suggest moving the computation and sanity checking of size above the
initialization of opts, and then in the three places where
'seek_records' is used as a boolean (opts, open logic, decision whether
to do ftruncate), replace with 'size'.

Rasmus



reply via email to

[Prev in Thread] Current Thread [Next in Thread]