I'm trying to read data from a faulty external SSD to create an image for data recovery. The drive is an Apacer Panther SSD connected to a USB port via an ICY BOX SATA to USB connector on Ubuntu.
Executing the MWE below, read
hangs at some address. The address is mostly stable between consecutive runs, but it can vary (e.g. on different days). With a block size of 1, read
hangs on the first byte of some sector. The result is that the program freezes and no signal interrupts the read, ctrl-c simply prints "^C" to the terminal but does not kill the program and the alarm's handler is never called.
Closing the terminal and re-running the program on a new terminal, no read
is completed (it hangs on the first iteration). Only by disconnecting and reconnecting the SSD can I read again from the disk. However, if I disconnect the drive while read
is blocked, the program continues.
Modifying and running the program with stdin as the file descriptor, both SIGINT and SIGALRM interrupt read
.
So the question is:
a) Why does read
block indefinitely since according to the man page it is interrupted by signals?
b) Is there any way to fix this?
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/select.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
void sig_handler(int signum){
printf("Alarm handler\n");
}
int main(int argc, char *argv[]) {
// Register ALARM signal handler to prevent read() from blocking indefinitely
struct sigaction alarm_int_handler = {.sa_handler=sig_handler};
sigaction(SIGALRM, &alarm_int_handler, 0);
char* disk_name = "/dev/sdb";
const int block_size = 512;
int offset = 0;
char block[block_size];
// Open disk to read as binary file via file descriptor
int fd = open(disk_name, O_RDONLY | O_NONBLOCK);
if (fd == -1){
perror(disk_name);
exit(0);
}
int i;
int position = offset;
for (i=0; i<100000; i++){
// Reset alarm to 1 sec (to interrupt blocked read)
alarm(1);
// Seek to current position
int seek_pos = lseek(fd, position, SEEK_SET);
if (seek_pos == -1){
perror("Seek");
}
printf("Reading... ");
fflush(stdout);
int len = read(fd, block, block_size);
printf("Read %d chars at %d\n", len, position);
if (len == -1){
if (errno != EINTR){
perror("Read");
}
else {
printf("Read aborted due to interrupt\n");
// TODO: handle it
}
}
position += len;
}
close(fd);
printf("Position %d (%d)\n", position, i * block_size);
printf("Done\n");
return 0;
}
Output on the terminal looks like this
.
.
.
Reading... Read 1 chars at 29642749
Reading... Read 1 chars at 29642750
Reading... Read 1 chars at 29642751
Reading...