getdiskfreespaceex 和 driveinfo 不返回用户配额信息,因windows配额是ntfs卷级sid策略;需用wmi(win32_quotasetting/win32_diskquota)或netapi32查询,且须管理员权限。

Windows 下用 GetDiskFreeSpaceEx 只能看总量,不返回配额
很多人调用 GetDiskFreeSpaceEx 或 DriveInfo 后发现 AvailableFreeSpace 和磁盘实际“剩余额度”对不上——不是 bug,是它压根不感知用户配额。Windows 配额是 NTFS 卷级策略,按用户 SID 限制,和卷总空间无关。
真正要查配额,得走 WMI 或 Win32 API 的 Win32_QuotaSetting + Win32_DiskQuota。但注意:Win32_DiskQuota 在 Windows 10/11 默认禁用,且仅对启用了配额的 NTFS 卷有效。
- 必须以管理员权限运行,否则查询返回空或拒绝访问
-
Win32_DiskQuota的Limit和Usage字段单位是字节,不是 KB/MB,别直接除 1024 搞错量级 - 查当前用户配额,得先用
WindowsIdentity.GetCurrent().User.Value拿 SID,再匹配QuotaEntry的UserId
Linux 下没有统一“用户磁盘配额”API,靠 quotactl 系统调用
.NET 6+ 的 System.IO.DriveInfo 在 Linux 上完全忽略配额——它只读 /proc/mounts 和 statvfs,而配额信息存在 /aquota.user 或 /quota.user 文件里,需用 quotactl。
C# 没有内置封装,得 P/Invoke libc 的 quotactl 函数。参数极易出错:比如 cmd 传 QCMD(Q_GETQUOTA, USRQUOTA) 而不是硬编码数字;id 是 uid_t 类型(32 位),不是 .NET 的 int(可能溢出)。
- 必须确保文件系统挂载时带
usrquota或grpquota选项,否则quotactl返回ENOSYS - 目标路径得是挂载点(如
/home),不是任意目录;传/home/user会失败 - struct
dqblk的字段顺序、填充字节与 libc 版本强相关,建议用Marshal.SizeOf<dqblk>()</dqblk>校验,别手写偏移
跨平台抽象层别硬造,配额逻辑天然不兼容
试图写一个 IQuotaProvider.GetQuota(string path, string user) 并让它在 Windows/Linux 都工作,大概率掉坑里。两边模型根本不同:Windows 配额绑定 SID + 卷,Linux 绑定 uid + 挂载点 + quota 文件位置,且 Linux 还分 v1/v2 quota 格式。
更现实的做法是分环境实现,用 #if WINDOWS / #if LINUX 隔离。别把配额当“文件系统属性”去泛化,它其实是操作系统策略子系统的一部分。
- Windows 侧优先用 WMI(可读性强),性能差就换
NetApi32.dll的NetUserGetInfo查配额状态(但不返回用量) - Linux 侧不要依赖
quota命令行工具输出——格式随 locale 变,解析不可靠;坚持用quotactl - 没配额时,Windows 返回
Limit = 0,Linux 返回dqb_bsoftlimit = 0,别当成“无限”,得结合dqb_bhardlimit判断是否启用
权限和错误处理比逻辑本身更关键
配额查询失败,90% 不是代码写错,而是权限或配置问题。Windows 报 UnauthorizedAccessException,大概率没管理员权限;Linux 报 EPERM,八成是没开 quota 或进程没 CAP_SYS_ADMIN。
别在 catch 里吞掉异常然后返回 “0 bytes available”。真实场景中,配额未启用、服务未启动、路径不在配额卷上,都该区分反馈。
- Windows:检查
Win32_QuotaSetting.QuotaState == true再查配额项,否则直接跳过 - Linux:先
access("/path/aquota.user", F_OK)确认 quota 文件存在,再调quotactl - 所有路径输入必须做
Path.GetFullPath归一化,Linux 下/home/../home可能绕过挂载点判断
配额不是文件系统自带能力,它是额外开启的策略控制模块。查不到数据时,先确认它是否真的被启用,而不是急着改代码。









