c语言拦截php写入磁盘函数钩子实现服务器文件夹容量限制

Php C

c语言拦截php写入磁盘函数钩子实现服务器文件夹容量限制


centos上限制目录容量大小的办法通常有两种:

1、磁盘配额 (Quota):限制用户或组在分区上的使用上限。需在 /etc/fstab 启用 usrquota,并用 edquota 命令设置限制。

2、目录项目 (Project):针对 XFS 文件系统,使用 xfs_quota 直接限制特定目录的容量,不依赖用户身份。

今天发现一种c语言hook拦截php的write函数来实现,通过每个目录下都有一个限制磁盘大小的文件,这个限制文件php所在运行组不能删除和修改。

写出hook_dir_quota.c

// hook_dir_quota.c
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <limits.h>
#include <stdint.h>
#include <ctype.h>

/* ---------- 配置区 ---------- */
#define MONITOR_DIR         "/var/www/html"   // 监控根目录
#define QUOTA_FILE_NAME     ".size_limit"        // 配额文件名
#define DEFAULT_LIMIT       (1LL * 1024 * 1024)  // 默认 1 MiB

/* ---------- 调试开关 ---------- */
// #define DEBUG 1

/* 原始函数指针 */
typedef int (*orig_open_t)(const char *, int, ...);
typedef int (*orig_openat_t)(int, const char *, int, ...);
typedef int (*orig_creat_t)(const char *, mode_t);

/* ---------- 辅助函数:计算目录总大小(递归) ---------- */
static int64_t get_dir_size(const char *dirpath) {
    DIR *dir;
    struct dirent *entry;
    struct stat st;
    char fullpath[PATH_MAX];
    int64_t total = 0;

    if (!(dir = opendir(dirpath)))
        return -1;

    while ((entry = readdir(dir)) != NULL) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
            continue;

        snprintf(fullpath, sizeof(fullpath), "%s/%s", dirpath, entry->d_name);

        if (lstat(fullpath, &st) == -1)
            continue;

        if (S_ISDIR(st.st_mode)) {
            int64_t sub = get_dir_size(fullpath);
            if (sub > 0) total += sub;
        } else if (S_ISREG(st.st_mode)) {
            total += st.st_size;
        }
    }
    closedir(dir);
    return total;
}

/* ---------- 解析配额文件,返回字节数 ---------- */
static int64_t parse_quota_file(const char *filepath) {
    FILE *fp;
    char line[128];
    int64_t limit = -1;

    fp = fopen(filepath, "r");
    if (!fp) return -1;  // 文件不存在或无法打开

    if (fgets(line, sizeof(line), fp) != NULL) {
        // 去除末尾换行和空白
        char *p = line;
        while (*p && isspace((unsigned char)*p)) p++;  // 跳过前导空白
        // 解析数字
        char *num_end;
        long long val = strtoll(p, &num_end, 10);
        if (num_end == p) {
            fclose(fp);
            return -1;  // 无效数字
        }

        // 跳过后置空白
        while (*num_end && isspace((unsigned char)*num_end)) num_end++;

        // 单位处理(k/m/g,大小写不敏感)
        if (*num_end) {
            switch (tolower((unsigned char)*num_end)) {
                case 'k': val *= 1024LL; break;
                case 'm': val *= 1024LL * 1024LL; break;
                case 'g': val *= 1024LL * 1024LL * 1024LL; break;
                default:  // 未知单位,忽略
                    break;
            }
...

点击查看剩余70%

{{collectdata}}

网友评论0