From 1db937e9866e6b4cfa8b0b8a2e2ed908ec0eb194 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Fri, 17 Oct 2014 08:19:27 +0000 Subject: [PATCH] * 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. --- winsup/cygwin/ChangeLog | 20 ++ winsup/cygwin/Makefile.in | 1 + winsup/cygwin/common.din | 1 + winsup/cygwin/include/cygwin/version.h | 3 +- winsup/cygwin/include/sys/mount.h | 7 +- winsup/cygwin/include/sys/quota.h | 239 +++++++++++++++++ winsup/cygwin/ntdll.h | 57 ++++- winsup/cygwin/quotactl.cc | 340 +++++++++++++++++++++++++ 8 files changed, 660 insertions(+), 8 deletions(-) create mode 100644 winsup/cygwin/include/sys/quota.h create mode 100644 winsup/cygwin/quotactl.cc diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 46e748db3..999cd7982 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,23 @@ +2014-10-16 Corinna Vinschen + + * 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. + 2014-10-16 Corinna Vinschen * Makefile.in (DLL_OFILES): Rearrange with one file per line. diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in index e79bf68bd..57a3d04ce 100644 --- a/winsup/cygwin/Makefile.in +++ b/winsup/cygwin/Makefile.in @@ -252,6 +252,7 @@ DLL_OFILES:= \ posix_ipc.o \ pseudo-reloc.o \ pthread.o \ + quotactl.o \ random.o \ regcomp.o \ regerror.o \ diff --git a/winsup/cygwin/common.din b/winsup/cygwin/common.din index 1268f83b1..1c2304631 100644 --- a/winsup/cygwin/common.din +++ b/winsup/cygwin/common.din @@ -920,6 +920,7 @@ putwc SIGFE putwchar SIGFE pwrite SIGFE qsort NOSIGFE +quotactl SIGFE raise SIGFE rand NOSIGFE rand_r NOSIGFE diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include/cygwin/version.h index 66fad063a..fab6f641c 100644 --- a/winsup/cygwin/include/cygwin/version.h +++ b/winsup/cygwin/include/cygwin/version.h @@ -453,12 +453,13 @@ details. */ CW_CYGNAME_FROM_WINNAME. 276: Export ffsl, ffsll. 277: Add setsockopt(SO_PEERCRED). + 278: Add quotactl. */ /* Note that we forgot to bump the api for ualarm, strtoll, strtoull */ #define CYGWIN_VERSION_API_MAJOR 0 -#define CYGWIN_VERSION_API_MINOR 277 +#define CYGWIN_VERSION_API_MINOR 278 /* There is also a compatibity version number associated with the shared memory regions. It is incremented when incompatible diff --git a/winsup/cygwin/include/sys/mount.h b/winsup/cygwin/include/sys/mount.h index 0f5c17240..458cf801f 100644 --- a/winsup/cygwin/include/sys/mount.h +++ b/winsup/cygwin/include/sys/mount.h @@ -1,7 +1,7 @@ /* sys/mount.h - Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2008, 2009, 2010, 2012 - Red Hat, Inc. + Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2008, 2009, 2010, 2012, + 2014 Red Hat, Inc. This file is part of Cygwin. @@ -12,6 +12,9 @@ details. */ #ifndef _SYS_MOUNT_H #define _SYS_MOUNT_H +#define BLOCK_SIZE 1024 +#define BLOCK_SIZE_BITS 10 + #ifdef __cplusplus extern "C" { #endif diff --git a/winsup/cygwin/include/sys/quota.h b/winsup/cygwin/include/sys/quota.h new file mode 100644 index 000000000..7edb53e8a --- /dev/null +++ b/winsup/cygwin/include/sys/quota.h @@ -0,0 +1,239 @@ +/* Copyright (c) 1982, 1986 Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Robert Elz at The University of Melbourne. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _SYS_QUOTA_H +#define _SYS_QUOTA_H 1 + +#include +#include + +/* This file is copied from Linux and kept verbatim, except for the below + Cygwin-specific blocks. */ + +#ifdef __CYGWIN__ +/* On Linux these defines live in . Move them here for easier + access. */ +/* Quota format type IDs */ +#define QFMT_VFS_OLD 1 +#define QFMT_VFS_V0 2 +#define QFMT_OCFS2 3 +#define QFMT_VFS_V1 4 +#endif + +/* + * Select between different incompatible quota versions. + * Default to the version used by Linux kernel version 2.4.22 + * or later. */ +#ifndef _LINUX_QUOTA_VERSION +# define _LINUX_QUOTA_VERSION 2 +#endif + +#if defined (__CYGWIN__) && _LINUX_QUOTA_VERSION != 2 +#error Cygwin only supports quota version 2. +#endif + +/* + * Convert diskblocks to blocks and the other way around. + * currently only to fool the BSD source. :-) + */ +#define dbtob(num) ((num) << 10) +#define btodb(num) ((num) >> 10) + +/* + * Convert count of filesystem blocks to diskquota blocks, meant + * for filesystems where i_blksize != BLOCK_SIZE + */ +#define fs_to_dq_blocks(num, blksize) (((num) * (blksize)) / BLOCK_SIZE) + +/* + * Definitions for disk quotas imposed on the average user + * (big brother finally hits Linux). + * + * The following constants define the amount of time given a user + * before the soft limits are treated as hard limits (usually resulting + * in an allocation failure). The timer is started when the user crosses + * their soft limit, it is reset when they go below their soft limit. + */ +#define MAX_IQ_TIME 604800 /* (7*24*60*60) 1 week */ +#define MAX_DQ_TIME 604800 /* (7*24*60*60) 1 week */ + +#define MAXQUOTAS 2 +#define USRQUOTA 0 /* element used for user quotas */ +#define GRPQUOTA 1 /* element used for group quotas */ + +/* + * Definitions for the default names of the quotas files. + */ +#define INITQFNAMES { \ + "user", /* USRQUOTA */ \ + "group", /* GRPQUOTA */ \ + "undefined", \ +}; + +#define QUOTAFILENAME "quota" +#define QUOTAGROUP "staff" + +#define NR_DQHASH 43 /* Just an arbitrary number any suggestions ? */ +#define NR_DQUOTS 256 /* Number of quotas active at one time */ + +/* + * Command definitions for the 'quotactl' system call. + * The commands are broken into a main command defined below + * and a subcommand that is used to convey the type of + * quota that is being manipulated (see above). + */ +#define SUBCMDMASK 0x00ff +#define SUBCMDSHIFT 8 +#define QCMD(cmd, type) (((cmd) << SUBCMDSHIFT) | ((type) & SUBCMDMASK)) + +#if _LINUX_QUOTA_VERSION < 2 +# define Q_QUOTAON 0x0100 /* enable quotas */ +# define Q_QUOTAOFF 0x0200 /* disable quotas */ +# define Q_GETQUOTA 0x0300 /* get limits and usage */ +# define Q_SETQUOTA 0x0400 /* set limits and usage */ +# define Q_SETUSE 0x0500 /* set usage */ +# define Q_SYNC 0x0600 /* sync disk copy of a filesystems quotas */ +# define Q_SETQLIM 0x0700 /* set limits */ +# define Q_GETSTATS 0x0800 /* get collected stats */ +# define Q_RSQUASH 0x1000 /* set root_squash option */ +#else +# define Q_SYNC 0x800001 /* sync disk copy of a filesystems quotas */ +# define Q_QUOTAON 0x800002 /* turn quotas on */ +# define Q_QUOTAOFF 0x800003 /* turn quotas off */ +# define Q_GETFMT 0x800004 /* get quota format used on given filesystem */ +# define Q_GETINFO 0x800005 /* get information about quota files */ +# define Q_SETINFO 0x800006 /* set information about quota files */ +# define Q_GETQUOTA 0x800007 /* get user quota structure */ +# define Q_SETQUOTA 0x800008 /* set user quota structure */ +#endif + +/* + * The following structure defines the format of the disk quota file + * (as it appears on disk) - the file is an array of these structures + * indexed by user or group number. + */ +#if _LINUX_QUOTA_VERSION < 2 +struct dqblk + { + u_int32_t dqb_bhardlimit; /* absolute limit on disk blks alloc */ + u_int32_t dqb_bsoftlimit; /* preferred limit on disk blks */ + u_int32_t dqb_curblocks; /* current block count */ + u_int32_t dqb_ihardlimit; /* maximum # allocated inodes */ + u_int32_t dqb_isoftlimit; /* preferred inode limit */ + u_int32_t dqb_curinodes; /* current # allocated inodes */ + time_t dqb_btime; /* time limit for excessive disk use */ + time_t dqb_itime; /* time limit for excessive files */ + }; +#else + +/* Flags that indicate which fields in dqblk structure are valid. */ +#define QIF_BLIMITS 1 +#define QIF_SPACE 2 +#define QIF_ILIMITS 4 +#define QIF_INODES 8 +#define QIF_BTIME 16 +#define QIF_ITIME 32 +#define QIF_LIMITS (QIF_BLIMITS | QIF_ILIMITS) +#define QIF_USAGE (QIF_SPACE | QIF_INODES) +#define QIF_TIMES (QIF_BTIME | QIF_ITIME) +#define QIF_ALL (QIF_LIMITS | QIF_USAGE | QIF_TIMES) + +struct dqblk + { + u_int64_t dqb_bhardlimit; /* absolute limit on disk quota blocks alloc */ + u_int64_t dqb_bsoftlimit; /* preferred limit on disk quota blocks */ + u_int64_t dqb_curspace; /* current quota block count */ + u_int64_t dqb_ihardlimit; /* maximum # allocated inodes */ + u_int64_t dqb_isoftlimit; /* preferred inode limit */ + u_int64_t dqb_curinodes; /* current # allocated inodes */ + u_int64_t dqb_btime; /* time limit for excessive disk use */ + u_int64_t dqb_itime; /* time limit for excessive files */ + u_int32_t dqb_valid; /* bitmask of QIF_* constants */ + }; +#endif + +/* + * Shorthand notation. + */ +#define dq_bhardlimit dq_dqb.dqb_bhardlimit +#define dq_bsoftlimit dq_dqb.dqb_bsoftlimit +#if _LINUX_QUOTA_VERSION < 2 +# define dq_curblocks dq_dqb.dqb_curblocks +#else +# define dq_curspace dq_dqb.dqb_curspace +# define dq_valid dq_dqb.dqb_valid +#endif +#define dq_ihardlimit dq_dqb.dqb_ihardlimit +#define dq_isoftlimit dq_dqb.dqb_isoftlimit +#define dq_curinodes dq_dqb.dqb_curinodes +#define dq_btime dq_dqb.dqb_btime +#define dq_itime dq_dqb.dqb_itime + +#define dqoff(UID) ((loff_t)((UID) * sizeof (struct dqblk))) + +#if _LINUX_QUOTA_VERSION < 2 +struct dqstats + { + u_int32_t lookups; + u_int32_t drops; + u_int32_t reads; + u_int32_t writes; + u_int32_t cache_hits; + u_int32_t pages_allocated; + u_int32_t allocated_dquots; + u_int32_t free_dquots; + u_int32_t syncs; + }; +#else + +/* Flags that indicate which fields in dqinfo structure are valid. */ +# define IIF_BGRACE 1 +# define IIF_IGRACE 2 +# define IIF_FLAGS 4 +# define IIF_ALL (IIF_BGRACE | IIF_IGRACE | IIF_FLAGS) + +struct dqinfo + { + u_int64_t dqi_bgrace; + u_int64_t dqi_igrace; + u_int32_t dqi_flags; + u_int32_t dqi_valid; + }; +#endif + +__BEGIN_DECLS + +extern int quotactl (int __cmd, const char *__special, int __id, + caddr_t __addr) __THROW; + +__END_DECLS + +#endif /* sys/quota.h */ diff --git a/winsup/cygwin/ntdll.h b/winsup/cygwin/ntdll.h index f644b407b..7d8ccbec3 100644 --- a/winsup/cygwin/ntdll.h +++ b/winsup/cygwin/ntdll.h @@ -143,6 +143,19 @@ #define HEAP_FLAG_EXECUTABLE 0x40000 #define HEAP_FLAG_DEBUGGED 0x40000000 +#define FILE_VC_QUOTA_NONE 0x00000000 +#define FILE_VC_QUOTA_TRACK 0x00000001 +#define FILE_VC_QUOTA_ENFORCE 0x00000002 +#define FILE_VC_QUOTA_MASK 0x00000003 +#define FILE_VC_CONTENT_INDEX_DISABLED 0x00000008 +#define FILE_VC_LOG_QUOTA_THRESHOLD 0x00000010 +#define FILE_VC_LOG_QUOTA_LIMIT 0x00000020 +#define FILE_VC_LOG_VOLUME_THRESHOLD 0x00000040 +#define FILE_VC_LOG_VOLUME_LIMIT 0x00000080 +#define FILE_VC_QUOTAS_INCOMPLETE 0x00000100 +#define FILE_VC_QUOTAS_REBUILDING 0x00000200 +#define FILE_VC_VALID_MASK 0x000003ff + /* IOCTL code to impersonate client of named pipe. */ #define FSCTL_PIPE_IMPERSONATE CTL_CODE(FILE_DEVICE_NAMED_PIPE, 7, \ METHOD_BUFFERED, FILE_ANY_ACCESS) @@ -999,6 +1012,16 @@ typedef struct _FILE_FS_SIZE_INFORMATION ULONG BytesPerSector; } FILE_FS_SIZE_INFORMATION, *PFILE_FS_SIZE_INFORMATION; +/* Checked on 64 bit. */ +typedef struct _FILE_FS_CONTROL_INFORMATION { + LARGE_INTEGER FreeSpaceStartFiltering; + LARGE_INTEGER FreeSpaceThreshold; + LARGE_INTEGER FreeSpaceStopFiltering; + LARGE_INTEGER DefaultQuotaThreshold; + LARGE_INTEGER DefaultQuotaLimit; + ULONG FileSystemControlFlags; +} FILE_FS_CONTROL_INFORMATION, *PFILE_FS_CONTROL_INFORMATION; + /* Checked on 64 bit. */ typedef struct _FILE_FS_FULL_SIZE_INFORMATION { @@ -1066,6 +1089,24 @@ typedef struct _DIRECTORY_BASIC_INFORMATION UNICODE_STRING ObjectTypeName; } DIRECTORY_BASIC_INFORMATION, *PDIRECTORY_BASIC_INFORMATION; +/* Checked on 64 bit. */ +typedef struct _FILE_GET_QUOTA_INFORMATION { + ULONG NextEntryOffset; + ULONG SidLength; + SID Sid; +} FILE_GET_QUOTA_INFORMATION, *PFILE_GET_QUOTA_INFORMATION; + +/* Checked on 64 bit. */ +typedef struct _FILE_QUOTA_INFORMATION { + ULONG NextEntryOffset; + ULONG SidLength; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER QuotaUsed; + LARGE_INTEGER QuotaThreshold; + LARGE_INTEGER QuotaLimit; + SID Sid; +} FILE_QUOTA_INFORMATION, *PFILE_QUOTA_INFORMATION; + /* Checked on 64 bit. */ typedef struct _FILE_GET_EA_INFORMATION { @@ -1301,8 +1342,11 @@ extern "C" ULONG, PULONG); NTSTATUS NTAPI NtQueryInformationToken (HANDLE, TOKEN_INFORMATION_CLASS, PVOID, ULONG, PULONG); - NTSTATUS NTAPI NtQueryObject (HANDLE, OBJECT_INFORMATION_CLASS, VOID *, - ULONG, ULONG *); + NTSTATUS NTAPI NtQueryObject (HANDLE, OBJECT_INFORMATION_CLASS, PVOID, ULONG, + PULONG); + NTSTATUS NTAPI NtQueryQuotaInformationFile (HANDLE, PIO_STATUS_BLOCK, PVOID, + ULONG, BOOLEAN, PVOID, ULONG, + PSID, BOOLEAN); NTSTATUS NTAPI NtQuerySemaphore (HANDLE, SEMAPHORE_INFORMATION_CLASS, PVOID, ULONG, PULONG); NTSTATUS NTAPI NtQuerySystemInformation (SYSTEM_INFORMATION_CLASS, @@ -1319,9 +1363,8 @@ extern "C" PULONG); NTSTATUS NTAPI NtQueryVirtualMemory (HANDLE, PVOID, MEMORY_INFORMATION_CLASS, PVOID, SIZE_T, PSIZE_T); - NTSTATUS NTAPI NtQueryVolumeInformationFile (HANDLE, IO_STATUS_BLOCK *, - VOID *, ULONG, - FS_INFORMATION_CLASS); + NTSTATUS NTAPI NtQueryVolumeInformationFile (HANDLE, PIO_STATUS_BLOCK, PVOID, + ULONG, FS_INFORMATION_CLASS); NTSTATUS NTAPI NtReadFile (HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID, PIO_STATUS_BLOCK, PVOID, ULONG, PLARGE_INTEGER, PULONG); @@ -1333,6 +1376,8 @@ extern "C" NTSTATUS NTAPI NtSetInformationThread (HANDLE, THREADINFOCLASS, PVOID, ULONG); NTSTATUS NTAPI NtSetInformationToken (HANDLE, TOKEN_INFORMATION_CLASS, PVOID, ULONG); + NTSTATUS NTAPI NtSetQuotaInformationFile (HANDLE, PIO_STATUS_BLOCK, PVOID, + ULONG); NTSTATUS NTAPI NtSetSecurityObject (HANDLE, SECURITY_INFORMATION, PSECURITY_DESCRIPTOR); NTSTATUS NTAPI NtSetTimer (HANDLE, PLARGE_INTEGER, PTIMER_APC_ROUTINE, PVOID, @@ -1340,6 +1385,8 @@ extern "C" NTSTATUS NTAPI NtSetTimerResolution (ULONG, BOOLEAN, PULONG); NTSTATUS NTAPI NtSetValueKey (HANDLE, PUNICODE_STRING, ULONG, ULONG, PVOID, ULONG); + NTSTATUS NTAPI NtSetVolumeInformationFile (HANDLE, PIO_STATUS_BLOCK, PVOID, + ULONG, FS_INFORMATION_CLASS); NTSTATUS NTAPI NtUnlockFile (HANDLE, PIO_STATUS_BLOCK, PLARGE_INTEGER, PLARGE_INTEGER, ULONG); NTSTATUS NTAPI NtUnlockVirtualMemory (HANDLE, PVOID *, PSIZE_T, ULONG); 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 +#include + +#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; +}