Skip to content

Instantly share code, notes, and snippets.

@gkbrk
Last active January 15, 2022 00:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gkbrk/cbab5c8840bec9f8c415542a1276ebc1 to your computer and use it in GitHub Desktop.
Save gkbrk/cbab5c8840bec9f8c415542a1276ebc1 to your computer and use it in GitHub Desktop.
Thanos-snap half of your files out of existence
#define _XOPEN_SOURCE 500
#include <assert.h>
#include <dirent.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
/* Random number generator */
static uint64_t
rotl64(uint64_t n, uint8_t k)
{
uint64_t a = n << k;
uint64_t b = n >> (64 - k);
return a | b;
}
static uint64_t
rxprime64(uint64_t x)
{
x *= 7919;
x ^= rotl64(x, 7);
x *= 7723;
x ^= rotl64(x, 11);
x *= 7561;
x ^= rotl64(x, 13);
x *= 7411;
x ^= rotl64(x, 17);
return x;
}
static uint64_t rng_state;
static void
rng_absorb(const void* ptr, size_t n)
{
size_t i;
const char* buf = ptr;
for (i = 0; i < n; i++)
rng_state = rxprime64(rng_state ^ buf[i]);
}
static uint64_t
rng_next()
{
uint64_t n;
rng_state = rxprime64(rng_state);
n = rng_state & 0xFFFFFFFF00000000;
rng_state = rxprime64(rng_state);
n |= (rng_state & 0xFFFFFFFF00000000) >> 32;
return n;
}
static void
init_rng()
{
time_t t = time(NULL);
pid_t pid;
rng_absorb(&t, sizeof(t));
pid = getpid();
rng_absorb(&pid, sizeof(pid));
pid = getppid();
rng_absorb(&pid, sizeof(pid));
}
/* Dynamic file list */
typedef struct
{
size_t count;
char** files;
} FileList;
FileList file_list;
void
add_file(FileList* fl, const char* path)
{
fl->count++;
fl->files = realloc(fl->files, fl->count * sizeof(char*));
fl->files[fl->count - 1] = strdup(path);
}
void
shuffle_files(FileList* fl)
{
size_t i;
size_t j;
char* tmp;
for (i = fl->count - 1; i > 0; i--) {
j = rng_next() % i;
tmp = fl->files[i];
fl->files[i] = fl->files[j];
fl->files[j] = tmp;
}
}
bool
streq(const char* s1, const char* s2)
{
return strcmp(s1, s2) == 0;
}
size_t
path_mode(const char* path)
{
struct stat st = { 0 };
int res = stat(path, &st);
if (res != 0) {
printf("Could not stat file %s\n", path);
return 0;
}
return st.st_mode;
}
bool
is_file(const char* path)
{
size_t mode = path_mode(path);
return S_ISREG(mode);
}
bool
is_dir(const char* path)
{
size_t mode = path_mode(path);
return S_ISDIR(mode);
}
void
recursive_ls(const char* path)
{
DIR* dir;
struct dirent* de;
char* filepath;
size_t path_len = strlen(path);
dir = opendir(path);
if (dir == NULL) {
printf("Could not open directory %s\n", path);
return;
}
while (true) {
de = readdir(dir);
if (de == NULL)
break;
if (streq(de->d_name, ".") || streq(de->d_name, ".."))
continue;
/* Build a path string, if it's a dir, recurse, else print */
/* +2 comes from the path separator and the terminating null byte.
*/
filepath = calloc(1, path_len + strlen(de->d_name) + 2);
strcat(filepath, path);
strcat(filepath, "/");
strcat(filepath, de->d_name);
if (is_dir(filepath)) {
recursive_ls(filepath);
} else if (is_file(filepath)) {
add_file(&file_list, filepath);
}
free(filepath);
}
closedir(dir);
}
int
main(int argc, char** argv)
{
size_t i;
bool really_do_it = false;
if (argc == 2 && streq(argv[1], "--really-do-it")) {
really_do_it = true;
} else {
puts("Dry run, pass --really-do-it to do it for real");
}
init_rng();
recursive_ls(".");
shuffle_files(&file_list);
printf("Found %ld files, going to remove %ld of them\n",
file_list.count,
file_list.count / 2);
for (i = 0; i < file_list.count / 2; i++) {
printf("The file %s has turned to dust\n", file_list.files[i]);
if (really_do_it)
unlink(file_list.files[i]);
}
/* Free all the file paths */
for (i = 0; i < file_list.count; i++)
free(file_list.files[i]);
free(file_list.files);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment