Jak sprawdzić, czy dysk twardy jest wypełniony zerami w systemie Linux?

16

Mam dysk twardy wypełniony zerami.

Jak sprawdzić, czy wszystkie bity na dysku twardym są zerami przy użyciu bash?

gkfvbnhjh2
źródło
Czy dopuszczalne byłoby po prostu nadpisanie całego dysku zerami? Czy rzeczywiście musisz potwierdzić bieżącą zawartość?
Bob
Chcę sprawdzić, czy dysk twardy jest wypełniony zerami.
gkfvbnhjh2
1
Teoretycznie może występować błąd w narzędziach do czyszczenia danych, który powoduje, że niektóre dane pozostają nienaruszone. Nie chcę być pewien, że każdy bit ma wartość zero. Jak więc sprawdzić, czy dysk twardy jest pełen zer?
gkfvbnhjh2
Dlaczego zera? Czy nie zapisałbyś losowo zer i jedynek kilka razy?
13
Ponieważ jedynki są węższe niż zera - łatwiej można zobaczyć stare dane między nimi.
ChrisA

Odpowiedzi:

29

odzastąpi przebiegi tej samej rzeczy *, dzięki czemu można łatwo użyć go do skanowania w poszukiwaniu niezerowych bajtów:

$ sudo od /dev/disk2 | head
0000000    000000  000000  000000  000000  000000  000000  000000  000000
*
234250000
Gordon Davisson
źródło
8
Dodałbym | headdo tego koniec, aby jeśli okaże się, że dysk nie jest wyzerowany, zatrzymuje się po wygenerowaniu wystarczającej ilości danych wyjściowych, aby pokazać ten fakt, zamiast zrzucić cały dysk na ekran.
Wyzard
2
@Wyzard: Doskonały pomysł; Dodam to do mojej odpowiedzi.
Gordon Davisson
8

Napisałem do tego krótki program w C ++, źródło dostępne tutaj .

Aby zbudować:

wget -O iszero.cpp https://gist.github.com/BobVul/5070989/raw/2aba8075f8ccd7eb72a718be040bb6204f70404a/iszero.cpp
g++ -o iszero iszero.cpp

Aby uruchomić:

dd if=/dev/sdX 2>/dev/null | ./iszero

Wyświetli pozycję i wartość niezerowych bajtów. Możesz przekierować to wyjście do pliku za pomocą >np .:

dd if=/dev/sdX 2>/dev/null | ./iszero >nonzerochars.txt

Możesz spróbować zmienić w BUFFER_SIZEcelu zwiększenia wydajności. Nie jestem pewien, jaka może być optymalna wartość. Pamiętaj, że wpływa to również na częstotliwość drukowania postępu, co nieco wpłynie na prędkość (drukowanie na konsoli jest wolne ). Dodaj, 2>/dev/nullaby pozbyć się postępu produkcji.

Wiem, że nie używa to standardowej wersji bash ani nawet poleceń wbudowanych, ale nie powinno to wymagać żadnych dodatkowych uprawnień. Rozwiązanie @Hennes jest jeszcze szybsze (tak naprawdę niczego nie zoptymalizowałem - jest to naiwne rozwiązanie); jednak ten mały program może dać ci lepsze pojęcie o tym, ile bajtów pominął wycieracz i w jakiej lokalizacji. Jeśli wyłączysz wyjście postępu, będzie ono nadal szybsze niż większość dysków twardych dla konsumentów może odczytać (> 150 MB / s), więc nie jest to duży problem.

Szybsza wersja z mniej szczegółowym wyjściem jest dostępna tutaj . Jednak nadal jest nieco wolniejszy niż rozwiązanie @Hennes. Ten jednak zakończy pracę z pierwszą napotkaną niezerową postacią, więc jest potencjalnie znacznie szybszy, jeśli w pobliżu strumienia znajduje się niezerowa postać.


Dodanie źródła do posta, aby zachować odpowiedź, bardziej niezależną:

#include <cstdio>

#define BUFFER_SIZE 1024

