Эффект 'cp /dev/zero a' при наличии файла a

У меня была та же проблема, и я быстро нашел простое решение, используя функцию awklog():

awk '
  BEGIN {
    split("B,kiB,MiB,GiB", suff, ",")
  }

  {
    size=$1;
    rank=int(log(size)/log(1024));
    printf "%.4g%s\n", size/(1024**rank), suff[rank+1]
  }
'

И потеря точности при использовании чисел с плавающей запятой не так уж плоха, поскольку эта точность все равно будет потеряна.

6
08.08.2020, 07:28
3 ответа

Это зависит от используемой файловой системы и устройства хранения. Как правило, файловые системы будут перезаписываться, когда вы им это скажете, если только вы не используете файловую систему, оптимизированную для необработанной флэш-памяти; эти специальные файловые системы могут реализовывать выравнивание износа на уровне FS и, следовательно, записывать в другое место.

Но если устройство хранения представляет собой обычный твердотельный накопитель SATA или NVMe, оно все равно может выполнять внутреннее выравнивание износа, и реальные физические блоки хранения будут отличаться от того, что покажет даже необработанное блочное устройство.Таким образом, «перезапись» в конечном итоге будет происходить в другом физическом месте, даже если файловая система считает, что она определенно перезаписывает определенный блок #.

Но преодоление системы выравнивания износа SSD и осмысленное чтение необработанного хранилища должно быть нетривиальной -технической проблемой, требующей специальных знаний и, возможно, специальных аппаратных средств. И SSD, вероятно, предварительно -предусмотрительно сотрет любые «перезаписанные» блоки, как только это станет практически возможным, чтобы иметь как можно больше стертых блоков, доступных для записи, поскольку стирание, как правило, является ограничивающим фактором производительности SSD.

3
18.03.2021, 23:15

Я подозреваю, что когда вы открываете файл, чтобы записать его с самого начала, все существующие блоки будут немедленно освобождены (, все еще содержащие их существующее содержимое ), и ваш cp обнулит новые блоки -. ] наполнять.

Кроме того, ваш файл будет расширен до тех пор, пока не заполнит весь раздел :первоначальный размер не будет учитываться.

Команда ddимеет параметр conv=notrunc, и вы сможете найти исходный размер с помощью stat, округлить его до целых блоков и использовать параметры countи bsдля определения размера нулей. в те же блоки, что и оригинал.

Изменить :Тестом подтверждено, что оболочка > перенаправление сохраняет тот же номер инода, но немедленно уменьшает размер файла до нулевых блоков и освобождает их для доступного пространства.

strace на cp показывает ванильную перезапись файла через область mmap:

