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%
网友评论0