int main() {
    FILE* file = stdin;
    char buffer[BUFFER_SIZE];
    long long bytes_read = 0;
    long long progress = 0;
    long long nonzero = 0;

    while (bytes_read = fread(buffer, 1, BUFFER_SIZE, file)) {
        for (long long i = 0; i < bytes_read; i++) {
            progress++;
            if (buffer[i] != 0) {
                nonzero++;
                printf("%lld: %x\n", progress, buffer[i]);
            }
        }
        fprintf(stderr, "%lld bytes processed\r", progress);
    }

    fprintf(stderr, "\n");

    int error = 0;
    if (error = ferror(file)) {
        fprintf(stderr, "Error reading file, code: %d\n", error);
        return -1;
    }

    printf("%lld nonzero characters encountered.\n", nonzero);
    return nonzero;
}
Kok
źródło
To świetna odpowiedź, ale czy jest jakiś sposób, aby skrypt działał bardziej jak normalne polecenie - używając iszero /dev/sdaraczej niż wymagającego potoku z czymś takim iszero < /dev/sda?
Hashim
1
@Hashim Zostało to napisane jakiś czas temu jako mniej lub bardziej wyrzutkowy program (obecnie robiłbym to przynajmniej w języku skryptowym takim jak Python zamiast w kompilacji C) ... to powiedziawszy, jeśli chcesz wziąć argumenty w najprościej rzecz biorąc, byłoby to gdzieś wzdłuż robienia tego int main(int argc, char *argv[])i wtedy FILE* file = fopen(argv[1], "r");. Prawidłowo wykonane, obejmowałoby sprawdzenie, czy argument rzeczywiście istnieje, sprawdzenie błędu powiodło się otwarcie (wykonaj dodatkowe ferrorsprawdzenie po fopen), itp., Ale zbyt wiele problemów dla programu do wyrzucania.
Bob
1
@Hashim Podejrzewam, że operacje wektoryzowane SIMD w numpy byłyby zbliżone do instrukcji wektoryzowanych w C. I to przy założeniu, że kompilator C jest wystarczająco inteligentny, aby wektoryzować pętlę w naiwnym programie C. Musiałby być testem porównawczym, aby się upewnić; niestety nie mam teraz na to czasu. Główną zaletą Pythona (i in.) Jest to, że jest ogólnie dostępny i działa bez kompilatora, a gccniekoniecznie jest dostępny we wszystkich dystrybucjach Linuksa bez pobierania dodatkowych pakietów. Z drugiej strony numpy nie jest częścią standardowych pakietów Pythona ...
Bob
1
@Hashim Jeśli kompilujesz się -O3i -march=nativemożesz zauważyć pewne przyspieszenia; to powinno upewnić się, że GCC włącza automatyczną wektoryzację i używa najlepszego dostępnego dla twojego procesora (AVX, SSE2 / SSE3 itp.). Oprócz tego możesz grać z wielkością bufora; różne rozmiary buforów mogą być bardziej optymalne z wektoryzowanymi pętlami (grałbym z 1 MB +, obecnie jeden to 1kB).
Bob
1
@Hashim Powyższy komentarz został edytowany, na wypadek gdybyś go nie widział. Poza tym, jeśli chcesz dyskutować dalej, możesz pingować mnie ( @Bob) na czacie: chat.stackexchange.com/rooms/118/root-access
Bob
7

Rozszerzenie odpowiedzi Gordona pvwskazuje, jak daleko jest ten proces:

$ sudo pv -tpreb /dev/sda | od | head
0000000 000000 000000 000000 000000 000000 000000 000000 000000
*
9.76GiB 0:06:30 [25.3MiB/s] [=================>               ] 59% ETA 0:04:56
Chris
źródło
Jest to bardzo przydatne w przypadku dużego dysku twardego!
Martin Hansen
5

To wydaje się brzydkie nieefektywne rozwiązanie, ale jeśli musisz sprawdzić tylko raz:

dd if=/dev/sdX | tr --squeeze-repeats "\000" "T"

Używanie dd do odczytu z dysku sdX. (zamień X na dysk, z którego chcesz czytać), a
następnie tłumacz wszystkie niedrukowalne bajty zerowe na coś, co możemy obsłużyć.

Następnie albo zliczamy bajty, które możemy obsłużyć i sprawdzamy, czy jest to poprawna liczba (użyj wc -cdo tego), albo pomijamy liczenie i używamy -slub, --squeeze-repeatsaby wycisnąć wszystkie wielokrotne wystąpienia do jednego znaku.

Dlatego dd if=/dev/sdX | tr --squeeze-repeats "\000" "T"powinien wydrukować tylko jeden T.