open("foo.tiny", O_RDONLY)              = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=8192,...}) = 0
open("foo.copy", O_WRONLY|O_CREAT|O_EXCL, 0644) = 4
fstat(4, {st_mode=S_IFREG|0644, st_size=0,...}) = 0
fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
mmap(NULL, 139264, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc295d30000
read(3, "\0\0\0\0"..., 131072) = 8192
write(4, "\0\0\0\0"..., 8192) = 8192
read(3, "", 131072)                     = 0
close(4)                                = 0
close(3)                                = 0
munmap(0x7fc295d30000, 139264)          = 0
lseek(0, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
close(0)                                = 0
close(1)                                = 0
close(2)                                = 0

Вывод должен заключаться в том, что cpудалит ваши конфиденциальные данные в свободный список и перезапишет любые блоки, которые будут выделены впоследствии.

Этот сценарий демонстрирует, что dd обнуляет часть или все существующие блоки в файле, не освобождая блоки и не изменяя индексный дескриптор. Возможно, вам потребуется получить фактический BLKSZ и размер файла из команды stat,и использовать арифметику bash для округления размера до целого числа блоков. Также показывает, что dd расширит файл и запишет разреженные данные.

В результате получается около 100 строк вывода, поэтому я не буду это публиковать. Это доброкачественно. Функция Zero - это мясо.

#! /bin/bash

FN='./Erase.data'
BLKSZ=4096

#.. Zeroise a specified range of blocks (zero-based).

Zero () {   #:: (from, to)

    dd 2>&1 ibs="${BLKSZ}" obs="${BLKSZ}" \
        seek="${1}" count="$(( $2 - $1 + 1 ))" \ 
        conv=notrunc if="/dev/zero" of="${FN}"
}

#.. Create a file of 8 * 4096B blocks, each labelled in every character.

Make () {   #:: (void)

    AWK='   
function Block (sz, id, Local, buf) {
    buf = sprintf ("%*s", sz, "");
    gsub (/./, id, buf);
    printf ("%s", buf);
}
{ for (f = 2; f <= NF; ++f) Block( $1, $(f)); }
'
    echo "${BLKSZ}" {A..H} | awk "${AWK}" > "${FN}" 
}

#.. Reveal the file.

Show () {

    echo; ls -l "${FN}"; stat "${FN}"; od -A d -t a "${FN}"; sleep 2 
}

#### Script Body Starts Here.

    #.. Make the file and prove its contents.
    Make > "${FN}" && Show 
    Zero 3 6 && Show 
    Zero 0 1 && Show 
    Zero 0 7 && Show 
    Zero 220 231 && Show 

Это приближение к производственной версии.

#! /bin/bash

Usage () { expand -t 4 <<'EOF'

    Usage: ZeroAllBlocks [-h] [files...]
        Warning: this command is as brutal as rm -f.
        -h: shows this message.

        Zeroises (binary zero) all blocks of all the files named.
        Sparse blocks will then consume real disk space.
EOF
}

#.. Zeroise a specified range of blocks (zero-based).

Zero () {   #:: (Fn, blksz, seek, count)

    local Fn="${1}" blksz="${2}" seek="${3}" count="${4}"

    dd status=none ibs="${blksz}" obs="${blksz}" \ 
        seek="${seek}" count="${count}" \
        conv=notrunc if="/dev/zero" of="${Fn}"
}

#.. Process a file.

File () {   #:: (filename)

    local Fn="${1}" szFile szBlock nBlock

    [[ -f "${Fn}" ]] || { printf '%s: No such file\n' "${Fn}"; return; }
    [[ -w "${Fn}" ]] || { printf '%s: Not writable\n' "${Fn}"; return; }
    read -r szFile szBlock <<<$( stat --printf='%s %o\n' "${Fn}" )
    nBlock="$(( (szFile + szBlock - 1) / szBlock ))"
    Zero "${Fn}" "${szBlock}" 0 "${nBlock}"
}

#### Script Body Starts Here.

    [[ "${1}" = "-h" ]] && { Usage; exit 2; }

    for Fn in "${@}"; do File "${Fn}"; done
8
18.03.2021, 23:15

Почему вы хотите скопировать /dev/zeroв какой-то файл?

  • Вы хотите безопасно удалить a? Тогда cp— неправильный инструмент. Посмотрите shred, если вы используете жесткий диск, и fstrim, если вы используете SSD. (Скорее всего, есть и другие инструменты, о которых я не знаю, поэтому было бы неплохо погуглить «безопасное удаление».)

  • Вы понимаете, что /dev/zeroбесконечен в размерах? Он будет бесконечно возвращать нули, поэтому cpникогда не завершится, пока файловая система, содержащая a, не заполнится.

  • Но этого может и не произойти, потому что cp,в рамках обнаружения разреженных файлов подавляет запись всех -нулевых страниц.

  • Вы хотите создать файл заданного размера, полный нулей? cpвсе еще не тот инструмент. Вам нужно будет использовать dd, чтобы указать, сколько байт копировать из /dev/zero.

В общем, используйте cpдля копирования обычного файла в другой обычный файл, когда все, о чем вы заботитесь, это то, что копия логически эквивалентна.

Если вы работаете с файлами устройств или пытаетесь управлять удалением блоков исходного файла, вам следует использовать другую программу.

8
18.03.2021, 23:15

Теги

Похожие вопросы