3
\$\begingroup\$

The idea here is to destroy the data in a directory tree by finding any files in it and overwriting their data with garbage before deleting them. The function returns minus the number of errors encountered, or zero if successful.

Obliterate.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
static char B[FILENAME_MAX];
static int RM(const size_t len) {
    struct stat st;
    if (stat(B, &st))
        return -1;
    if (S_ISDIR(st.st_mode)) {
        DIR *const dir = opendir(B);
        if (!dir)
            return -1;
        B[len] = '/';
        for (struct dirent *dirent; (dirent = readdir(dir));) {
            if (memcmp(dirent->d_name, ".", 2) && memcmp(dirent->d_name, "..", 3)) {
                const size_t namelen = strlen(dirent->d_name);
                char *const ptr = (char *)memcpy(B+len+1, dirent->d_name, namelen)+namelen;
                *ptr = 0;
                const int tmp = RM(ptr-B);
                if (tmp)
                    return closedir(dir)+tmp-1;
            }
        }
        B[len] = 0;
        if (closedir(dir))
            return -1;
    } else if (S_ISREG(st.st_mode)) {
        const int fd = open(B, O_WRONLY);
        if (fd == -1)
            return -1;
        for (char buf[BUFSIZ]; st.st_size > 0; st.st_size -= BUFSIZ) {
            const size_t size = st.st_size > BUFSIZ ? BUFSIZ : st.st_size;
            arc4random_buf(buf, size);
            if (write(fd, buf, size) < size)
                return close(fd)-1;
        }
        if (close(fd))
            return -1;
    }
    return remove(B);
}
int main(const int argc, const char *const *argv) {
    while (*++argv) {
        const size_t len = strlen(*argv);
        memcpy(B, *argv, len);
        if (RM(len))
            perror(*argv);
    }
    return 0;
}
\$\endgroup\$
2
  • 1
    \$\begingroup\$ You do know that overwriting like shred is only effective for filesystems that write file data in-place, without journalling, on disks with no translation layer (i.e. not most SSDs today)? \$\endgroup\$ Commented Jan 23, 2023 at 20:49
  • 1
    \$\begingroup\$ I suggest that you read blancco.com/resources/… ; this is a solved problem and unfortunately yours is very unlikely to be an acceptable solution \$\endgroup\$
    – Reinderien
    Commented Jan 24, 2023 at 0:42

1 Answer 1

4
\$\begingroup\$

The code is very dense, to the extent that it harms readability. I'm not a fan of variable names such as B, especially for globals. We could do with better layout and more comments. And I recommend always using compound statements with loops and conditionals, even when the body is a single statement and {…} aren't strictly required.

Compilation warns that there's no visible definition of arc4random_buf(). I strongly urge you to require prototypes for all functions that are used rather than fallback back to "implicit int".

I would use strcmp() rather than memcmp() with hand-crafted lengths when testing for . and .., to more clearly convey intent and to eliminate even the possibility of mismatch. Similarly, prefer strcpy() (or strncpy) to memcpy when copying arguments or directory entries into B.

memcpy() returns a void*, so there's no need to cast to assign it to a char* variable.

We have no defence against overflowing the end of B. FILENAME_MAX is not a guarantee that we can't create longer path names! Consider changing directory to keep pathnames relative and avoid the need for copying altogether.

Error messages aren't particularly informative: perror(*argv) shows only the argument at the root of the failure, but users are more likely to be interested in the actual file where the problem occurred.

Opening a file with O_WRONLY may well cause the file to be truncated on the first write, and new storage allocated - there's no expectation that the new contents will overwrite the disk blocks previously allocated to the file. In any case, overwriting like shred is only effective for filesystems that write file data in-place, without journalling, on disks with no translation layer (i.e. not most SSDs today). Even on files where that expectation is valid, I would lean towards mmap() for in-place overwriting.

\$\endgroup\$
2
  • \$\begingroup\$ On my implementation (MacOS), arc4random_buf() seems to be included in <stdlib.h> \$\endgroup\$
    – CPlus
    Commented Jan 23, 2023 at 23:49
  • \$\begingroup\$ That's evidently a platform-specific extension, and not part of standard C or POSIX. Not sure if there's a portable way to get that function. \$\endgroup\$ Commented Jan 24, 2023 at 7:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.