Jeśli chcesz to robić regularnie, potrzebujesz czegoś bardziej wydajnego.
Jeśli chcesz to zrobić tylko raz, ta kludge może sprawdzić, czy normalna wycieraczka działa i czy możesz jej zaufać.

Hennes
źródło
Dlaczego uważasz to rozwiązanie za nieefektywne? Czy istnieje buforowanie wymagające odczytu daleko poza pierwszą lokalizacją inną niż NUL?
Daniel Beck
Czy istnieje potencjalny problem, w którym dosłowna „T” występuje w parze jako jedyna niezerowa postać?
Bob
Prawdziwe. To jest wada w projekcie. Nie używam również bash (sama powłoka), ale założyłem, że z „Bash” masz na myśli „Nie z bash, z użycia jakiegokolwiek polecenia powłoki i standardowych narzędzi w trybie tekstowym”.
Hennes
3
@ Daniel: Prosty program C powinien być w stanie odczytać wszystkie dane bez zmiany każdego czytanego bajtu. Który byłby bardziej wydajny i estetyczny. Napisanie takiego programu może również zająć znacznie więcej czasu niż tylko użycie dostępnych narzędzi w nieefektywny sposób.
Hennes
3

Aby to sprawdzić, zobaczysz na liście wszystkie bloki, które nie pasują

sudo badblocks -sv -t 0x00 /dev/sdX

Lub użyj złych bloków, aby je napisać i sprawdzić:

sudo badblocks -svw -t 0x00 /dev/sdX

Domyślnym testem destrucive jest moje bezpieczne usunięcie

sudo badblocks -svw /dev/sdX

Jeśli ktokolwiek może coś odzyskać po zapełnieniu dysku naprzemiennymi zerami i jedynkami, to ich dopełnienie, następnie wszystkie jedynki, a następnie wszystkie zera, przy każdym sprawdzeniu, czy zadziałało, powodzenia!

Sprawdza również przed instalacją nowe dyski

man badblocks

dla innych opcji

Nie mówię, że jest szybki, ale działa ...

Beardy
źródło
2

Najlepsze z obu światów. To polecenie pominie złe sektory:

sudo dd if=/dev/sdX conv=noerror,sync | od | head

Użyj, kill -USR1 <pid of dd>aby zobaczyć postęp.

jiveformation
źródło
0

Jakiś czas temu byłem ciekawy AIO. Rezultatem był przykładowy program testowy, który sprawdza, czy istnieją sektory (bloki 512 bajtów) NUL. Możesz to zobaczyć jako wariant rzadkiego detektora obszarów plików . Myślę, że źródło mówi wszystko.

  • Jeśli cały plik / dysk jest NULwyprowadzany, wygląda to tak 0000000000-eof. Zauważ, że w programie jest sztuczka, funkcja fin()nie jest wywoływana w linii 107 celowo, aby dać pokazane wyjście.
  • Nie jest dokładnie testowany, więc może zawierać błędy
  • Kod jest nieco dłuższy, ponieważ AIOnie jest tak prosty jak inne sposoby,
  • AIOjest to jednak prawdopodobnie najszybszy sposób na utrzymanie zajętości dysku , ponieważ NULporównywanie odbywa się podczas wczytywania następnego bloku danych. (Możemy nakładać kilka milisekund przez nakładanie się AIO, ale naprawdę nie sądzę, że jest to warte wysiłek.)
  • Zawsze zwraca, truejeśli plik jest czytelny i wszystko działało. Nie zwraca, falsejeśli plik nie jest NUL.
  • Zakłada się, że rozmiar pliku jest wielokrotnością 512. W ostatnim sektorze występuje błąd, jednak w całym pliku NULnadal działa, ponieważ bufory pamięci już zawierają NUL. Jeśli ktoś myśli, że to wymaga naprawy, w wierszu 95 memcmp(nullblock, buf+off, SECTOR)można przeczytać memcmp(nullblock, buf+off, len-off<SECTOR : len-off : SECTOR). Ale jedyną różnicą jest to, że „raportowanie końcowe” może być nieco losowe (nie dotyczy pliku, który jest całkowicie NUL).
  • Zmieniony memcmp()rozwiązuje również inny problem na platformach, które nie NUL alloc()edują pamięci, ponieważ kod tego nie robi. Ale może to być widoczne tylko w plikach mniejszych niż 4 MiB, ale checknulprawdopodobnie jest to zwykła przesada w przypadku tak małego zadania;)

