From b579e9a5aa7bf5dbeb20e2fedb6eb743b305a79d Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Sat, 2 Nov 2019 15:21:09 +0200 Subject: Add old code --- fast-rm-rf.c | 173 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 fast-rm-rf.c (limited to 'fast-rm-rf.c') diff --git a/fast-rm-rf.c b/fast-rm-rf.c new file mode 100644 index 0000000..c8665a8 --- /dev/null +++ b/fast-rm-rf.c @@ -0,0 +1,173 @@ +/* + * fast-rm-rf.c - remove files and directories really fast but unsafely + * Copyright 2011 Lars Wirzenius + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * GNU Coreutils has an rm implementation that is not as fast as it should + * be. It is, for example, slower than "find -delete", according to my + * benchmarks: http://blog.liw.fi/posts/fileops-benchmark/ . + * + * This program is an attempt to make a program that does the equivalent + * of "rm -rf" as quickly as possible for Linux. Some points: + * + * - use getdents instead of opendir/readdir/closedir + * - further, use getdents with a fixed size buffer of reasonable size, + * and remove as much as you get, then iterate if you didn't get + * everything; this seems to be faster than trying to get everything + * in one getdents call for very large directories + * - don't use stat to see if something is a directory; use unlink(2) + * and check errno for EISDIR instead + * - avoid any other unnecessary system calls, too + * + * Screw portability for this program. + * + * Ideas for further improvement: + * + * - use threading: at least one for getdents, at least one for unlink + */ + + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* There seems to be no header that declares this. */ +struct linux_dirent { + long d_ino; + off_t d_off; + unsigned short d_reclen; + char d_name[]; +}; + + +/* The size of the buffer for getdents. */ +#define MAXBUF (1024U * 1024U) + + +/* We have no settings, but we want --help to work. */ +static int help; +static struct option options[] = { + { "help", 0, &help, 1 }, + { NULL }, +}; + + +static void usage(const char *argv0) +{ + printf("usage: %s [--help] FILE...\n", argv0); +} + + + +/* Remove a file or directory (recursively). */ +static int fast_remove(const char *filename) +{ + /* Most things are non-directories. Can we just remove it? */ + if (unlink(filename) == 0) + return 0; + + /* Did it fail because it's a directory? If not, abort. */ + if (errno != EISDIR) { + perror(filename); + return -1; + } + + /* Read some filenames in the directory, remove them, then repeat. */ + size_t filename_len = strlen(filename); + bool got_any; + do { + /* + * Read filenames using getdents. It is not wrapped in a function, + * so we have to call via syscall(2). + */ + char buf[MAXBUF]; + int fd = open(filename, O_RDONLY); + int n = syscall(SYS_getdents, fd, buf, MAXBUF); + close(fd); + if (n == -1) { + perror(filename); + return -1; + } + + /* Iterate over what getdents returned. */ + int ret = 0; + got_any = false; + struct linux_dirent *d; + for (char *pos = buf; pos < buf + n; pos += d->d_reclen) { + d = (void *) pos; + + /* Skip . and .. since we really can't remove them. */ + if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) + continue; + got_any = true; + + /* Construct pathname to file in directory. */ + size_t name_len = strlen(d->d_name); + char child[filename_len + 1 + name_len + 1]; + memcpy(child, filename, filename_len); + child[filename_len] = '/'; + memcpy(child + filename_len + 1, d->d_name, name_len); + child[filename_len + 1 + name_len] = '\0'; + + /* Recurse. Remember failures, but remove everything we can. */ + if (fast_remove(child) == -1) + ret = -1; + } + + /* Did we fail? */ + if (ret != 0) + return -1; + } while (got_any); + + /* Remove the directory itself. */ + if (rmdir(filename) == -1) { + perror(filename); + return -1; + } + + return 0; +} + + +int main(int argc, char **argv) +{ + while (getopt_long(argc, argv, "", options, NULL) != -1) + continue; + + if (help) { + usage(argv[0]); + return 0; + } + + int ret = 0; + for (int i = optind; i < argc; ++i) { + if (fast_remove(argv[i]) == -1) + ret = 1; + } + return ret; +} -- cgit v1.2.1