* Makefile.in (DLL_OFILES): Add quotactl.o.
* common.din (quotactl): Export. * ntdll.h: Define FILE_FS_CONTROL_INFORMATION::FileSystemControlFlags flag values. (struct _FILE_FS_CONTROL_INFORMATION): Define. (struct _FILE_GET_QUOTA_INFORMATION): Define. (typedef struct _FILE_QUOTA_INFORMATION): Define. (NtQueryObject): Use PVOID rather than VOID*. (NtQueryVolumeInformationFile): Ditto. (NtQueryQuotaInformationFile): Declare. (NtSetQuotaInformationFile): Declare. (NtSetVolumeInformationFile): Declare. * quotactl.cc: New file implementing quotactl(). * include/sys/mount.h (BLOCK_SIZE): Define. (BLOCK_SIZE_BITS): Define. * include/sys/quota.h: New header. * include/cygwin/version.h (CYGWIN_VERSION_API_MINOR): Bump.
This commit is contained in:
340
winsup/cygwin/quotactl.cc
Normal file
340
winsup/cygwin/quotactl.cc
Normal file
@@ -0,0 +1,340 @@
|
||||
/* quotactl.cc: code for manipulating disk quotas
|
||||
|
||||
Copyright 2014 Red Hat, Inc.
|
||||
|
||||
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;
|
||||
cygsid sid;
|
||||
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;
|
||||
}
|
||||
/*FALLTHRU*/
|
||||
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;
|
||||
/*FALLTHRU*/
|
||||
case Q_GETQUOTA:
|
||||
/* Windows feature: Default limits. Get or set them with id == -1. */
|
||||
if (id != -1)
|
||||
{
|
||||
struct passwd *pw = NULL;
|
||||
struct group *gr = NULL;
|
||||
|
||||
if (type == USRQUOTA)
|
||||
pw = internal_getpwuid (id);
|
||||
else
|
||||
gr = internal_getgrgid (id);
|
||||
if (pw)
|
||||
sid.getfrompw (pw);
|
||||
else if (gr)
|
||||
sid.getfromgr (gr);
|
||||
else
|
||||
{
|
||||
set_errno (EINVAL);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
set_errno (EINVAL);
|
||||
return -1;
|
||||
}
|
||||
/* Check path */
|
||||
pc.check (special, PC_SYM_FOLLOW | PC_NOWARN, stat_suffixes);
|
||||
if (pc.error)
|
||||
{
|
||||
set_errno (pc.error);
|
||||
return -1;
|
||||
}
|
||||
if (!pc.exists ())
|
||||
{
|
||||
set_errno (ENOENT);
|
||||
return -1;
|
||||
}
|
||||
if (!S_ISBLK (pc.dev.mode))
|
||||
{
|
||||
set_errno (ENOTBLK);
|
||||
return -1;
|
||||
}
|
||||
pc.get_object_attr (attr, sec_none_nih);
|
||||
/* For the following functions to work, we must attach the virtual path to
|
||||
the quota file to the device path.
|
||||
|
||||
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;
|
||||
break;
|
||||
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:
|
||||
__try
|
||||
{
|
||||
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;
|
||||
break;
|
||||
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;
|
||||
}
|
Reference in New Issue
Block a user