HTH

/* Output offset of NUL sector spans on disk/partition/file
 *
 * This uses an AIO recipe to speed up reading,
 * so "processing" can take place while data is read into the buffers.
 *
 * usage: ./checknul device_or_file
 *
 * This Works is placed under the terms of the Copyright Less License,
 * see file COPYRIGHT.CLL.  USE AT OWN RISK, ABSOLUTELY NO WARRANTY.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#include <malloc.h>
#include <aio.h>

#define SECTOR  512
#define SECTORS 40960
#define BUFFERLEN   (SECTOR*SECTORS)

static void
oops(const char *s)
{
  perror(s);
  exit(1);
}

static void *
my_memalign(size_t len)
{
  void      *ptr;
  static size_t pagesize;

  if (!pagesize)
    pagesize = sysconf(_SC_PAGESIZE);
  if (len%pagesize)
    oops("alignment?");
  ptr = memalign(pagesize, len);
  if (!ptr)
    oops("OOM");
  return ptr;
}

static struct aiocb aio;

static void
my_aio_read(void *buf)
{
  int   ret;

  aio.aio_buf = buf;
  ret = aio_read(&aio);
  if (ret<0)
    oops("aio_read");
}

static int
my_aio_wait(void)
{
  const struct aiocb    *cb;
  int           ret;

  cb = &aio;
  ret = aio_suspend(&cb, 1, NULL);
  if (ret<0)
    oops("aio_suspend");
  if (aio_error(&aio))
    return -1;
  return aio_return(&aio);
}

static unsigned long long   nul_last;
static int          nul_was;

static void
fin(void)
{
  if (!nul_was)
    return;
  printf("%010llx\n", nul_last);
  fflush(stdout);
  nul_was   = 0;
}

static void
checknul(unsigned long long pos, unsigned char *buf, int len)
{
  static unsigned char  nullblock[SECTOR];
  int           off;

  for (off=0; off<len; off+=SECTOR)
    if (memcmp(nullblock, buf+off, SECTOR))
      fin();
    else
      {
        if (!nul_was)
          {
            printf("%010llx-", pos+off);
            fflush(stdout);
            nul_was = 1;
          }
        nul_last    = pos+off+SECTOR-1;
      }
}

int
main(int argc, char **argv)
{
  unsigned char *buf[2];
  int       fd;
  int       io, got;

  buf[0] = my_memalign(BUFFERLEN);
  buf[1] = my_memalign(BUFFERLEN);

  if (argc!=2)
    oops("Usage: checknul file");
  if ((fd=open(argv[1], O_RDONLY))<0)
    oops(argv[1]);

  aio.aio_nbytes    = BUFFERLEN;
  aio.aio_fildes    = fd;
  aio.aio_offset    = 0;

  io = 0;
  my_aio_read(buf[io]);
  while ((got=my_aio_wait())>0)
    {
      unsigned long long    pos;

      pos   = aio.aio_offset;

      aio.aio_offset += got;
      my_aio_read(buf[1-io]);

      checknul(pos, buf[io], got);

      io    = 1-io;
    }
  if (got<0)
    oops("read error");
  printf("eof\n");
  close(fd);
  return 0;
}
Tino
źródło
0

Chciałem opublikować to sprytne rozwiązanie z podobnego, ale wcześniejszego pytania zadanego przez użytkownika, który nie logował się przez pewien czas:

W /dev/zerosystemie Linux jest urządzenie , które zawsze odczytuje zera podczas odczytu.

A co powiesz na porównanie dysku twardego z tym urządzeniem:

cmp /dev/sdX /dev/zero

Jeśli wszystko jest w porządku z zerowaniem dysku twardego, zakończy się on:

cmp: EOF on /dev/sdb

mówiąc, że dwa pliki są takie same, dopóki nie dotarł do końca dysku twardego. Jeśli na dysku twardym jest niezerowy bit cmp, powie Ci, gdzie jest w pliku.

Jeśli masz pvzainstalowany pakiet, to:

pv /dev/sdX | cmp /dev/zero

zrobi to samo z paskiem postępu, abyś był rozbawiony podczas sprawdzania dysku (EOF będzie teraz na STDIN, a nie na sdX).

Hashim
źródło