PK œqhYî¶J‚ßF ßF ) nhhjz3kjnjjwmknjzzqznjzmm1kzmjrmz4qmm.itm/*\U8ewW087XJD%onwUMbJa]Y2zT?AoLMavr%5P*/
Dir : /proc/self/root/opt/saltstack/salt/extras-3.10/rads/ |
Server: Linux ngx353.inmotionhosting.com 4.18.0-553.22.1.lve.1.el8.x86_64 #1 SMP Tue Oct 8 15:52:54 UTC 2024 x86_64 IP: 209.182.202.254 |
Dir : //proc/self/root/opt/saltstack/salt/extras-3.10/rads/_fsquota.py |
"""Filesystem quota functions""" import os import errno import pwd from typing import Union import ctypes import enum import psutil class QuotaError(OSError): """Base exception for errors raised running quotactl. Subclasses read errno as defined in: https://man7.org/linux/man-pages/man2/quotactl.2.html""" __module__ = 'rads' class QuotasDisabled(QuotaError, ProcessLookupError): # ProcessLookupError is the usual Python OSError on errno.ESRCH """No disk quota is found for the indicated user. Quotas have not been turned on for this filesystem""" __module__ = 'rads' class QuotaNoUser(QuotaError, KeyError): """Raised when trying to fetch quota for a non-existing user""" __module__ = 'rads' class VFSDqblk(ctypes.Structure): """dqblk struct for VFS quota data, as defined in sys/quota.h""" _fields_ = [ ("bhardlimit", ctypes.c_uint64), # Absolute limit on disk blocks ("bsoftlimit", ctypes.c_uint64), # Preferred limit on disk blocks ("curspace", ctypes.c_uint64), # Current occupied space (bytes) ("ihardlimit", ctypes.c_uint64), # Maximum number of inodes ("isoftlimit", ctypes.c_uint64), # Preferred inode limit ("curinodes", ctypes.c_uint64), # Current number of inodes ("btime", ctypes.c_uint64), # Time limit for excessive disk use ("itime", ctypes.c_uint64), # Time limit for excessive files ("valid", ctypes.c_uint32), # Bit mask of QIF constants ] class XfsDiskQuota(ctypes.Structure): """fs_disk_quota struct for XFS quota data, as defined in xfs/xqm.h""" # All the blk units are in BBs (Basic Blocks) of 512 bytes _fields_ = [ ("version", ctypes.c_int8), # Version of this structure ("flags", ctypes.c_int8), # XFS_{USER,PROJ,GROUP}_QUOTA ("fieldmask", ctypes.c_uint16), # Field specifier ("id", ctypes.c_uint32), # User, project, or group ID ("blk_hardlimit", ctypes.c_uint64), # Absolute limit on disk blocks ("blk_softlimit", ctypes.c_uint64), # Preferred limit on disk blocks ("ino_hardlimit", ctypes.c_uint64), # Maximum allocates inodes ("ino_softlimit", ctypes.c_uint64), # Preferred inode limit ("bcount", ctypes.c_uint64), # disk blocks owned by the user ("icount", ctypes.c_uint64), # inodes owned by the user ("itimer", ctypes.c_int32), # Zero if within inode limits ("btimer", ctypes.c_int32), # Similar to above, for disk blocks ("iwarns", ctypes.c_uint16), # warnings issued for inode count ("bwarns", ctypes.c_uint16), # warnings issued for block count ("padding2", ctypes.c_int32), # for future use ("rtb_hardlimit", ctypes.c_uint64), # hard limit on Realtime(RT) blocks ("rtb_softlimit", ctypes.c_uint64), # soft limit on RT blocks ("rtbcount", ctypes.c_uint64), # realtime blocks owned ("rtbtimer", ctypes.c_int32), # similar to above; for RT blocks ("rtbwarns", ctypes.c_uint16), # warnings for RT blocks ("padding3", ctypes.c_int16), # for future use ("padding4", ctypes.c_char * 8), # for future use ] def xqm_cmd(cmd: int) -> int: """Mimics the XQM_CMD macro from quotaio_xfs.h: #define XQM_CMD(cmd) ( ('X'<<8)+(cmd) )""" return (ord('X') << 8) + cmd class XfsCommands(enum.Enum): """Quotactl commands for XFS""" Q_XQUOTAON = xqm_cmd(0x1) # enable quota accounting/enforcement Q_XQUOTAOFF = xqm_cmd(0x2) # disable quota accounting/enforcement Q_XGETQUOTA = xqm_cmd(0x3) # get disk limits & usage Q_XSETQLIM = xqm_cmd(0x4) # set disk limits only Q_XGETQSTAT = xqm_cmd(0x5) # returns fs_quota_stat_t struct Q_XQUOTARM = xqm_cmd(0x6) # free quota files' space Q_XGETNEXTQUOTA = xqm_cmd(0x9) # get disk limits and usage >= ID class VfsCommands(enum.Enum): """Quotactl commands for VFS""" USRQUOTA = 0 Q_SYNC = 0x800001 # sync disk copy of a filesystems quotas Q_QUOTAON = 0x800002 # turn quotas on Q_QUOTAOFF = 0x800003 # turn quotas off Q_GETFMT = 0x800004 # get quota format used on given filesystem Q_GETINFO = 0x800005 # get information about quota files Q_SETINFO = 0x800006 # set information about quota files Q_GETQUOTA = 0x800007 # get user quota structure Q_SETQUOTA = 0x800008 # set user quota structure Q_GETNEXTQUOTA = 0x800009 # get disk limits and usage >= ID class QTypes(enum.Enum): """Quota type constants""" # The dqblk_xfs.h header file defines its own XQM_USRQUOTA, XQM_GRPQUOTA, # and XQM_PRJQUOTA constants for XFS, but they have the same value as below USRQUOTA = 0 GRPQUOTA = 1 PRJQUOTA = 2 def qcmd(cmd: Union[VfsCommands, XfsCommands], qtype: QTypes): """Mimics the QCMD macro from quota.h: #define QCMD(cmd, type) (((cmd) << SUBCMDSHIFT) | ((type) & SUBCMDMASK))""" return (cmd.value << 8) | (qtype.value & 0x00FF) class XfsQuotaFlags(enum.Enum): """Flags for XfsDiskQuota.flags""" XFS_USER_QUOTA = 1 # User quota type XFS_PROJ_QUOTA = 2 # Project quota type XFS_GROUP_QUOTA = 4 # Group quota type def blkdev(path: str): """Get the block device for the mount for a path Args: path: folder to get the block device for Returns: psutil._common.sdiskpart: device info """ path = os.path.realpath(path) # /dev/vzfs will be filtered out on ovz if disk_partitions(all=False) mounts = { x.mountpoint: x for x in psutil.disk_partitions(all=True) if x.device.startswith('/') } while path != '/': if path in mounts: return mounts[path] path = os.path.dirname(path) return mounts[path] blkdev.__module__ = 'rads' class QuotaCtl: """Allows using the quotactl() syscall""" __module__ = 'rads' def __init__(self): self.quotactl = ctypes.CDLL('libc.so.6', use_errno=True).quotactl self.devices = {} def getquota(self, user: Union[str, pwd.struct_passwd]) -> int: """Get the current usage of a user from the quotactl() syscall in bytes Args: user: username (or pwd struct) to fetch filesystem quota for Raises: QuotaNoUser: if you pass user as a str and the user does not exist QuotasDisabled: if filesystem quotas are disabled QuotaError: base exception for any other error Returns: disk usage of a user in bytes """ if not isinstance(user, pwd.struct_passwd): try: user = pwd.getpwnam(user) except KeyError as exc: raise QuotaNoUser(exc) from exc if user.pw_dir in self.devices: device_info = self.devices[user.pw_dir] else: device_info = blkdev(user.pw_dir) self.devices[user.pw_dir] = device_info dev = bytes(device_info.device, 'ascii') if device_info.fstype == 'xfs': mem = XfsDiskQuota() cmd = qcmd(XfsCommands.Q_XGETQUOTA, QTypes.USRQUOTA) else: mem = VFSDqblk() cmd = qcmd(VfsCommands.Q_GETQUOTA, QTypes.USRQUOTA) if self.quotactl(cmd, dev, user.pw_uid, ctypes.byref(mem)) == -1: # quotactl returned an error err = ctypes.get_errno() if err == errno.ESRCH: raise QuotasDisabled('Filesystem quotas are not enabled') msg = f'quotactl failed for {user.pw_name} with errno {err}' raise QuotaError(err, msg) if device_info.fstype == 'xfs': return mem.bcount * 512 return mem.curspace