diff options
Diffstat (limited to 'winsup/cygwin/quotactl.cc')
-rw-r--r-- | winsup/cygwin/quotactl.cc | 340 |
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; +} |