2014-10-17 16:19:27 +08:00
|
|
|
/* quotactl.cc: code for manipulating disk quotas
|
|
|
|
|
|
|
|
This file is part of Cygwin.
|
|
|
|
|
|
|
|
This software is a copyrighted work licensed under the terms of the
|
|
|
|
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
|
|
|
|
details. */
|
|
|
|
|
|
|
|
#include "winsup.h"
|
|
|
|
#include "cygtls.h"
|
|
|
|
#include "security.h"
|
|
|
|
#include "path.h"
|
|
|
|
#include "fhandler.h"
|
|
|
|
#include "dtable.h"
|
|
|
|
#include "cygheap.h"
|
|
|
|
#include "ntdll.h"
|
|
|
|
#include "tls_pbuf.h"
|
|
|
|
#include <sys/mount.h>
|
|
|
|
#include <sys/quota.h>
|
|
|
|
|
|
|
|
#define PGQI_SIZE (sizeof (FILE_GET_QUOTA_INFORMATION) + SECURITY_MAX_SID_SIZE)
|
|
|
|
#define PFQI_SIZE (sizeof (FILE_QUOTA_INFORMATION) + SECURITY_MAX_SID_SIZE)
|
|
|
|
|
|
|
|
/* Modelled after the Linux quotactl function. */
|
|
|
|
extern "C" int
|
|
|
|
quotactl (int cmd, const char *special, int id, caddr_t addr)
|
|
|
|
{
|
|
|
|
ACCESS_MASK access = FILE_READ_DATA;
|
2015-04-08 17:00:08 +08:00
|
|
|
PSID sid = NO_SID;
|
2014-10-17 16:19:27 +08:00
|
|
|
path_conv pc;
|
|
|
|
tmp_pathbuf tp;
|
|
|
|
UNICODE_STRING path;
|
|
|
|
OBJECT_ATTRIBUTES attr;
|
|
|
|
NTSTATUS status;
|
|
|
|
HANDLE fh;
|
|
|
|
IO_STATUS_BLOCK io;
|
|
|
|
FILE_FS_CONTROL_INFORMATION ffci;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
uint32_t subcmd = (uint32_t) cmd >> SUBCMDSHIFT;
|
|
|
|
uint32_t type = (uint32_t) cmd & SUBCMDMASK;
|
|
|
|
|
|
|
|
if (type != USRQUOTA && type != GRPQUOTA)
|
|
|
|
{
|
|
|
|
set_errno (EINVAL);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
switch (subcmd)
|
|
|
|
{
|
|
|
|
case Q_SYNC:
|
|
|
|
if (!special)
|
|
|
|
return 0;
|
|
|
|
access |= FILE_WRITE_DATA;
|
|
|
|
break;
|
|
|
|
case Q_QUOTAON:
|
|
|
|
if (id < QFMT_VFS_OLD || id > QFMT_VFS_V1)
|
|
|
|
{
|
|
|
|
set_errno (EINVAL);
|
|
|
|
return -1;
|
|
|
|
}
|
2020-08-06 03:58:22 +08:00
|
|
|
fallthrough;
|
2014-10-17 16:19:27 +08:00
|
|
|
case Q_QUOTAOFF:
|
|
|
|
case Q_SETINFO:
|
|
|
|
access |= FILE_WRITE_DATA;
|
|
|
|
break;
|
|
|
|
case Q_GETFMT:
|
|
|
|
case Q_GETINFO:
|
|
|
|
break;
|
|
|
|
case Q_SETQUOTA:
|
|
|
|
access |= FILE_WRITE_DATA;
|
2020-08-06 03:58:22 +08:00
|
|
|
fallthrough;
|
2014-10-17 16:19:27 +08:00
|
|
|
case Q_GETQUOTA:
|
|
|
|
/* Windows feature: Default limits. Get or set them with id == -1. */
|
|
|
|
if (id != -1)
|
|
|
|
{
|
|
|
|
if (type == USRQUOTA)
|
2015-04-08 17:00:08 +08:00
|
|
|
sid = sidfromuid (id, NULL);
|
2014-10-17 16:19:27 +08:00
|
|
|
else
|
2015-04-08 17:00:08 +08:00
|
|
|
sid = sidfromgid (id, NULL);
|
|
|
|
if (sid == NO_SID)
|
2014-10-17 16:19:27 +08:00
|
|
|
{
|
|
|
|
set_errno (EINVAL);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
set_errno (EINVAL);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
/* Check path */
|
2020-01-29 04:59:22 +08:00
|
|
|
pc.check (special, PC_SYM_FOLLOW, stat_suffixes);
|
2014-10-17 16:19:27 +08:00
|
|
|
if (pc.error)
|
|
|
|
{
|
|
|
|
set_errno (pc.error);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (!pc.exists ())
|
|
|
|
{
|
|
|
|
set_errno (ENOENT);
|
|
|
|
return -1;
|
|
|
|
}
|
2016-06-23 22:56:41 +08:00
|
|
|
if (!S_ISBLK (pc.dev.mode ()))
|
2014-10-17 16:19:27 +08:00
|
|
|
{
|
|
|
|
set_errno (ENOTBLK);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
pc.get_object_attr (attr, sec_none_nih);
|
2020-03-11 20:36:41 +08:00
|
|
|
/* For the following functions to work, we must attach the virtual path to
|
2014-10-17 16:19:27 +08:00
|
|
|
the quota file to the device path.
|
2020-03-11 20:28:27 +08:00
|
|
|
|
2014-10-17 16:19:27 +08:00
|
|
|
FIXME: Note that this is NTFS-specific. Adding ReFS in another step. */
|
|
|
|
tp.u_get (&path);
|
|
|
|
RtlCopyUnicodeString (&path, attr.ObjectName);
|
|
|
|
RtlAppendUnicodeToString (&path, L"\\$Extend\\$Quota:$Q:$INDEX_ALLOCATION");
|
|
|
|
attr.ObjectName = &path;
|
|
|
|
|
|
|
|
/* Open filesystem */
|
|
|
|
status = NtOpenFile (&fh, access, &attr, &io, FILE_SHARE_VALID_FLAGS, 0);
|
|
|
|
if (NT_SUCCESS (status))
|
|
|
|
switch (subcmd)
|
|
|
|
{
|
|
|
|
case Q_SYNC:
|
|
|
|
/* No sync, just report success. */
|
|
|
|
status = STATUS_SUCCESS;
|
2020-03-11 20:23:55 +08:00
|
|
|
break;
|
2014-10-17 16:19:27 +08:00
|
|
|
case Q_QUOTAON:
|
|
|
|
case Q_QUOTAOFF:
|
|
|
|
/* Ignore filename in addr. */
|
|
|
|
status = NtQueryVolumeInformationFile (fh, &io, &ffci, sizeof ffci,
|
|
|
|
FileFsControlInformation);
|
|
|
|
if (!NT_SUCCESS (status))
|
|
|
|
break;
|
|
|
|
ffci.FileSystemControlFlags &= ~FILE_VC_QUOTA_ENFORCE
|
|
|
|
& ~FILE_VC_QUOTA_TRACK
|
|
|
|
& ~FILE_VC_QUOTAS_INCOMPLETE
|
|
|
|
& ~FILE_VC_QUOTAS_REBUILDING;
|
|
|
|
if (subcmd == Q_QUOTAON)
|
|
|
|
ffci.FileSystemControlFlags |= FILE_VC_QUOTA_ENFORCE;
|
|
|
|
status = NtSetVolumeInformationFile (fh, &io, &ffci, sizeof ffci,
|
|
|
|
FileFsControlInformation);
|
|
|
|
break;
|
|
|
|
case Q_GETFMT:
|
2020-03-11 20:23:55 +08:00
|
|
|
__try
|
2014-10-17 16:19:27 +08:00
|
|
|
{
|
|
|
|
uint32_t *retval = (uint32_t *) addr;
|
|
|
|
|
|
|
|
/* Always fake the latest format. */
|
|
|
|
*retval = QFMT_VFS_V1;
|
|
|
|
}
|
|
|
|
__except (EFAULT)
|
|
|
|
{
|
|
|
|
ret = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
__endtry
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
break;
|
|
|
|
case Q_GETINFO:
|
|
|
|
__try
|
|
|
|
{
|
|
|
|
struct dqinfo *dqi = (struct dqinfo *) addr;
|
|
|
|
|
|
|
|
dqi->dqi_bgrace = dqi->dqi_igrace = UINT64_MAX;
|
|
|
|
dqi->dqi_flags = 0;
|
|
|
|
dqi->dqi_valid = IIF_BGRACE | IIF_IGRACE;
|
|
|
|
}
|
|
|
|
__except (EFAULT)
|
|
|
|
{
|
|
|
|
ret = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
__endtry
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
break;
|
|
|
|
case Q_SETINFO:
|
|
|
|
/* No settings possible, just report success. */
|
|
|
|
status = STATUS_SUCCESS;
|
2020-03-11 20:23:55 +08:00
|
|
|
break;
|
2014-10-17 16:19:27 +08:00
|
|
|
case Q_GETQUOTA:
|
|
|
|
/* Windows feature: Default limits. Get or set them with id == -1. */
|
|
|
|
if (id == -1)
|
|
|
|
{
|
|
|
|
status = NtQueryVolumeInformationFile (fh, &io, &ffci, sizeof ffci,
|
|
|
|
FileFsControlInformation);
|
|
|
|
if (!NT_SUCCESS (status))
|
|
|
|
break;
|
|
|
|
__try
|
|
|
|
{
|
|
|
|
struct dqblk *dq = (struct dqblk *) addr;
|
|
|
|
|
|
|
|
dq->dqb_bhardlimit = (uint64_t) ffci.DefaultQuotaLimit.QuadPart;
|
|
|
|
if (dq->dqb_bhardlimit != UINT64_MAX)
|
|
|
|
dq->dqb_bhardlimit /= BLOCK_SIZE;
|
|
|
|
dq->dqb_bsoftlimit =
|
|
|
|
(uint64_t) ffci.DefaultQuotaThreshold.QuadPart;
|
|
|
|
if (dq->dqb_bsoftlimit != UINT64_MAX)
|
|
|
|
dq->dqb_bsoftlimit /= BLOCK_SIZE;
|
|
|
|
dq->dqb_curspace = 0;
|
|
|
|
dq->dqb_ihardlimit = UINT64_MAX;
|
|
|
|
dq->dqb_isoftlimit = UINT64_MAX;
|
|
|
|
dq->dqb_curinodes = 0;
|
|
|
|
dq->dqb_btime = UINT64_MAX;
|
|
|
|
dq->dqb_itime = UINT64_MAX;
|
|
|
|
dq->dqb_valid = QIF_BLIMITS;
|
|
|
|
}
|
|
|
|
__except (EFAULT)
|
|
|
|
{
|
|
|
|
ret = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
__endtry
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
PFILE_GET_QUOTA_INFORMATION pgqi = (PFILE_GET_QUOTA_INFORMATION)
|
|
|
|
alloca (PGQI_SIZE);
|
|
|
|
PFILE_QUOTA_INFORMATION pfqi = (PFILE_QUOTA_INFORMATION)
|
|
|
|
alloca (PFQI_SIZE);
|
|
|
|
|
|
|
|
pgqi->NextEntryOffset = 0;
|
|
|
|
pgqi->SidLength = RtlLengthSid (sid);
|
|
|
|
RtlCopySid (RtlLengthSid (sid), &pgqi->Sid, sid);
|
|
|
|
status = NtQueryQuotaInformationFile (fh, &io, pfqi, PFQI_SIZE,
|
|
|
|
TRUE, pgqi, PGQI_SIZE,
|
|
|
|
NULL, TRUE);
|
|
|
|
if (!NT_SUCCESS (status))
|
|
|
|
break;
|
|
|
|
__try
|
|
|
|
{
|
|
|
|
struct dqblk *dq = (struct dqblk *) addr;
|
|
|
|
|
|
|
|
dq->dqb_bhardlimit = (uint64_t) pfqi->QuotaLimit.QuadPart;
|
|
|
|
if (dq->dqb_bhardlimit != UINT64_MAX)
|
|
|
|
dq->dqb_bhardlimit /= BLOCK_SIZE;
|
|
|
|
dq->dqb_bsoftlimit = (uint64_t) pfqi->QuotaThreshold.QuadPart;
|
|
|
|
if (dq->dqb_bsoftlimit != UINT64_MAX)
|
|
|
|
dq->dqb_bsoftlimit /= BLOCK_SIZE;
|
|
|
|
dq->dqb_curspace = (uint64_t) pfqi->QuotaUsed.QuadPart;
|
|
|
|
if (dq->dqb_curspace != UINT64_MAX)
|
|
|
|
dq->dqb_curspace /= BLOCK_SIZE;
|
|
|
|
dq->dqb_ihardlimit = UINT64_MAX;
|
|
|
|
dq->dqb_isoftlimit = UINT64_MAX;
|
|
|
|
dq->dqb_curinodes = 0;
|
|
|
|
dq->dqb_btime = UINT64_MAX;
|
|
|
|
dq->dqb_itime = UINT64_MAX;
|
|
|
|
dq->dqb_valid = QIF_BLIMITS | QIF_SPACE;
|
|
|
|
}
|
|
|
|
__except (EFAULT)
|
|
|
|
{
|
|
|
|
ret = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
__endtry
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Q_SETQUOTA:
|
|
|
|
/* Windows feature: Default limits. Get or set them with id == -1. */
|
|
|
|
if (id == -1)
|
|
|
|
{
|
|
|
|
status = NtQueryVolumeInformationFile (fh, &io, &ffci, sizeof ffci,
|
|
|
|
FileFsControlInformation);
|
|
|
|
if (!NT_SUCCESS (status))
|
|
|
|
break;
|
|
|
|
__try
|
|
|
|
{
|
|
|
|
struct dqblk *dq = (struct dqblk *) addr;
|
|
|
|
|
|
|
|
if (!(dq->dqb_valid & QIF_BLIMITS))
|
|
|
|
break;
|
|
|
|
ffci.DefaultQuotaLimit.QuadPart = dq->dqb_bhardlimit;
|
|
|
|
if (ffci.DefaultQuotaLimit.QuadPart != -1)
|
|
|
|
ffci.DefaultQuotaLimit.QuadPart *= BLOCK_SIZE;
|
|
|
|
ffci.DefaultQuotaThreshold.QuadPart = dq->dqb_bsoftlimit;
|
|
|
|
if (ffci.DefaultQuotaThreshold.QuadPart != -1)
|
|
|
|
ffci.DefaultQuotaThreshold.QuadPart *= BLOCK_SIZE;
|
|
|
|
}
|
|
|
|
__except (EFAULT)
|
|
|
|
{
|
|
|
|
ret = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
__endtry
|
|
|
|
status = NtSetVolumeInformationFile (fh, &io, &ffci, sizeof ffci,
|
|
|
|
FileFsControlInformation);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
PFILE_GET_QUOTA_INFORMATION pgqi = (PFILE_GET_QUOTA_INFORMATION)
|
|
|
|
alloca (PGQI_SIZE);
|
|
|
|
PFILE_QUOTA_INFORMATION pfqi = (PFILE_QUOTA_INFORMATION)
|
|
|
|
alloca (PFQI_SIZE);
|
|
|
|
|
|
|
|
pgqi->NextEntryOffset = 0;
|
|
|
|
pgqi->SidLength = RtlLengthSid (sid);
|
|
|
|
RtlCopySid (RtlLengthSid (sid), &pgqi->Sid, sid);
|
|
|
|
status = NtQueryQuotaInformationFile (fh, &io, pfqi, PFQI_SIZE,
|
|
|
|
TRUE, pgqi, PGQI_SIZE,
|
|
|
|
NULL, TRUE);
|
|
|
|
if (!NT_SUCCESS (status))
|
|
|
|
break;
|
|
|
|
__try
|
|
|
|
{
|
|
|
|
struct dqblk *dq = (struct dqblk *) addr;
|
|
|
|
|
|
|
|
if (!(dq->dqb_valid & QIF_BLIMITS))
|
|
|
|
break;
|
|
|
|
pfqi->QuotaLimit.QuadPart = dq->dqb_bhardlimit;
|
|
|
|
if (pfqi->QuotaLimit.QuadPart != -1)
|
|
|
|
pfqi->QuotaLimit.QuadPart *= BLOCK_SIZE;
|
|
|
|
pfqi->QuotaThreshold.QuadPart = dq->dqb_bsoftlimit;
|
|
|
|
if (pfqi->QuotaThreshold.QuadPart != -1)
|
|
|
|
pfqi->QuotaThreshold.QuadPart *= BLOCK_SIZE;
|
|
|
|
}
|
|
|
|
__except (EFAULT)
|
|
|
|
{
|
|
|
|
ret = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
__endtry
|
|
|
|
status = NtSetQuotaInformationFile (fh, &io, pfqi, PFQI_SIZE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!NT_SUCCESS (status))
|
|
|
|
{
|
|
|
|
__seterrno_from_nt_status (status);
|
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|