summaryrefslogtreecommitdiffstats
path: root/winsup/cygwin/quotactl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'winsup/cygwin/quotactl.cc')
-rw-r--r--winsup/cygwin/quotactl.cc340
1 files changed, 340 insertions, 0 deletions
diff --git a/winsup/cygwin/quotactl.cc b/winsup/cygwin/quotactl.cc
new file mode 100644
index 000000000..96c6134e8
--- /dev/null
+++ b/winsup/cygwin/quotactl.cc
@@ -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;
+}