#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <limits.h>
#include <errno.h>

#define TARGET_SIZE ((off_t)8 * 1024 * 1024 * 1024)  // 8GB

static off_t get_size(int fd)
{
    struct stat st;

    if (fstat(fd, &st) == 0 && st.st_size > 0)
        return st.st_size;

    off_t end = lseek(fd, 0, SEEK_END);

    if (end >= 0)
        lseek(fd, 0, SEEK_SET);

    return end;
}

static void check_fd(const char *pid, const char *fdname)
{
    char path[PATH_MAX];
    char linktarget[PATH_MAX];
    char symlinkname[PATH_MAX];

    snprintf(path, sizeof(path),
             "/proc/%s/fd/%s",
             pid, fdname);

    ssize_t len = readlink(path,
                            linktarget,
                            sizeof(linktarget) - 1);

    if (len < 0)
        return;

    linktarget[len] = '\0';

    int fd = open(path, O_RDONLY | O_CLOEXEC);

    if (fd < 0)
        return;

    off_t size = get_size(fd);

    close(fd);

    printf("checking %s : size = %lld\n", fdname, (long long)size);

    if (size == TARGET_SIZE)
    {
        snprintf(symlinkname,
                 sizeof(symlinkname),
                 "/dev/shm/baboon_%s",
                 fdname);

        // remove existing symlink if present
        unlink(symlinkname);

        if (symlink(path, symlinkname) == 0)
        {
            printf("[+] matched 8GB fd %s -> %s\n",
                   fdname,
                   symlinkname);
        }
        else
        {
            perror("symlink");
        }
    }
}

int main(int argc, char **argv)
{
    if (argc != 2)
    {
        fprintf(stderr,
                "usage: %s <pid>\n",
                argv[0]);
        return 1;
    }

    const char *pid = argv[1];

    char dirpath[PATH_MAX];
    snprintf(dirpath, sizeof(dirpath),
             "/proc/%s/fd",
             pid);

    DIR *dir = opendir(dirpath);

    if (!dir)
    {
        perror("opendir");
        return 1;
    }

    struct dirent *de;

    while ((de = readdir(dir)) != NULL)
    {
        if (de->d_name[0] == '.')
            continue;

        check_fd(pid, de->d_name);
    }

    closedir(dir);

    return 0;
}
