pax_global_header00006660000000000000000000000064140026175710014515gustar00rootroot0000000000000052 comment=b8c3322c2f47ab901224664b11008c8fae46a701 python-fsquota-0.1.0+dfsg1/000077500000000000000000000000001400261757100155145ustar00rootroot00000000000000python-fsquota-0.1.0+dfsg1/CHANGES000066400000000000000000000426311400261757100165150ustar00rootroot00000000000000Changes in Python-FsQuota 0.1.0 (April 2020) - interface clean-up: renamed option "timelimit_reset" "timereset" - merged compile-fixes & enhancements for BSDs & RPC from Perl-Quota 1.8.1 (see below), plus: - FreeBSD: compile-fix getmntinfo() & getqcarg: stat.st_dev comparison - NetBSD, query: missing exception upon error from quota_get() - when compiled with NO_RPC: throw exception in __init__ for NFS - setup.py: compatibility to Python 3.4 - added new smoke test script: tests/test_smoke.py => module verified on Linux and the latest BSDs on local FS and NFS Changes in Python-FsQuota 0.0.2 (April 2020) - Switched to Object-Oriented API Changes in Python-FsQuota 0.0.1 (April 2020) - Initial port from Perl-Quota, using function-based API =============================================================================== * History of the Perl module, which is base for the Python module; * * included here to acknowledge all the people who contributed in the past. * =============================================================================== Changes in 1.8.1 (April 2020) - correction to Q_XQUOTASYNC update done in 1.8.0: return value inverted - compile-fixes and enhancements for latest versions of the BSDs: - compile fix: FreeBSD lacks define RQUOTA_USRQUOTA in rpcsvc/rquota.h - NetBSD: map QUOTA_NOLIMIT to 0 for consistency with other platforms - DragonFly support prepared (untested); obsolete BSD/OS removed - getmntent(): decode numerical values of fs type and options to strings - renamed compile-switch HAVE_STATVFS: too generic (conflict Python.h) - fixes for group quota support via RPC (i.e. v2 aka extended RPC): - removed use of USE_EXT_RQUOTA in include/rquota.h: switch is defined only later, thus extended quota was inadvertedly disabled - Linux & OpenBSD: switched from rpcsvc/rquota.h to internal rquota.h, as sys header lacks definition for extended RPC - getnfsquota(): don't use v1 RPC for group quota if v2 fails; return error instead - adjusted order of elements in ext_getquota_args to match XDR encoding (just to avoid confusion; no harm) - setqlim: raise error in case of truncation of given limits to 32-bit - test.pl: - added smoke test: run automatically upon $ENV{AUTOMATED_TESTING} - enhanced "forced RPC" test: ask for host:path, allow skipping Changes in 1.8.0 (April 2020) - revised "tirpc" change in 1.7.3: use "-ltirpc" only when SUN-RPC is NOT included in libc; else we may compile against tirpc but linker may resolved against libc; leads to memory corruption in auth_destroy() - cntd. attempt at fixing Makefile.PL for build on NetBSD release > 6 - Backport of minor fixes & enhancements done while porting to Python - extended test scripts (RPC test; read-back&verify limits after setqlim) - RPC result handling: removed forced ESRCH error upon 0 limits so that behavior matches that of local query (at least on Linux) - corrected Quota::strerr() for errors caused in Sun-RPC library funcs - updated include/quotaio_xfs.h to latest version in Linux headers and use newer (~2004) interface Q_XQUOTASYNC for Quota::sync() Changes in Perl-Quota 1.7.4 (March 2020) - Build fixes for NetBSD release > 6 and Apple/Darwin based on failure reports of automated CPAN testing - Added support for group quotas in test.pl; Corrections to documentation of group quota handling Changes in Perl-Quota 1.7.3 (March 2020) - Added detection for missing header rpc/rpc.h; automatically switch to using "tirpc", if present. Issue reported by Michael Stauber via CPAN ticket 128302 - Also fixed compiler warnings in ancient RPC code. Changes in Perl-Quota 1.7.2 (May 2015) - Adapted platform detection for Linux 4.* Thanks to C. Affolter for reporting the issue (CPAN ticket 104652) Changes in Perl-Quota 1.7.1 (September 2013) - "make test" now aborts if STDOUT is not a terminal, same as already for STDIN. Suggested by Alexandr Ciornii via CPAN ticket 82564. Changes in Perl-Quota 1.7.0 (August 2013) - Added support for the new NetBSD 6.0 quota system (libquota) Thanks to David Holland and Edgar Fuss - To support limits and usage >= 2^32 on 32-bit clients, Quota::setqlim and Quota::query interfaces were changed to use double (NV) instead of integer (IV). This should be transparent to users of the module. Changes in Perl-Quota 1.6.7 (November 2011) - Added support to detect 3.x Linux kernels Thanks to Salvatore Bonaccorso of the Debian Perl Group Changes in Perl-Quota 1.6.6 (June 2011) - Ignore "rootfs" filesystem in Quota::getqcarg() on Linux, which always is a duplicate. Thanks to "abaturin" for providing this patch. - Made test.pl exit immediately when AUTOMATED_TESTING is set in the environment (same as when called not from a tty), as all tests require user interaction. Changed on request of TODDR of cpan.org Changes in Perl-Quota 1.6.5 (January 2011) - Fixed syntax error and missing init for params to setqlim() Thanks to Heinrich Mislik for reporting the bugs Changes in Perl-Quota 1.6.4 (January 2010) - Fixed RPC quota block count truncation to 32bit (CPAN ticket 52813 ) Thanks to Lloyd Brown for reporting the bug Changes in Perl-Quota 1.6.3 (December 2008) - Fixed SEG fault for 32-bit Linux clients on top of 64-bit kernels Thanks to Jani Ollikainen (pronetko.fi) for reporting and debugging this. Thanks to Thomas Rzipa for reporting this issue, too. - Fixed setqlim to allow setting limits exceeding 2^32 blocks or files. The current solution will work only for perl executables which support 64-bit wide integers (e.g. x86-64) Changes in Perl-Quota 1.6.2 (January 2008) - Fixed memory leak in RPC authentication Thanks again to Kostik (koc at rol.ru) Changes in Perl-Quota 1.6.1 (November 2007) - Adapted hints/bsd.h to use statvfs instead of statfs in NetBSD 3.0 - Added support for extended quota RPC to allow querying group quotas. Thanks to Kostik (koc AT rol.ru) for the suggestion. Changes in Perl-Quota 1.6.0 (October 2007) - Work-around for different getmntent() semantics for linux loop mounts Thanks Wouter de Jong (wouter@widexs.nl) for reporting this issue. - (late) version bump due to the interface change in 1.5.2 Changes in Perl-Quota 1.5.2 (October 2007) - Added support for new quotactl() parameters in JFS2 on AIX 5.3. Thanks to Joshua Frigerio (joshua@uic.edu) for providing a development account. (Note: also prepared support for JFS2 quota classes; this is yet unfinished and untested. See sub-directory contrib/aix_jfs2_class) - Added support for XFS project quotas on request of Sten Spans (sten@blinkenlights.nl); received no feedback it if works. Changed the "isgrp" param to Quota::query() and setqlim() from boolean into an enum: 0:=user, 1:=group, 2:=project quotas. - Added a copyright notice to the main module and the manual page on request of the Debian maintainer. Changes in Perl-Quota 1.5.1 (July 2005) - Added new API function rpcauth: use non-default authentication for RPC Patch submitted by Karol Lassak - Makefile fix for Linux: compile linuxapi.c with default CFLAGS because for 64-bit systems -fPIC is required. Patches submitted by David Lee and James Olin Oden; used a different solution though. Changes in Perl-Quota 1.5.0 (Nov 2004) - Added new API function rpcpeer: set port and protocol for RPC queries. On request by Sten Spans (sten@blinkenlights.nl) Changes in Perl-Quota 1.4.11 (Aug 2004) - Updated VxFS detection in Makefile.PL for recent Solaris/VxFS versions Thanks to Joshua Frigerio for the info and to Keith Clay for verification. - tiny syntax cleanup in linuxapi.c Changes in Perl-Quota 1.4.10 (Jan 2004) - Applied patch by Antti Tapaninen (aet at cc.hut.fi) to add support for MacOS X (based on BSD config) and NFS3 support for AIX. - Applied patch by Chris Adams (cmadams at hiwaay.net) to avoid warning in Quota::getqcarg() on systems where getmntent(3) may return invalid mount points, e.g. Tru64 UNIX Changes in Perl-Quota 1.4.9 (Aug 2003) - Applied patch by Wolfgang.Friebel@desy.de: Changed AFS quota support, now based on OpenAFS instead of arla and Kerberos4. Changes in Perl-Quota 1.4.8 (May 2003) - fixed bug in linuxapi.c, thanks to Shane DeRidder Changes in Perl-Quota 1.4.7 (May 2003) - adapted for the latest fashion of Linux quota APIs (version 6.5.1) Thanks to Jay R. Wren, Shane DeRidder, Roman "romke" and Geoffrey Pan for your infos and feedback. - added quota threshold monitoring script to the contrib directory. Thanks to Ziaur Rahman. Changes in Perl-Quota 1.4.6 (August 2002) - changed the test script to exit if STDIN is not a terminal device. This is an interactive script which cannot be run by CPANPLUS. Changes in Perl-Quota 1.4.5 (July 2002) - replaced use of macro BCOPY with libc function memcpy() because of build problems on Solaris 2.8 (bcopy is depreciated now anyways) Thanks to Jost Krieger and Paul Sand for reporting this problem. Changes in Perl-Quota 1.4.4 (June 2002) - bugfix in Quota::getqcarg(): the function failed if the device id returned by stat(2) for the queried path was zero (e.g. for the root fs on NetBSD) Thanks to Jake Fan (jake@chaogic.com) for reporting this bug. Changes in Perl-Quota 1.4.3 (May 2002) - updated for new version of Solaris VxFS (Veritas Filesystem) by David Lee (T.D.Lee@durham.ac.uk) Changes in Perl-Quota 1.4.2 (Jan. - Mar. 2002) - fixed test.pl to allow uids with more than 5 digits. Thanks to Neil Prockter (perl@iamafreeman.com) for the fix. - updated Linux quota version detection with quota-tools 3.04; removed compile-time version detection because it caused problems; added switch LINUX_API_VERSION to hints/linux.h to allow to hard-wire the API in case the automatic version detection fails. Changes in Perl-Quota 1.4.1 (Sep. 2001 - Jan. 2002) - added support for an older (intermediate?) version of the new Linux Quota API which contains a mixture of v1 and v2 command ids (uses the old GETSTAT id, however with the new and larger struct as argument). Required for RedHat-7.1. Thanks to Andy Choi (andy@ensim.com) for pointing this out. - enabled RPC in hints/bsd.h Confirmed to work on FreeBSD by Alex Batko (abatko@cs.mcgill.ca) - fixed several glitches in the manual page. Changes in Perl-Quota 1.4 (August 2001) - added support for the Alan Cox (ac) branch of the Linux kernel which uses a new and completely backwards incompatible Quota API. The API version is determined dynamically by use of Q_GETSTATS/v2, i.e. the same module binary will work on kernels with either API. Since the Linux quota API now needs some very special handling, I moved it into a separate file called linuxapi.c. NOTE: internally the module still uses the old (v1) dqblk structure, so any advantages the new struct mem_dqblk might have are not present here. Let me know if this is a problem for you. - commented out #define LINUX_RQUOTAD_BUG in hints/linux.h as this should no longer be needed on most systems. Updated INSTALL and README accordingly. - removed the Linux syscall wrapper in quotactl.c as this is now in libc. - changed copyright from "none/public domain" to Artistic License (not to restrict usage, but simply to include the legal disclaimers) - fixed bug in Quota::query() and setqlim(): when argument isgrp was present but 0, the functions still did work on group quotas instead of user quotas. Thanks to Szymon Juraszczyk (szymon@ssk.pl) for pointing this out. Changes in Perl-Quota 1.3.4 (May 2001) - added support for SGI XFS on Linux. Thanks to Brian Johnson (brian@dev.brianj.com) for providing a development account. Changes in Perl-Quota 1.3.3 (May 2001) - bugfix Quota::query, branch NO_RPC: forgot to set error flag, arbitrary results were returned; Pointed out by Mahlon Smith - fixed declaration of GQR_* macros for RPC in hints/bsd.h RPC still untested for BSD though - fixed OpenBSD2.7 fix from last release: replaced macro OpenBSD2_7 with __OpenBSD__ because the former is not defined in 2.8 anymore. Reported by Lou Hevly (lou@visca.com) - fixed hints/linux.h for RedHat 7.1: use sys/quota.h instead of linux/quota.h because the former has an incompatible definition of struct dqblk. [NOTE: this change proved to be wrong and was undone in 1.4] Reported by Waldemar Krotkiewicz (wk@brenet.de), Andy Choi (andy@ensim.com) and Brian Johnson (brian@dev.brianj.com). Changes in Perl-Quota 1.3.2 (February 2001) - fixed AFS detection in Makefile.PL for Perl 5.6 thanks to Wolfgang Friebel - adapted getmntent for incompatible change of struct statfs in OpenBSD 2.7 thanks to andrew@ugh.net.au - adapted getqcarg for for OpenBSD and BSDi: define QCARG_MNTPT for all BSD os as reported by andrew@ugh.net.au and Chee-Wai Yeung - fixed block to kB conversion in Quota::query() for VxFS as reported by Rui Monteiro - renamed config.h symlink to myconfig.h to avoid conflict with Perl config Changes in Perl-Quota 1.3.1 (October 2000) - added support for NetBSD, merged hints for BSD-based systems by Jaromir Dolecek - fixed include file name for vxquotactl.h changed quota block factors Q_DIV/Q_MUL from 1 to 2 for Veritas fs Thanks to David Lee (T.D.Lee@durham.ac.uk) - added automatic recognition of HPUX B.11 in Makefile.PL by Albert Chin (china@thewrittenword.com) Changes in Perl-Quota 1.3: (January 2000) - bugfix/enhanced support for OSF/1 and Digital Unix: getqcarg used wrong path to quotas file and NFS file systems were not recognized when in "/path@host" format in mtab provided by Victor Mora (Victor.Mora@ac.upc.es) and Alessandro Miotto (Alessandro.Miotto@cern.ch) - added support for FreeBSD. provided by Kurt Jaeger (pi@complx.LF.net) and Jon Schewe (schewe@tcfreenet.org) - added support for Veritas file system (VxFS) on Solaris provided by David Lee (T.D.Lee@durham.ac.uk), Michael Gerdts (gerdts@cae.wisc.edu) and Daniel Hagerty (hag@shore.net). Beta-tested by John Randell Smith (jrsmith@eng.utah.edu). - added workaround for yet another bug in Linux rpc.rquotad: rquotad reports grace times as absolute values instead of relative bug found by Seth Vidal (skvidal@phy.duke.edu) - fixed grace time output for Y2K in test.pl - fixed bug in group-quota patch as pointed out by asun@cobaltnet.com incorporated the patch into the distribution. - fixed possible integer overflow in RPC quota block size conversions for very large quota limits; pointed out by Peter.Pickford (ppickfor@jaguar.com) - added warning to Makefile.PL if config.h symlink already exists Changes in Perl-Quota 1.2.3: (April 1999) - added patch-file "group-quota.patch" that provides optional support for group-quotas (on some OS versions and localhost only) - added auto-detection for AFS by Wolfgang Friebel - fixed include path for AFS in Quota.xs Changes in Perl-Quota 1.2.2: (December 1998) - fixed 2 problems in getqcarg() thanks to py@ecst.csuchico.edu for pointing those out. Changes in Perl-Quota 1.2: (November 1998) - added support for AIX 4.1 (thanks to Wolfgang Friebel (friebel@ifh.de) for providing a development account) - added support for AFS (Andrew File System) by arla-0.13 on the following platforms: AIX, SunOS, Solaris, HP-UX, IRIX 6, Linux with much help from Wolfgang Friebel. Changes in Perl-Quota 1.1.2: (August 1998) - changed names of tar archive and module directory - fixed message for getqcarg failure in test.pl - compatibility with sfio (moved fopen and fclose to seperate file) suggested by Christoph Lupe (lupe@alanya.m.isar.de) - yet UNTESTED - fixed problems with Solaris automounter (ignore lofs mounts in getqcarg) Changes in Perl-Quota 1.1: (August 1998) - added support for Linux (thanks to Steve Nolan (nolansj@bookmark.com) for providing a development account) - added OpenBSD 2.2 support, provided by James Shelburne (reilly@eramp.net) - added syntax fixes (2 additional #includes) for IRIX 6.4 provided by Subhendu Ghosh (sghosh@menger.eecs.stevens-tech.edu) - support for IRIX xfs filesystems (additional to the older efs fs) provided by David Lloyd (cclloyd@monotreme.cc.missouri.edu) Changes in Perl-Quota 1.0: (That's the version that's been released with O'Reillys Perl Resource Books) - improved documentation, now provided in pod format inside Quota.pm - finally fixed errno.h problem in Quota.pm Thanks to Tobias Oetiker (oetiker@ee.ethz.ch) - added BSDI port by Jim Hribnak (hribnak@nucleus.com) unfortunately without RPC support. - small fixes for OSF/1 - more hints in hints/none.h - I've again received requests for Linux ports. However since I don't have access to an installation with quota support, I can't do this myself. I've included a config file that compiles without warnings on our system ("Red Hat for Sparc" or something), but I can't run it. If anyone gets it to work with or without RPC, please contact me. Changes in Perl-Quota 0.3a: - started port for Linux 2.0; still needs some work. Compiles correctly, but RPC call fails. - workarounds for HP-UX/10 bug in test script (SYNC without arguments doesn't work) - some cleanup in Query.pm fixes 0.2a's problems with the autoloader under perl-5.003 Changes in Perl-Quota 0.2a: - need generic getqcarg instead of getdev, because: - added support for Solaris, OSF/1 (which operate without access to the according block device) Required extensive code changes. - getqcarg recognizes if path points into a NFS file system - query() may take NFS path as argument (in form host:/path) Thanks to David Lee for alpha-testing on Solaris and suggesting the two latter improvements. python-fsquota-0.1.0+dfsg1/FsQuota.egg-info/000077500000000000000000000000001400261757100205705ustar00rootroot00000000000000python-fsquota-0.1.0+dfsg1/FsQuota.egg-info/PKG-INFO000066400000000000000000000447051400261757100216770ustar00rootroot00000000000000Metadata-Version: 2.1 Name: FsQuota Version: 0.1.0 Summary: Interface to file system quotas on UNIX platforms Home-page: https://github.com/tomzox/Python-Quota Author: T. Zoerner Author-email: tomzo@users.sourceforge.net License: GNU GPLv2+ Description: =============================== Python File-system Quota module =============================== The Python file-system quota module allows accessing file system quotas on UNIX platforms from Python scripts. The module is intended mainly for system administrators who have to manage quotas for all the users on their system. The following operating systems and file systems are supported transparently through a common API. Supported operating systems: * Linux - kernel 2.0.30 - 4.15 * FreeBSD 3 - 12.1, OpenBSD 2.2 - 6.6 & NetBSD 5 - 9 * SunOS 4.1.3 (aka Solaris 1) * Solaris 2.4 - 2.10 * HP-UX 9.0x & 10.10 & 10.20 & 11.00 * IRIX 5.2 & 5.3 & 6.2 - 6.5 * OSF/1 & Digital Unix 4 * AIX 4.1, 4.2 and 5.3 Supported file systems: * Standard file systems of the platforms listed above * NFS (Network file system) on all of the above (i.e. using an integrated RPC client) * XFS on Linux and IRIX 6 * AFS (Andrew File System) on many of the above (see INSTALL) * VxFS (Veritas File System) on Solaris 2 Historical note: The C implementation of this module is derived from the `Quota module for Perl`_ (also at `CPAN`_). Since its beginnings in 1995, the module was continuously extended by porting to more UNIX platforms and file-systems. Numerous people have contributed to this process; for a complete list of names please see the CHANGES document in the package. In case of build issues, please refer to the INSTALL document within the package. .. _Quota module for Perl: https://github.com/tomzox/Perl-Quota .. _CPAN: https://metacpan.org/pod/Quota The following is a copy of the API documentation in file doc/FsQuota.rst SYNOPSIS ======== :: import FsQuota qObj = FsQuota.Quota(path [,rpc_host=hostname]) (bcount, bsoft, bhard, btime, icount, isoft, ihard, itime) = qObj.query(uid [,grpquota=1] [,prjquota=1]) qObj.setqlim(uid, bsoft, bhard, isoft, ihard [,timereset=1] [,grpquota=1] [,prjquota=1]) qObj.sync() qObj.rpc_opt([option keywords]) for dev, path, type, opts in FsQuota.MntTab(): ... FsQuota Module ============== The **FsQuota** module provides two classes that allow accessing file system quotas from Python scripts: Instances of the **Quota** class take as main init parameter a path of a mount point (or any path below the mount point). The instance can then be used to query or modify quota of users or groups within that file system. The class is designed portably, so that the same interfaces work across all file system types and UNIX platforms. (Although there are some extra options during initialization for certain file systems.) Instances of the **MntTab** class allow iterating across the mount table. For each entry in the table, it provides the file system type, mount point and options. (Note this class is usually not required to work with the Quota class. It is provided here just for convenience, as the functionality is actually used internally by the Quota class.) Class FsQuota.Quota =================== :: qObj = FsQuota.Quota(path) qObj = FsQuota.Quota(remote_path, rpc_host=remote_host) Creates a Quota object that then is used for querying or modifying quotas. In case of special file systems which are known not to suport quota, the creation may raise exception **FsQuota.error**. However note the absence of an exception is not a guarantee that the file system actually supports quota limits. Internal behavior: Most importantly, the initialization determines the file system type and thus the access method to be used in following quota operations. Many platforms use the **quotactl** syscall, but even then the type of device parameter to be passed varies from system to system. It may be the path of a device file (e.g. `/dev/sda1`) or the path of the mount point or the quotas file at the top of the file system (e.g. `/home/quotas`). For the rare cases you need this information, it can be queried via the **Quota.dev** attribute. The given mount point may also be on a remote file system (e.g. mounted via Network File System, NFS), which has the class transparently query the given host via a remote procedure call (RPC). Note: RPC queries require *rquotad(1m)* to be running on the target system. If the daemon or host are down, the operations time out after a configurable delay. When parameter **rpc_host** is specified, the automatic detection of file system type is omitted. In this case the following operations will address the file system containing the given path on the given remote host using RPC. This mode should normally not be needed, but could for example be used for accessing file systems that are not mounted locally. See also the **rpc_opt()** method for additional RPC configuration options. Quota.query() ------------- :: (bcount,bsoft,bhard,btime, icount,isoft,ihard,itime) = qObj.query(uid, [keyword_options...]) Get current usage and quota limits for blocks and files respectively, owned by the given user. The user is specified by a numeric UID. The result is a named tuple of type **FsQuota.QueryResult**, so that members can be accessed via name as well as via indices: 0. **bcount**: Number of 1 kB blocks currently used by inodes owned by the user. 1. **bsoft**: Soft limit for block count (or 0 if none) 2. **bhard**: Hard limit for block count (or 0 if none) 3. **btime**: Time when an exceeded soft block limit turns into a hard limit. This value is meaningless when the soft limit is not exceeded. 4. **icount**: Number of inodes (i.e. files) currently owned by the user. 5. **isoft**: Soft limit for inode count (or 0 if none) 6. **ihard**: Hard limit for inode count (or 0 if none) 7. **itime**: Time when an exceeded soft inode limit turns into a hard limit. This value is meaningless when the soft limit is not exceeded. When a hard limit is reached, the OS will reject any further write with errno *EDQUOT* (or *ENOSPC* on older systems). If the soft limit is exceeded, but hard limit not exceeded, writes by this user will fail only after the time indicated by *btime* or *itime* respectively is reached. The time is usually set to 7 days after exceeding the soft limit for the first time. These times are expressed as elapsed seconds since 00:00 1/Jan/1970 GMT. Note when hard and soft limits are both zero, this means there is no limit for that user. (On some platforms the query may fail with error code *ESRCH* in that case; most however still report valid usage values.) Optional keyword-only parameters: :grpquota: When parameter **grpquota** is present and set to a value that evaluates to *True*, the value in *uid* is taken as GID and group quotas are queried. Group quotas may not be supported across all platforms (e.g. Linux and other BSD based Unix variants, OSF/1 and AIX - check the quotactl(2) man page on your systems). :prjquota: When parameter **prjquota** is present and set to a value that evaluates to *True*, project quotas are queried; this is currently only supported for XFS. Exception **FsQuota.error(ENOTSUP)** is raised for unsupported file-systems. It is an error to select both group and project quota in the same query. Method Quota.setqlim() ---------------------- :: qObj.setqlim(uid, bsoft, bhard, isoft, ihard [,keyword options...]) Sets quota limits for the given user. Meanings of parameters *uid*, *bsoft*, *bhard*, *isoft* and *ihard* are the same as for the **query()** method. Note all the limit values are optional and default to zero. The parameters can also be passed in form of keyword parameters. For example `qObj.setqlim(uid, isoft=10,ihard=20)` would limit inode counts to 10 soft, 20 hard, but remove limits for block count. (Note it's not possible to set only block or inode limits repsectively; to do so query current limits first and then pass those values to setqlim if you want to keep them unchanged.) Note: if you want to set the quota of a particular user to zero, i.e. no write permission, you must not set all limits to zero, since that is equivalent to unlimited access. Instead set only the hard limit to 0 and the soft limit to a non-zero value. Optional keyword-only parameters: :timereset: Optional parameter **timereset** defines how time limits are initialized: When the assigned value is *False*, time limits are set to `NOT STARTED` (i.e. the time limits are not initialized until the first write attempt by this user). This is the default when the parameter is omitted. When assigned *True*, the time limits are set to `7.0 days`. More alternatives (i.e. setting a specific time) aren't available in most implementations. :grpquota: When parameter **grpquota** is present and set to True, parameter *uid* is interpreted as GID and the the limit of the corresponding group is modified. This is not supported on all platforms. :prjquota: When parameter **prjquota** is present and set to True, project quotas are modified; this is currently only supported for XFS. Exception **FsQuota.error(ENOTSUP)** is raised for unsupported file-systems. It is an error to select both group and project quota in the same query. Note that the class does not support setting quotas via RPC (even though some implementations of *rpc.rquotad(8)* allow optionally enabling this, but it seems a bad idea for security.) Method Quota.sync() ------------------- :: qObj.sync() Have the kernel update the quota file on disk, in particular after modifying quota limits. A secondary purpose of this method is checking if quota support is enabled in the kernel (and on some platforms, for a particular file system; on others however the call succeeds even if quota is not enabled in the given file system.) Read the **quotaon(1m)** man page on how to enable quotas on a file system. Method Quota.rpc_opt() ---------------------- :: qObj.rpc_opt([keyword options...]) This method allows configuring networking and authentication parameters for queries of network file system quotas via RPC. The options have no effect when targeting other file system types. The following keyword-only parameters are available: :rpc_port: Sets the port used by *rpc.rquotad(8)*; default value is zero, which which means the remote host's portmapper (aka rpcbind) is used. (Note in case of the latter you can find out the port using *rpcinfo -p host*) :rpc_use_tcp: If *True*, use TCP; if *False* use UDP (default). :rpc_timeout: Timeout value in milliseconds in case the remote host does not respond. :auth_uid: UID value (i.e. user identifier) to provide for authentication. If not specified, this defaults to the UID of the current process. For example, you could set the UID here that you later want to query, for circumventing a permission error. :auth_gid: GID value (i.e. group identifier) to provide for authentication. If not specified, this defaults to the GID of the current process. :auth_hostname: Hostname to provide for authentication. If not specified or empty, this defaults to the name of the local machine. Note for resetting to default authentication, set both **auth_uid** and **auth_gid** to value -1 (even if you previously changed only one, as the opposite is filled in automatically if missing). Attribute Quota.dev ------------------- This attribute provides the device argument used internally by **query()** and **setqlim()** methods for the selected file system. Attribute Quota.is_nfs ---------------------- This attribute indicates 1 is the file system is NFS, else 0. Class FsQuota.MntTab() ====================== This class defines objects that can be used as an iterator which lists all entries in the mount table. Each object returned by iteration is a named tuple of type **FsQuota.MntEnt** with the following entries of type string: 0. **mnt_fsname**: Name of the filesystem (e.g. device name) 1. **mnt_dir**: Filesystem path prefix (aka mount point) 2. **mnt_type**: Mount type (aka file system type) 3. **mnt_opts**: Mount options, separated by colon. Note the mount table contains information about all currently mounted (local or remote) file systems. The format and location of this table varies from system to system (e.g. it may be in file `/etc/mtab`). This iterator provides a portable way to read it. (On some systems, like **OSF/1**, this table isn't accessible as a file at all, i.e. only via C library interfaces). Internally, the iterator will call *setmntent(3)* or the equivalent of your platform upon initialization, call *getmntent(3)* during iteration, and call *endmntent(3)* upon deallocation. Hint: For finding the mount table entry corresponding to a given path (e.g. to determine the file system type), you can compare the device ID indicated by *os.stat(path).st_dev* of the mount points returned from iteration with that of the path in question. ERROR HANDLING ============== All methods raise exception **FsQuota.error** upon errors. The exception class is derived from exception **OSError** and thus contains firstly a numerical error code in attribute **errno** (copied from *errno* in most cases), secondly a derived error message in attribute **strerror**, and when applicable, thirdly a file name in attribute **filename**. Note the error string is adapted to the context of quota operations and therefore not always identical to the text returned by **strerror(ex.errno)**. This is necessary as normal error descriptions don't always make sense for quota errors (e.g. *ESRCH*: *No such process*, here: *No quota for this user*) AUTHORS ======= This module is derived from an equivalent extension module for Perl, created 1995 by T. Zoerner (email: tomzo AT users.sourceforge.net) and since then continuously improved and ported to many more operating systems and file systems - and now ported to Python. Numerous people have contributed to this process in the past; for a complete list of names please see the CHANGES document. LICENSE ======= Copyright (C) 1995-2020 T. Zoerner This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. (Either version 2 of the GPL, or any later version, see http://www.opensource.org/licenses/). This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. SEE ALSO ======== python3(1), edquota(8), quotactl(2) or quotactl(7I), mount(8), mtab(4) or mnttab(4), quotaon(8), setmntent(3), getmntent(3) or getmntinfo(3), endmntent(3), rpc(3), rquotad(8) or rpc.rquotad(8), rpcinfo(7). Keywords: file-system,quota,quotactl,mtab,getmntent Platform: posix Classifier: Development Status :: 4 - Beta Classifier: Programming Language :: C Classifier: Programming Language :: Python :: 3 Classifier: Topic :: System :: Filesystems Classifier: Topic :: System :: Systems Administration Classifier: Intended Audience :: Developers Classifier: Intended Audience :: System Administrators Classifier: Operating System :: POSIX :: Linux Classifier: Operating System :: POSIX :: BSD Classifier: Operating System :: POSIX :: AIX Classifier: Operating System :: POSIX :: HP-UX Classifier: Operating System :: POSIX :: IRIX Classifier: Operating System :: POSIX :: SunOS/Solaris Classifier: License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+) Requires-Python: >=3.2 Description-Content-Type: text/x-rst python-fsquota-0.1.0+dfsg1/FsQuota.egg-info/SOURCES.txt000066400000000000000000000012261400261757100224550ustar00rootroot00000000000000CHANGES INSTALL LICENSE MANIFEST.in README.md TODO setup.py FsQuota.egg-info/PKG-INFO FsQuota.egg-info/SOURCES.txt FsQuota.egg-info/dependency_links.txt FsQuota.egg-info/top_level.txt doc/FsQuota.rst doc/README.rst hints/aix_4_1.h hints/bsd.h hints/dec_osf.h hints/hpux.h hints/irix_5.h hints/irix_6.h hints/linux.h hints/none.h hints/solaris_2.h hints/sunos_4_1.h src/FsQuota.c src/afsquota.c src/afsquota.h src/linuxapi.c src/quotaio_xfs.h src/rquota.h src/vxquotactl.c src/vxquotactl.h tests/README.md tests/test_64bit.py tests/test_ALL_interactively.py tests/test_ALL_smoke.py tests/test_getqcarg.py tests/test_group.py tests/test_mnttab.py tests/test_rpc.pypython-fsquota-0.1.0+dfsg1/FsQuota.egg-info/dependency_links.txt000066400000000000000000000000011400261757100246360ustar00rootroot00000000000000 python-fsquota-0.1.0+dfsg1/FsQuota.egg-info/top_level.txt000066400000000000000000000000101400261757100233110ustar00rootroot00000000000000FsQuota python-fsquota-0.1.0+dfsg1/INSTALL000066400000000000000000000060551400261757100165530ustar00rootroot00000000000000Installation Instructions ------------------------- Usually the module should install automatically using PIP. Note since the module is implemented in C, a C compiler and kernel-headers are pre-requisite for installation from source. When installing manually use the following steps: python3 setup.py test env PYTHONPATH=. python3 tests/test_ALL_smoke.py env PYTHONPATH=. python3 tests/test_ALL_interactively.py python3 setup.py install If any step fails, regard the following instructions step by step. Options: -> Support for Solaris VxFS should be added automatically if the module is installed. If not, #define or #undef SOLARIS_VXFS in myconfig.h -> If you want support for AFS, you need the OpenAFS package (tested with OpenAFS on (SuSE) Linux, HP-UX10, Solaris 2.6, 2.8, AIX 4.3 and IRIX 6.5). The Makefile.PL tries to detect automatically if AFS is preset on a system. If that fails, edit the Makefile.PL to always define $hasafs (and drop me a mail). 1) On Linux systems make sure you have configured your kernel with quota support. You also need the quota utilities and headers. E.g., with the SuSE Linux 6.0 distribution you'll find them in package ap::quota. If your distribution doesn't include the package you can get it from . See also (6) below. Since 2019, SUN-RPC support has been split off from glibc in some Linux distributions. If you run into compilation problems due to missing header rpc/rpc.h, install package "libtirpc-dev" 2) Link or create the hints file. a) Should be done by Makefile.PL for all supported systems. If not, and there is a file below hints/ for your operating system (maybe for a previous release), just create a link named "myconfig.h" to that file: ln -s hints/my-os.h myconfig.h b) Else, use hints/none.h to create a myconfig.h of your own. Check which #includes you need in the online manuals. Contact me before you invest a lot of time, it might already be done. Edit Makefile.PL to maybe add/delete some libraries. Usually you'll need only librpcsvc for the quota RPC mechanism. If you don't have this library or it lacks the quota routines, #define MY_XDR in myconfig.h to include the routines provided with this module. If you don't have /usr/include/rpcsvc/rquota.h, include "include/rquota.h" instead. If you don't need RPC at all, just define NO_RPC. 3) Generate the Makefile & compile: python3 setup.py test 4) Run tests/test_ALL_interactively.py to check if the module routines do work correctly. Run the script once for user-quota and once group-quota, and for different file-systems, as needed. 5) To install the module: "python3 setup.py install" 6) Before you start building for another OS, type "python3 setup.py clean" Please email me with any adaption required in the hints files or setup.py for getting the package to compile on your system. Please remember to include the name of the OS and version numbers of OS (uname -rs) and quota module. python-fsquota-0.1.0+dfsg1/LICENSE000066400000000000000000000432541400261757100165310ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. python-fsquota-0.1.0+dfsg1/MANIFEST.in000066400000000000000000000002361400261757100172530ustar00rootroot00000000000000include src/* include hints/* include tests/* include doc/* include CHANGES include INSTALL include LICENSE include TODO include MANIFEST.in include setup.py python-fsquota-0.1.0+dfsg1/PKG-INFO000066400000000000000000000447051400261757100166230ustar00rootroot00000000000000Metadata-Version: 2.1 Name: FsQuota Version: 0.1.0 Summary: Interface to file system quotas on UNIX platforms Home-page: https://github.com/tomzox/Python-Quota Author: T. Zoerner Author-email: tomzo@users.sourceforge.net License: GNU GPLv2+ Description: =============================== Python File-system Quota module =============================== The Python file-system quota module allows accessing file system quotas on UNIX platforms from Python scripts. The module is intended mainly for system administrators who have to manage quotas for all the users on their system. The following operating systems and file systems are supported transparently through a common API. Supported operating systems: * Linux - kernel 2.0.30 - 4.15 * FreeBSD 3 - 12.1, OpenBSD 2.2 - 6.6 & NetBSD 5 - 9 * SunOS 4.1.3 (aka Solaris 1) * Solaris 2.4 - 2.10 * HP-UX 9.0x & 10.10 & 10.20 & 11.00 * IRIX 5.2 & 5.3 & 6.2 - 6.5 * OSF/1 & Digital Unix 4 * AIX 4.1, 4.2 and 5.3 Supported file systems: * Standard file systems of the platforms listed above * NFS (Network file system) on all of the above (i.e. using an integrated RPC client) * XFS on Linux and IRIX 6 * AFS (Andrew File System) on many of the above (see INSTALL) * VxFS (Veritas File System) on Solaris 2 Historical note: The C implementation of this module is derived from the `Quota module for Perl`_ (also at `CPAN`_). Since its beginnings in 1995, the module was continuously extended by porting to more UNIX platforms and file-systems. Numerous people have contributed to this process; for a complete list of names please see the CHANGES document in the package. In case of build issues, please refer to the INSTALL document within the package. .. _Quota module for Perl: https://github.com/tomzox/Perl-Quota .. _CPAN: https://metacpan.org/pod/Quota The following is a copy of the API documentation in file doc/FsQuota.rst SYNOPSIS ======== :: import FsQuota qObj = FsQuota.Quota(path [,rpc_host=hostname]) (bcount, bsoft, bhard, btime, icount, isoft, ihard, itime) = qObj.query(uid [,grpquota=1] [,prjquota=1]) qObj.setqlim(uid, bsoft, bhard, isoft, ihard [,timereset=1] [,grpquota=1] [,prjquota=1]) qObj.sync() qObj.rpc_opt([option keywords]) for dev, path, type, opts in FsQuota.MntTab(): ... FsQuota Module ============== The **FsQuota** module provides two classes that allow accessing file system quotas from Python scripts: Instances of the **Quota** class take as main init parameter a path of a mount point (or any path below the mount point). The instance can then be used to query or modify quota of users or groups within that file system. The class is designed portably, so that the same interfaces work across all file system types and UNIX platforms. (Although there are some extra options during initialization for certain file systems.) Instances of the **MntTab** class allow iterating across the mount table. For each entry in the table, it provides the file system type, mount point and options. (Note this class is usually not required to work with the Quota class. It is provided here just for convenience, as the functionality is actually used internally by the Quota class.) Class FsQuota.Quota =================== :: qObj = FsQuota.Quota(path) qObj = FsQuota.Quota(remote_path, rpc_host=remote_host) Creates a Quota object that then is used for querying or modifying quotas. In case of special file systems which are known not to suport quota, the creation may raise exception **FsQuota.error**. However note the absence of an exception is not a guarantee that the file system actually supports quota limits. Internal behavior: Most importantly, the initialization determines the file system type and thus the access method to be used in following quota operations. Many platforms use the **quotactl** syscall, but even then the type of device parameter to be passed varies from system to system. It may be the path of a device file (e.g. `/dev/sda1`) or the path of the mount point or the quotas file at the top of the file system (e.g. `/home/quotas`). For the rare cases you need this information, it can be queried via the **Quota.dev** attribute. The given mount point may also be on a remote file system (e.g. mounted via Network File System, NFS), which has the class transparently query the given host via a remote procedure call (RPC). Note: RPC queries require *rquotad(1m)* to be running on the target system. If the daemon or host are down, the operations time out after a configurable delay. When parameter **rpc_host** is specified, the automatic detection of file system type is omitted. In this case the following operations will address the file system containing the given path on the given remote host using RPC. This mode should normally not be needed, but could for example be used for accessing file systems that are not mounted locally. See also the **rpc_opt()** method for additional RPC configuration options. Quota.query() ------------- :: (bcount,bsoft,bhard,btime, icount,isoft,ihard,itime) = qObj.query(uid, [keyword_options...]) Get current usage and quota limits for blocks and files respectively, owned by the given user. The user is specified by a numeric UID. The result is a named tuple of type **FsQuota.QueryResult**, so that members can be accessed via name as well as via indices: 0. **bcount**: Number of 1 kB blocks currently used by inodes owned by the user. 1. **bsoft**: Soft limit for block count (or 0 if none) 2. **bhard**: Hard limit for block count (or 0 if none) 3. **btime**: Time when an exceeded soft block limit turns into a hard limit. This value is meaningless when the soft limit is not exceeded. 4. **icount**: Number of inodes (i.e. files) currently owned by the user. 5. **isoft**: Soft limit for inode count (or 0 if none) 6. **ihard**: Hard limit for inode count (or 0 if none) 7. **itime**: Time when an exceeded soft inode limit turns into a hard limit. This value is meaningless when the soft limit is not exceeded. When a hard limit is reached, the OS will reject any further write with errno *EDQUOT* (or *ENOSPC* on older systems). If the soft limit is exceeded, but hard limit not exceeded, writes by this user will fail only after the time indicated by *btime* or *itime* respectively is reached. The time is usually set to 7 days after exceeding the soft limit for the first time. These times are expressed as elapsed seconds since 00:00 1/Jan/1970 GMT. Note when hard and soft limits are both zero, this means there is no limit for that user. (On some platforms the query may fail with error code *ESRCH* in that case; most however still report valid usage values.) Optional keyword-only parameters: :grpquota: When parameter **grpquota** is present and set to a value that evaluates to *True*, the value in *uid* is taken as GID and group quotas are queried. Group quotas may not be supported across all platforms (e.g. Linux and other BSD based Unix variants, OSF/1 and AIX - check the quotactl(2) man page on your systems). :prjquota: When parameter **prjquota** is present and set to a value that evaluates to *True*, project quotas are queried; this is currently only supported for XFS. Exception **FsQuota.error(ENOTSUP)** is raised for unsupported file-systems. It is an error to select both group and project quota in the same query. Method Quota.setqlim() ---------------------- :: qObj.setqlim(uid, bsoft, bhard, isoft, ihard [,keyword options...]) Sets quota limits for the given user. Meanings of parameters *uid*, *bsoft*, *bhard*, *isoft* and *ihard* are the same as for the **query()** method. Note all the limit values are optional and default to zero. The parameters can also be passed in form of keyword parameters. For example `qObj.setqlim(uid, isoft=10,ihard=20)` would limit inode counts to 10 soft, 20 hard, but remove limits for block count. (Note it's not possible to set only block or inode limits repsectively; to do so query current limits first and then pass those values to setqlim if you want to keep them unchanged.) Note: if you want to set the quota of a particular user to zero, i.e. no write permission, you must not set all limits to zero, since that is equivalent to unlimited access. Instead set only the hard limit to 0 and the soft limit to a non-zero value. Optional keyword-only parameters: :timereset: Optional parameter **timereset** defines how time limits are initialized: When the assigned value is *False*, time limits are set to `NOT STARTED` (i.e. the time limits are not initialized until the first write attempt by this user). This is the default when the parameter is omitted. When assigned *True*, the time limits are set to `7.0 days`. More alternatives (i.e. setting a specific time) aren't available in most implementations. :grpquota: When parameter **grpquota** is present and set to True, parameter *uid* is interpreted as GID and the the limit of the corresponding group is modified. This is not supported on all platforms. :prjquota: When parameter **prjquota** is present and set to True, project quotas are modified; this is currently only supported for XFS. Exception **FsQuota.error(ENOTSUP)** is raised for unsupported file-systems. It is an error to select both group and project quota in the same query. Note that the class does not support setting quotas via RPC (even though some implementations of *rpc.rquotad(8)* allow optionally enabling this, but it seems a bad idea for security.) Method Quota.sync() ------------------- :: qObj.sync() Have the kernel update the quota file on disk, in particular after modifying quota limits. A secondary purpose of this method is checking if quota support is enabled in the kernel (and on some platforms, for a particular file system; on others however the call succeeds even if quota is not enabled in the given file system.) Read the **quotaon(1m)** man page on how to enable quotas on a file system. Method Quota.rpc_opt() ---------------------- :: qObj.rpc_opt([keyword options...]) This method allows configuring networking and authentication parameters for queries of network file system quotas via RPC. The options have no effect when targeting other file system types. The following keyword-only parameters are available: :rpc_port: Sets the port used by *rpc.rquotad(8)*; default value is zero, which which means the remote host's portmapper (aka rpcbind) is used. (Note in case of the latter you can find out the port using *rpcinfo -p host*) :rpc_use_tcp: If *True*, use TCP; if *False* use UDP (default). :rpc_timeout: Timeout value in milliseconds in case the remote host does not respond. :auth_uid: UID value (i.e. user identifier) to provide for authentication. If not specified, this defaults to the UID of the current process. For example, you could set the UID here that you later want to query, for circumventing a permission error. :auth_gid: GID value (i.e. group identifier) to provide for authentication. If not specified, this defaults to the GID of the current process. :auth_hostname: Hostname to provide for authentication. If not specified or empty, this defaults to the name of the local machine. Note for resetting to default authentication, set both **auth_uid** and **auth_gid** to value -1 (even if you previously changed only one, as the opposite is filled in automatically if missing). Attribute Quota.dev ------------------- This attribute provides the device argument used internally by **query()** and **setqlim()** methods for the selected file system. Attribute Quota.is_nfs ---------------------- This attribute indicates 1 is the file system is NFS, else 0. Class FsQuota.MntTab() ====================== This class defines objects that can be used as an iterator which lists all entries in the mount table. Each object returned by iteration is a named tuple of type **FsQuota.MntEnt** with the following entries of type string: 0. **mnt_fsname**: Name of the filesystem (e.g. device name) 1. **mnt_dir**: Filesystem path prefix (aka mount point) 2. **mnt_type**: Mount type (aka file system type) 3. **mnt_opts**: Mount options, separated by colon. Note the mount table contains information about all currently mounted (local or remote) file systems. The format and location of this table varies from system to system (e.g. it may be in file `/etc/mtab`). This iterator provides a portable way to read it. (On some systems, like **OSF/1**, this table isn't accessible as a file at all, i.e. only via C library interfaces). Internally, the iterator will call *setmntent(3)* or the equivalent of your platform upon initialization, call *getmntent(3)* during iteration, and call *endmntent(3)* upon deallocation. Hint: For finding the mount table entry corresponding to a given path (e.g. to determine the file system type), you can compare the device ID indicated by *os.stat(path).st_dev* of the mount points returned from iteration with that of the path in question. ERROR HANDLING ============== All methods raise exception **FsQuota.error** upon errors. The exception class is derived from exception **OSError** and thus contains firstly a numerical error code in attribute **errno** (copied from *errno* in most cases), secondly a derived error message in attribute **strerror**, and when applicable, thirdly a file name in attribute **filename**. Note the error string is adapted to the context of quota operations and therefore not always identical to the text returned by **strerror(ex.errno)**. This is necessary as normal error descriptions don't always make sense for quota errors (e.g. *ESRCH*: *No such process*, here: *No quota for this user*) AUTHORS ======= This module is derived from an equivalent extension module for Perl, created 1995 by T. Zoerner (email: tomzo AT users.sourceforge.net) and since then continuously improved and ported to many more operating systems and file systems - and now ported to Python. Numerous people have contributed to this process in the past; for a complete list of names please see the CHANGES document. LICENSE ======= Copyright (C) 1995-2020 T. Zoerner This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. (Either version 2 of the GPL, or any later version, see http://www.opensource.org/licenses/). This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. SEE ALSO ======== python3(1), edquota(8), quotactl(2) or quotactl(7I), mount(8), mtab(4) or mnttab(4), quotaon(8), setmntent(3), getmntent(3) or getmntinfo(3), endmntent(3), rpc(3), rquotad(8) or rpc.rquotad(8), rpcinfo(7). Keywords: file-system,quota,quotactl,mtab,getmntent Platform: posix Classifier: Development Status :: 4 - Beta Classifier: Programming Language :: C Classifier: Programming Language :: Python :: 3 Classifier: Topic :: System :: Filesystems Classifier: Topic :: System :: Systems Administration Classifier: Intended Audience :: Developers Classifier: Intended Audience :: System Administrators Classifier: Operating System :: POSIX :: Linux Classifier: Operating System :: POSIX :: BSD Classifier: Operating System :: POSIX :: AIX Classifier: Operating System :: POSIX :: HP-UX Classifier: Operating System :: POSIX :: IRIX Classifier: Operating System :: POSIX :: SunOS/Solaris Classifier: License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+) Requires-Python: >=3.2 Description-Content-Type: text/x-rst python-fsquota-0.1.0+dfsg1/README.md000066400000000000000000000041071400261757100167750ustar00rootroot00000000000000# Python File-system Quota module This repository contains the sources of the Python file-system quota module, which has its official home at [PyPi](https://pypi.org/project/FsQuota/). The quota module allows accessing file system quotas on UNIX platforms. This works both for locally mounted file systems and network file systems (via RPC, i.e. Remote Procedure Call) for all the operating systems listed below. The interface is designed to be independent of UNIX flavours as well as file system types. The C implementation of this module is derived from the [Quota module for Perl](https://github.com/tomzox/Perl-Quota) (also at [CPAN](https://metacpan.org/pod/Quota)). I started developing the Perl module 1995, while working as a UNIX system administrator at university and kept maintaining it even after no longer working in this capacity. Since its beginnings, the module was continuously extended by porting to more UNIX platforms and file-systems. Numerous people have contributed to this process; for a complete list of names please see the CHANGES document in the repository. All this effort is now available also to Python users. ## Module information The following operating systems and file systems are supported transparently through a common API. Supported operating systems: * Linux - kernel 2.0.30 - 4.15 * FreeBSD 3 - 12.1, OpenBSD 2.2 - 6.6 & NetBSD 5 - 9 * SunOS 4.1.3 (aka Solaris 1) * Solaris 2.4 - 2.10 * HP-UX 9.0x & 10.10 & 10.20 & 11.00 * IRIX 5.2 & 5.3 & 6.2 - 6.5 * OSF/1 & Digital Unix 4 * AIX 4.1, 4.2 and 5.3 Supported file systems: * Standard file systems of the platforms listed above * NFS (Network file system) on all of the above * XFS on Linux and IRIX 6 * AFS (Andrew File System) on many of the above (see INSTALL) * VxFS (Veritas File System) on Solaris 2 ## Documentation For further information please refer to the following files: * FsQuota.rst: API documentation * INSTALL: Installation description * CHANGES: Change log & acknowledgements * LICENSE: GPL License python-fsquota-0.1.0+dfsg1/TODO000066400000000000000000000011371400261757100162060ustar00rootroot00000000000000optional enhancements: - RPC use persistent client connection (configurable) add support for __enter__ and __exit__ for "with" - NetBSD: call quota_open() only once during __init__ - FreeBSD >= 8.1: could use quotafile functions in -lutil - support setting quota limits via RPC (v2) - getmntent: distinguish end-of-list from error (when possible)? - use tirpc even if SUN-RPC is implemented in libc too: requires linker options to control symbol resolution order - automated unit-tests (as far as possible) - automatically define NDEBUG for debug builds (if possible?) - generate UNIX man page for dist python-fsquota-0.1.0+dfsg1/doc/000077500000000000000000000000001400261757100162615ustar00rootroot00000000000000python-fsquota-0.1.0+dfsg1/doc/FsQuota.rst000066400000000000000000000321031400261757100203740ustar00rootroot00000000000000=========================================================== FsQuota - Interface to file system quotas on UNIX platforms =========================================================== SYNOPSIS ======== :: import FsQuota qObj = FsQuota.Quota(path [,rpc_host=hostname]) (bcount, bsoft, bhard, btime, icount, isoft, ihard, itime) = qObj.query(uid [,grpquota=1] [,prjquota=1]) qObj.setqlim(uid, bsoft, bhard, isoft, ihard [,timereset=1] [,grpquota=1] [,prjquota=1]) qObj.sync() qObj.rpc_opt([option keywords]) for dev, path, type, opts in FsQuota.MntTab(): ... FsQuota Module ============== The **FsQuota** module provides two classes that allow accessing file system quotas from Python scripts: Instances of the **Quota** class take as main init parameter a path of a mount point (or any path below the mount point). The instance can then be used to query or modify quota of users or groups within that file system. The class is designed portably, so that the same interfaces work across all file system types and UNIX platforms. (Although there are some extra options during initialization for certain file systems.) Instances of the **MntTab** class allow iterating across the mount table. For each entry in the table, it provides the file system type, mount point and options. (Note this class is usually not required to work with the Quota class. It is provided here just for convenience, as the functionality is actually used internally by the Quota class.) Class FsQuota.Quota =================== :: qObj = FsQuota.Quota(path) qObj = FsQuota.Quota(remote_path, rpc_host=remote_host) Creates a Quota object that then is used for querying or modifying quotas. In case of special file systems which are known not to suport quota, the creation may raise exception **FsQuota.error**. However note the absence of an exception is not a guarantee that the file system actually supports quota limits. Internal behavior: Most importantly, the initialization determines the file system type and thus the access method to be used in following quota operations. Many platforms use the **quotactl** syscall, but even then the type of device parameter to be passed varies from system to system. It may be the path of a device file (e.g. `/dev/sda1`) or the path of the mount point or the quotas file at the top of the file system (e.g. `/home/quotas`). For the rare cases you need this information, it can be queried via the **Quota.dev** attribute. The given mount point may also be on a remote file system (e.g. mounted via Network File System, NFS), which has the class transparently query the given host via a remote procedure call (RPC). Note: RPC queries require *rquotad(1m)* to be running on the target system. If the daemon or host are down, the operations time out after a configurable delay. When parameter **rpc_host** is specified, the automatic detection of file system type is omitted. In this case the following operations will address the file system containing the given path on the given remote host using RPC. This mode should normally not be needed, but could for example be used for accessing file systems that are not mounted locally. See also the **rpc_opt()** method for additional RPC configuration options. Quota.query() ------------- :: (bcount,bsoft,bhard,btime, icount,isoft,ihard,itime) = qObj.query(uid, [keyword_options...]) Get current usage and quota limits for blocks and files respectively, owned by the given user. The user is specified by a numeric UID. The result is a named tuple of type **FsQuota.QueryResult**, so that members can be accessed via name as well as via indices: 0. **bcount**: Number of 1 kB blocks currently used by inodes owned by the user. 1. **bsoft**: Soft limit for block count (or 0 if none) 2. **bhard**: Hard limit for block count (or 0 if none) 3. **btime**: Time when an exceeded soft block limit turns into a hard limit. This value is meaningless when the soft limit is not exceeded. 4. **icount**: Number of inodes (i.e. files) currently owned by the user. 5. **isoft**: Soft limit for inode count (or 0 if none) 6. **ihard**: Hard limit for inode count (or 0 if none) 7. **itime**: Time when an exceeded soft inode limit turns into a hard limit. This value is meaningless when the soft limit is not exceeded. When a hard limit is reached, the OS will reject any further write with errno *EDQUOT* (or *ENOSPC* on older systems). If the soft limit is exceeded, but hard limit not exceeded, writes by this user will fail only after the time indicated by *btime* or *itime* respectively is reached. The time is usually set to 7 days after exceeding the soft limit for the first time. These times are expressed as elapsed seconds since 00:00 1/Jan/1970 GMT. Note when hard and soft limits are both zero, this means there is no limit for that user. (On some platforms the query may fail with error code *ESRCH* in that case; most however still report valid usage values.) Optional keyword-only parameters: :grpquota: When parameter **grpquota** is present and set to a value that evaluates to *True*, the value in *uid* is taken as GID and group quotas are queried. Group quotas may not be supported across all platforms (e.g. Linux and other BSD based Unix variants, OSF/1 and AIX - check the quotactl(2) man page on your systems). :prjquota: When parameter **prjquota** is present and set to a value that evaluates to *True*, project quotas are queried; this is currently only supported for XFS. Exception **FsQuota.error(ENOTSUP)** is raised for unsupported file-systems. It is an error to select both group and project quota in the same query. Method Quota.setqlim() ---------------------- :: qObj.setqlim(uid, bsoft, bhard, isoft, ihard [,keyword options...]) Sets quota limits for the given user. Meanings of parameters *uid*, *bsoft*, *bhard*, *isoft* and *ihard* are the same as for the **query()** method. Note all the limit values are optional and default to zero. The parameters can also be passed in form of keyword parameters. For example `qObj.setqlim(uid, isoft=10,ihard=20)` would limit inode counts to 10 soft, 20 hard, but remove limits for block count. (Note it's not possible to set only block or inode limits repsectively; to do so query current limits first and then pass those values to setqlim if you want to keep them unchanged.) Note: if you want to set the quota of a particular user to zero, i.e. no write permission, you must not set all limits to zero, since that is equivalent to unlimited access. Instead set only the hard limit to 0 and the soft limit to a non-zero value. Optional keyword-only parameters: :timereset: Optional parameter **timereset** defines how time limits are initialized: When the assigned value is *False*, time limits are set to `NOT STARTED` (i.e. the time limits are not initialized until the first write attempt by this user). This is the default when the parameter is omitted. When assigned *True*, the time limits are set to `7.0 days`. More alternatives (i.e. setting a specific time) aren't available in most implementations. :grpquota: When parameter **grpquota** is present and set to True, parameter *uid* is interpreted as GID and the the limit of the corresponding group is modified. This is not supported on all platforms. :prjquota: When parameter **prjquota** is present and set to True, project quotas are modified; this is currently only supported for XFS. Exception **FsQuota.error(ENOTSUP)** is raised for unsupported file-systems. It is an error to select both group and project quota in the same query. Note that the class does not support setting quotas via RPC (even though some implementations of *rpc.rquotad(8)* allow optionally enabling this, but it seems a bad idea for security.) Method Quota.sync() ------------------- :: qObj.sync() Have the kernel update the quota file on disk, in particular after modifying quota limits. A secondary purpose of this method is checking if quota support is enabled in the kernel (and on some platforms, for a particular file system; on others however the call succeeds even if quota is not enabled in the given file system.) Read the **quotaon(1m)** man page on how to enable quotas on a file system. Method Quota.rpc_opt() ---------------------- :: qObj.rpc_opt([keyword options...]) This method allows configuring networking and authentication parameters for queries of network file system quotas via RPC. The options have no effect when targeting other file system types. The following keyword-only parameters are available: :rpc_port: Sets the port used by *rpc.rquotad(8)*; default value is zero, which which means the remote host's portmapper (aka rpcbind) is used. (Note in case of the latter you can find out the port using *rpcinfo -p host*) :rpc_use_tcp: If *True*, use TCP; if *False* use UDP (default). :rpc_timeout: Timeout value in milliseconds in case the remote host does not respond. :auth_uid: UID value (i.e. user identifier) to provide for authentication. If not specified, this defaults to the UID of the current process. For example, you could set the UID here that you later want to query, for circumventing a permission error. :auth_gid: GID value (i.e. group identifier) to provide for authentication. If not specified, this defaults to the GID of the current process. :auth_hostname: Hostname to provide for authentication. If not specified or empty, this defaults to the name of the local machine. Note for resetting to default authentication, set both **auth_uid** and **auth_gid** to value -1 (even if you previously changed only one, as the opposite is filled in automatically if missing). Attribute Quota.dev ------------------- This attribute provides the device argument used internally by **query()** and **setqlim()** methods for the selected file system. Attribute Quota.is_nfs ---------------------- This attribute indicates 1 is the file system is NFS, else 0. Class FsQuota.MntTab() ====================== This class defines objects that can be used as an iterator which lists all entries in the mount table. Each object returned by iteration is a named tuple of type **FsQuota.MntEnt** with the following entries of type string: 0. **mnt_fsname**: Name of the filesystem (e.g. device name) 1. **mnt_dir**: Filesystem path prefix (aka mount point) 2. **mnt_type**: Mount type (aka file system type) 3. **mnt_opts**: Mount options, separated by colon. Note the mount table contains information about all currently mounted (local or remote) file systems. The format and location of this table varies from system to system (e.g. it may be in file `/etc/mtab`). This iterator provides a portable way to read it. (On some systems, like **OSF/1**, this table isn't accessible as a file at all, i.e. only via C library interfaces). Internally, the iterator will call *setmntent(3)* or the equivalent of your platform upon initialization, call *getmntent(3)* during iteration, and call *endmntent(3)* upon deallocation. Hint: For finding the mount table entry corresponding to a given path (e.g. to determine the file system type), you can compare the device ID indicated by *os.stat(path).st_dev* of the mount points returned from iteration with that of the path in question. ERROR HANDLING ============== All methods raise exception **FsQuota.error** upon errors. The exception class is derived from exception **OSError** and thus contains firstly a numerical error code in attribute **errno** (copied from *errno* in most cases), secondly a derived error message in attribute **strerror**, and when applicable, thirdly a file name in attribute **filename**. Note the error string is adapted to the context of quota operations and therefore not always identical to the text returned by **strerror(ex.errno)**. This is necessary as normal error descriptions don't always make sense for quota errors (e.g. *ESRCH*: *No such process*, here: *No quota for this user*) AUTHORS ======= This module is derived from an equivalent extension module for Perl, created 1995 by T. Zoerner (email: tomzo AT users.sourceforge.net) and since then continuously improved and ported to many more operating systems and file systems - and now ported to Python. Numerous people have contributed to this process in the past; for a complete list of names please see the CHANGES document. LICENSE ======= Copyright (C) 1995-2020 T. Zoerner This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. (Either version 2 of the GPL, or any later version, see http://www.opensource.org/licenses/). This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. SEE ALSO ======== python3(1), edquota(8), quotactl(2) or quotactl(7I), mount(8), mtab(4) or mnttab(4), quotaon(8), setmntent(3), getmntent(3) or getmntinfo(3), endmntent(3), rpc(3), rquotad(8) or rpc.rquotad(8), rpcinfo(7). python-fsquota-0.1.0+dfsg1/doc/README.rst000066400000000000000000000031351400261757100177520ustar00rootroot00000000000000=============================== Python File-system Quota module =============================== The Python file-system quota module allows accessing file system quotas on UNIX platforms from Python scripts. The module is intended mainly for system administrators who have to manage quotas for all the users on their system. The following operating systems and file systems are supported transparently through a common API. Supported operating systems: * Linux - kernel 2.0.30 - 4.15 * FreeBSD 3 - 12.1, OpenBSD 2.2 - 6.6 & NetBSD 5 - 9 * SunOS 4.1.3 (aka Solaris 1) * Solaris 2.4 - 2.10 * HP-UX 9.0x & 10.10 & 10.20 & 11.00 * IRIX 5.2 & 5.3 & 6.2 - 6.5 * OSF/1 & Digital Unix 4 * AIX 4.1, 4.2 and 5.3 Supported file systems: * Standard file systems of the platforms listed above * NFS (Network file system) on all of the above (i.e. using an integrated RPC client) * XFS on Linux and IRIX 6 * AFS (Andrew File System) on many of the above (see INSTALL) * VxFS (Veritas File System) on Solaris 2 Historical note: The C implementation of this module is derived from the `Quota module for Perl`_ (also at `CPAN`_). Since its beginnings in 1995, the module was continuously extended by porting to more UNIX platforms and file-systems. Numerous people have contributed to this process; for a complete list of names please see the CHANGES document in the package. In case of build issues, please refer to the INSTALL document within the package. .. _Quota module for Perl: https://github.com/tomzox/Perl-Quota .. _CPAN: https://metacpan.org/pod/Quota The following is a copy of the API documentation in file doc/FsQuota.rst python-fsquota-0.1.0+dfsg1/hints/000077500000000000000000000000001400261757100166415ustar00rootroot00000000000000python-fsquota-0.1.0+dfsg1/hints/aix_4_1.h000066400000000000000000000021601400261757100202350ustar00rootroot00000000000000/* * Configuration for AIX 4.1 * * For AFS support look at the end of this file */ /* See hints/none.h for a complete list of options with explanations */ #include #include #include #include #include #include #include #include #include #include #include "src/rquota.h" #include #include #include #include #define AIX #define Q_CTL_V2 #if defined(_AIXVERSION_530) #include "j2/j2_quota.h" #define HAVE_JFS2 #endif #define Q_DIV(X) (X) #define Q_MUL(X) (X) #define DEV_QBSIZE DEV_BSIZE #define CADR (caddr_t) #define GQA_TYPE_USR USRQUOTA /* RQUOTA_USRQUOTA */ #define GQA_TYPE_GRP GRPQUOTA /* RQUOTA_GRPQUOTA */ #define GQR_STATUS status #define GQR_RQUOTA getquota_rslt_u.gqr_rquota #define QS_BHARD dqb_bhardlimit #define QS_BSOFT dqb_bsoftlimit #define QS_BCUR dqb_curblocks #define QS_FHARD dqb_ihardlimit #define QS_FSOFT dqb_isoftlimit #define QS_FCUR dqb_curinodes #define QS_BTIME dqb_btime #define QS_FTIME dqb_itime python-fsquota-0.1.0+dfsg1/hints/dec_osf.h000066400000000000000000000020271400261757100204150ustar00rootroot00000000000000/* * Configuration for DEC OSF/1 V3.2 - 4.0 */ /* See hints/none.h for a complete list of options with explanations */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define Q_DIV(X) (X) #define Q_MUL(X) (X) #define DEV_QBSIZE DEV_BSIZE #define Q_CTL_V2 #define CADR #define NO_MNTENT #define OSF_QUOTA extern char *getvfsbynumber(); /* prototype missing!? */ #define GQA_TYPE_USR USRQUOTA /* RQUOTA_USRQUOTA */ #define GQA_TYPE_GRP GRPQUOTA /* RQUOTA_GRPQUOTA */ #define GQR_STATUS gqr_status #define GQR_RQUOTA gqr_rquota #define QS_BHARD dqb_bhardlimit #define QS_BSOFT dqb_bsoftlimit #define QS_BCUR dqb_curblocks #define QS_FHARD dqb_ihardlimit #define QS_FSOFT dqb_isoftlimit #define QS_FCUR dqb_curinodes #define QS_BTIME dqb_btime #define QS_FTIME dqb_itime python-fsquota-0.1.0+dfsg1/hints/hpux.h000066400000000000000000000021661400261757100200030ustar00rootroot00000000000000/* * Configuration for HP-UX 9.0.x & HP-UX 10.10 & HP-UX 10.20 & HP-UX 11.00 * * For AFS support look at the end of this file */ /* See hints/none.h for a complete list of options with explanations */ #include #include #include #include #include #include #include #include #include #include #include #include #define Q_DIV(X) (X) #define Q_MUL(X) (X) #define DEV_QBSIZE DEV_BSIZE #define CADR (caddr_t) #define MNTENT mntent /* HP-UX has no shared librpcsvc. So we need to include the * XDR routines supplied with this module. */ #define MY_XDR #define GQA_TYPE_USR USRQUOTA /* RQUOTA_USRQUOTA */ #define GQA_TYPE_GRP GRPQUOTA /* RQUOTA_GRPQUOTA */ #define GQR_STATUS gqr_status #define GQR_RQUOTA gqr_rquota #define QS_BHARD dqb_bhardlimit #define QS_BSOFT dqb_bsoftlimit #define QS_BCUR dqb_curblocks #define QS_FHARD dqb_fhardlimit #define QS_FSOFT dqb_fsoftlimit #define QS_FCUR dqb_curfiles #define QS_BTIME dqb_btimelimit #define QS_FTIME dqb_ftimelimit python-fsquota-0.1.0+dfsg1/hints/irix_5.h000066400000000000000000000016321400261757100202130ustar00rootroot00000000000000/* * Configuration for IRIX 5.3 */ /* See hints/none.h for a complete list of options with explanations */ #include #include #include #include #include #include #include #include #include #include #include #include #define Q_DIV(X) (X) #define Q_MUL(X) (X) #define DEV_QBSIZE DEV_BSIZE #define CADR (caddr_t) #define MNTENT mntent #define GQA_TYPE_USR USRQUOTA /* RQUOTA_USRQUOTA */ #define GQA_TYPE_GRP GRPQUOTA /* RQUOTA_GRPQUOTA */ #define GQR_STATUS gqr_status #define GQR_RQUOTA gqr_rquota #define QS_BHARD dqb_bhardlimit #define QS_BSOFT dqb_bsoftlimit #define QS_BCUR dqb_curblocks #define QS_FHARD dqb_fhardlimit #define QS_FSOFT dqb_fsoftlimit #define QS_FCUR dqb_curfiles #define QS_BTIME dqb_btimelimit #define QS_FTIME dqb_ftimelimit python-fsquota-0.1.0+dfsg1/hints/irix_6.h000066400000000000000000000026071400261757100202170ustar00rootroot00000000000000/* * Configuration for 6.2 - 6.4 * (the only difference to IRIX 5 is XFS and AFS support) * * If you use XFS with IRIX 6.2 you *must* install the latest xfs patch sets! * For AFS support look at the end of this file */ /* See hints/none.h for a complete list of options with explanations */ #include #include #include #include #include #include /* For IRIX 6.5.1 you need the following hack */ #define __SYS_SEMA_H__ /* prevent sys/sema.h from being loaded */ #include #include #include #include #include #include #include #include #define Q_DIV(X) (X) #define Q_MUL(X) (X) #define DEV_QBSIZE DEV_BSIZE #define CADR (caddr_t) #define MNTENT mntent #define GQA_TYPE_USR USRQUOTA /* RQUOTA_USRQUOTA */ #define GQA_TYPE_GRP GRPQUOTA /* RQUOTA_GRPQUOTA */ #define GQR_STATUS gqr_status #define GQR_RQUOTA gqr_rquota #define QS_BHARD dqb_bhardlimit #define QS_BSOFT dqb_bsoftlimit #define QS_BCUR dqb_curblocks #define QS_FHARD dqb_fhardlimit #define QS_FSOFT dqb_fsoftlimit #define QS_FCUR dqb_curfiles #define QS_BTIME dqb_btimelimit #define QS_FTIME dqb_ftimelimit /* optional: for support of SGI XFS file systems - comment out if not needed */ #define SGI_XFS #define QX_DIV(X) (X) #define QX_MUL(X) (X) python-fsquota-0.1.0+dfsg1/hints/linux.h000066400000000000000000000064621400261757100201610ustar00rootroot00000000000000/* * Configuration for Linux - kernel version 2.0.22 and later * * For AFS support look at the end of this file */ /* See hints/none.h for a complete list of options with explanations */ #include #include /* #include */ /* is required only on some distributions (Debian 2.0, RedHat) if your's doesn't have it you can simply remove the following line */ #include #include #include #include #include /*#include */ /* does not support EXT_RQUOTA */ #include "src/rquota.h" #include #include #include #include #include /* definitions from sys/quota.h */ #define USRQUOTA 0 /* element used for user quotas */ #define GRPQUOTA 1 /* element used for group quotas */ extern int quotactl(int, const char *, uid_t, caddr_t); /* * 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)) /* declare an internal version of the quota block struct */ typedef u_int64_t qsize_t; struct dqblk { qsize_t dqb_ihardlimit; /* absolute limit on allocated inodes */ qsize_t dqb_isoftlimit; /* preferred inode limit */ qsize_t dqb_curinodes; /* current # allocated inodes */ qsize_t dqb_bhardlimit; /* absolute limit on disk blks alloc */ qsize_t dqb_bsoftlimit; /* preferred limit on disk blks */ qsize_t dqb_curblocks; /* current block count */ time_t dqb_btime; /* time limit for excessive disk use */ time_t dqb_itime; /* time limit for excessive inode use */ }; /* you can use this switch to hard-wire the quota API if it's not identified correctly */ /* #define LINUX_API_VERSION 1 */ /* API range [1..3] */ int linuxquota_query( const char * dev, int uid, int isgrp, struct dqblk * dqb ); int linuxquota_setqlim( const char * dev, int uid, int isgrp, struct dqblk * dqb ); int linuxquota_sync( const char * dev, int isgrp ); #define Q_DIV(X) (X) #define Q_MUL(X) (X) #define DEV_QBSIZE 1024 #define Q_CTL_V3 #define CADR (caddr_t) #define MY_XDR #define MNTENT mntent #define GQA_TYPE_USR USRQUOTA /* RQUOTA_USRQUOTA */ #define GQA_TYPE_GRP GRPQUOTA /* RQUOTA_GRPQUOTA */ #define GQR_STATUS status #define GQR_RQUOTA getquota_rslt_u.gqr_rquota #define QS_BHARD dqb_bhardlimit #define QS_BSOFT dqb_bsoftlimit #define QS_BCUR dqb_curblocks #define QS_FHARD dqb_ihardlimit #define QS_FSOFT dqb_isoftlimit #define QS_FCUR dqb_curinodes #define QS_BTIME dqb_btime #define QS_FTIME dqb_itime /* uncomment this is you're using NFS with a version of the quota tools < 3.0 */ /* #define LINUX_RQUOTAD_BUG */ /* enable support for extended quota RPC (i.e. quota RPC version 2) */ /* note: could also be enabled by defining MY_XDR (and including "src/rquota.h") */ #if defined (EXT_RQUOTAVERS) #define USE_EXT_RQUOTA #endif /* optional: for support of SGI XFS file systems - comment out if not needed */ #define SGI_XFS #define QX_DIV(X) ((X) / 2) #define QX_MUL(X) ((X) * 2) #include "src/quotaio_xfs.h" python-fsquota-0.1.0+dfsg1/hints/none.h000066400000000000000000000122451400261757100177550ustar00rootroot00000000000000/* * Configuration options */ #include /* Defines all kinds of standard types (e.g. ulong). You may have to add * types.h include files from other /usr/include/ subdirectories */ #include /* This is needed for the quotactl syscall. See man quotactl(2) */ #include /* This is needed for the mntent library routines. See man getmntent(3) another probable name is mnttab or mtab. Basically that's the name of the file where mount(1m) keeps track of the current mounts. See FILES section of man mount for the name of that file */ #include /* See includes list man callrpc(3) and man rquota(3) */ #include #include /* Select one of the following, preferring the first */ #include /**/ /* #include "src/rquota.h" /**/ /* See man socket(2) and man gethostbyname(3) */ #include #include /* Needed for definition of type FILE for set/getmntent(3) routines */ #include /* Needed for global variable "errno" */ #include /* Needed (at least) for memcpy */ #include /* These factors depend on the blocksize of your filesystem. Scale it in a way that quota values are in kB */ #define Q_DIV(X) ((X) / 2) #define Q_MUL(X) ((X) * 2) /* Specify what parameters the quotactl call expects (see man quotactl) */ /* group quotas are supported only with BSD and Linux (see INSTALL) */ /* BSD style: quotactl(dev, QCMD(Q_GETQUOTA, USRQUOTA), uid, &dqblk); */ /* #define Q_CTL_V2 */ /* Linux special: quotactl(QCMD(Q_GETQUOTA, USRQUOTA), dev, uid, &dqblk); */ /* #define Q_CTL_V3 */ /* Solaris uses ioctl() instead of quotactl() */ /* #define USE_IOCTL */ /* if none of the above defined: * old style: quotactl(Q_GETQUOTA, dev, uid, CADR &dqblk); */ /* Normally quota should be reported in file system block sizes. * On Linux though all values are converted to 1k blocks. So we * must not use DEV_BSIZE (usually 512) but 1024 instead. On all * other systems use the file system block size. This value is * used only with RPC, else only Q_DIV and Q_MUL are relevant. */ #define DEV_QBSIZE DEV_BSIZE /* Turn off attempt to convert remote quota block reports to 1k sizes. * This assumes that the remote system always reports in 1k blocks. * Only needed when the remote system also reports a bogus block * size value in the rquota structure (like Linux does). */ /* #define LINUX_RQUOTAD_BUG /**/ /* Some systems need to cast the dqblk structure Do change only if your compiler complains */ #define CADR (caddr_t) /* define if you don't want the RPC query functionality, i.e. you want to operate on the local host only */ /* #define NO_RPC /**/ /* This is for systems that lack librpcsvc and don't have xdr_getquota_args et. al. in libc either. If you do have /usr/include/rpcsvc/rquota.x you can generate these routines with rpcgen, too */ /* #define MY_XDR /**/ /* define this to enable support for extended quota RPC (i.e. quota RPC * version 2), which is needed to allow querying group quotas via RPC. To * check if your OS supports it, search for EXT_RQUOTAVERS in the system * header files. If not, you can define MY_XDR to use module internal * support. */ /* #define USE_EXT_RQUOTA /**/ /* needed only if MOUNTED is not defined in (see above) */ /* define MOUNTED mnttab /**/ /* name of the structure used by getmntent(3) */ #define MNTENT mntent /* on some systems setmntent/endmntend do not exist */ /* #define NO_OPEN_MNTTAB /**/ /* if your system doesn't have /etc/mnttab, and hence no getmntent, use getmntinfo instead then (e.g. in OSF) */ /* #define NO_MNTENT /**/ /* With USE_EXT_RQUOTA these constants distinguish queries for user and * group quota respectively. Only BSD defines these constants properly. For * others use USRQUTA and GRPQUOTA, or simply the real constants (these must * be the same for all OS to allow inter-operability.) */ #define GQA_TYPE_USR 0 /* RQUOTA_USRQUOTA */ #define GQA_TYPE_GRP 1 /* RQUOTA_GRPQUOTA */ /* name of the status entry in struc getquota_rslt and name of the struct * or union that contains the quota values. See include * or "src/rquota.h" if you're using MY_XDR */ #define GQR_STATUS gqr_status #define GQR_RQUOTA gqr_rquota /* members of the dqblk structure, see the include named in man quotactl */ #define QS_BHARD dqb_bhardlimit #define QS_BSOFT dqb_bsoftlimit #define QS_BCUR dqb_curblocks #define QS_FHARD dqb_fhardlimit #define QS_FSOFT dqb_fsoftlimit #define QS_FCUR dqb_curfiles #define QS_BTIME dqb_btimelimit #define QS_FTIME dqb_ftimelimit /* SFIO_VERSION should get defined automatically if sfio routines * are used with Perl instead of stdio; but you might wanna define * it here if you are using PerlIO and experience problems with * Quota::getqcarg() or the Quota::getmntent() family. * If PerlIO is used, PERLIO_IS_STDIO is not defined */ /* #ifndef PERLIO_IS_STDIO /**/ /* #define SFIO_VERSION x.x /**/ /* #endif /**/ /* If you have AFS (e.g. arla-0.13) then modify the lines below * and insert your paths to the Kerberos libraries and header files. * Depending on your compiler you may have to change the compiler * and linker arguments. See man cc(1) */ python-fsquota-0.1.0+dfsg1/hints/solaris_2.h000066400000000000000000000025471400261757100207170ustar00rootroot00000000000000/* * Configuration for Solaris 2.4 & 2.5.x & 2.6 - NOT 2.7 * (this is for use with SYSV flavour, not /usr/bsd) * * For AFS support look at the end of this file */ /* See hints/none.h for a complete list of options with explanations */ #include #include #include #include #include #include #include #include #include #include #include #define USE_IOCTL #define Q_DIV(X) ((X) / 2) #define Q_MUL(X) ((X) * 2) #define DEV_QBSIZE DEV_BSIZE #define CADR (caddr_t) /* Haven't found definition of quotactl-struct in any include, just mentioned in man quotactl(7) */ struct quotactl { int op; uid_t uid; caddr_t addr; }; #define NO_OPEN_MNTTAB #define MOUNTED MNTTAB #define MNTENT mnttab /* * Solaris seems to lack xdr routines for rquota. Use my own. */ #define MY_XDR #define GQA_TYPE_USR USRQUOTA /* RQUOTA_USRQUOTA */ #define GQA_TYPE_GRP GRPQUOTA /* RQUOTA_GRPQUOTA */ #define GQR_STATUS status #define GQR_RQUOTA getquota_rslt_u.gqr_rquota #define QS_BHARD dqb_bhardlimit #define QS_BSOFT dqb_bsoftlimit #define QS_BCUR dqb_curblocks #define QS_FHARD dqb_fhardlimit #define QS_FSOFT dqb_fsoftlimit #define QS_FCUR dqb_curfiles #define QS_BTIME dqb_btimelimit #define QS_FTIME dqb_ftimelimit python-fsquota-0.1.0+dfsg1/hints/sunos_4_1.h000066400000000000000000000020011400261757100206150ustar00rootroot00000000000000/* * Configuration for SunOS 4.1.3 * * For AFS support look at the end of this file * For arla AFS an ANSI C compiler is required (SC1.0 acc or gcc) */ /* See hints/none.h for a complete list of options with explanations */ #include #include #include #include #include #include #include #include #include #include #include #define Q_DIV(X) ((X) / 2) #define Q_MUL(X) ((X) * 2) #define DEV_QBSIZE DEV_BSIZE #define CADR #define MNTENT mntent #define GQA_TYPE_USR USRQUOTA /* RQUOTA_USRQUOTA */ #define GQA_TYPE_GRP GRPQUOTA /* RQUOTA_GRPQUOTA */ #define GQR_STATUS gqr_status #define GQR_RQUOTA gqr_rquota #define QS_BHARD dqb_bhardlimit #define QS_BSOFT dqb_bsoftlimit #define QS_BCUR dqb_curblocks #define QS_FHARD dqb_fhardlimit #define QS_FSOFT dqb_fsoftlimit #define QS_FCUR dqb_curfiles #define QS_BTIME dqb_btimelimit #define QS_FTIME dqb_ftimelimit python-fsquota-0.1.0+dfsg1/setup.cfg000066400000000000000000000000461400261757100173350ustar00rootroot00000000000000[egg_info] tag_build = tag_date = 0 python-fsquota-0.1.0+dfsg1/setup.py000066400000000000000000000224521400261757100172330ustar00rootroot00000000000000# ---------------------------------------------------------------------------- # Copyright (C) 1995-2020 T. Zoerner # ---------------------------------------------------------------------------- # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by the # Free Software Foundation. (Either version 2 of the GPL, or any later # version, see http://www.opensource.org/licenses/). # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # ---------------------------------------------------------------------------- from setuptools import setup, Extension from distutils.command.install import install import os import os.path import sys import subprocess import shutil import re if sys.version_info[0] != 3: print("This script requires Python 3") exit() this_directory = os.path.abspath(os.path.dirname(__file__)) myconfig_h = os.path.join(this_directory, 'myconfig.h') # ---------------------------------------------------------------------------- # Note most configuration (including compile-switches) is done via includes # in the hints/ directory. The following only manages source lists and libs # ---------------------------------------------------------------------------- extrasrc = [] extrainc = [] extradef = [] extralibs = [] extralibdirs = [] # # Select a configuration header file based on OS & revision # if sys.version_info[1] >= 5: osr = subprocess.run(['uname', '-rs'], stdout=subprocess.PIPE).stdout.decode('utf-8') else: # subprocess.run() does not exist before 3.5 ps = subprocess.Popen(['uname', '-rs'], stdout=subprocess.PIPE) osr = ps.communicate()[0].decode('utf-8') if re.match(r"^SunOS 4\.1", osr) : config='sunos_4_1.h' elif re.match(r"^SunOS 5", osr) : config='solaris_2.h' elif re.match(r"^HP-UX (A\.09|B\.10|[BC]\.11)", osr): config='hpux.h' elif re.match(r"^IRIX 5", osr) : config='irix_5.h' elif re.match(r"^IRIX\d* 6", osr) : config='irix_6.h' elif re.match(r"^OSF1", osr) : config='dec_osf.h' elif re.match(r"^Linux", osr) : config='linux.h' elif re.match(r"^AIX", osr) : config='aix_4_1.h' elif (re.match(r"^dragonfly", osr, flags=re.IGNORECASE) or re.match(r"^Darwin", osr) or re.match(r"^FreeBSD", osr) or re.match(r"^NetBSD", osr) or re.match(r"^OpenBSD", osr)) : config='bsd.h' else: print("FATAL: No appropriate hints found for this OS/revision: \"" + osr + "\"\n" + "Please see instructions in file INSTALL", file=sys.stderr) exit(1) config = "hints/" + config print("Using %s for myconfig.h" % config, file=sys.stderr) if ( os.path.isfile(myconfig_h) and (not os.path.islink(myconfig_h) or not (os.readlink(myconfig_h) == config))): print("\nFATAL: myconfig.h already exists.\n\n" + "You need to do a \"make clean\" before configuring for a new platform.\n" + "If that doesn't help, remove myconfig.h manually.", file=sys.stderr) exit(1) # check whether the Andrew File System (AFS) is installed and running if os.path.isdir("/afs"): df_afs = subprocess.run(['df', '/afs'], stdout=subprocess.PIPE).stdout.decode('utf-8') if re.match(r"\nAFS|\(AFS/", df_afs): AFSHOME = "/usr/afsws" if os.path.isdir("/usr/afsws") else "/usr" extradef += [('AFSQUOTA', 1)] extrainc += [AFSHOME+"/include", AFSHOME+"/include/afs"] extralibdirs += [AFSHOME+"/lib", AFSHOME+"/lib/afs"] extralibs += ["sys", "rx", "rxkad", "lwp"] extrasrc += ["src/afsquota.c"] # check to see if we have a kernel module for the Veritas file system if re.match(r"^SunOS", osr): if os.path.isfile('/usr/include/sys/fs/vx_quota.h'): extradef += [('SOLARIS_VXFS', 1)]; extrasrc += ["src/vxquotactl.c"] print("Configured with the VERITAS File System on Solaris", file=sys.stderr) else: # no warning because newer versions of Solaris have internal VxFS support #print("Configured without VxFS support", file=sys.stderr) pass # check whether we are using the NetBSD quota library match1 = re.match(r"^NetBSD 5\.99\.(\d+)", osr) match2 = re.match(r"^NetBSD (\d)", osr) if ( (match1 and (int(match1.group(1)) >= 59)) or (match2 and (int(match2.group(1)) >= 6))): extralibs += ["quota"] # check whether RPCSVC is included within libc # - SUN RPC/XDR support was split off from glibc, see: # https://lists.fedoraproject.org/archives/list/devel@lists.fedoraproject.org/thread/F2NRCEXDDUF6WWNPSOMXRJS6BPMTEEVJ/ # - in RHEL apparently the rpc/rpc.h header was moved too # - Debian has libtirpc, but headers and implementation are still in glibc too # so there's a risk symbols are resolved from libc while compiling against tirpc headers; # therefore we do not use tirpc when rpc headers are present outside tirpc if re.match(r"^Linux", osr): extrasrc += ["src/linuxapi.c"] if os.path.isdir('/usr/include/tirpc') and not os.path.isfile('/usr/include/rpc/rpc.h'): print("Configured to use tirpc library instead of rpcsvc", file=sys.stderr) extrainc += ["/usr/include/tirpc"] extralibs += ["tirpc"] else: if not os.path.isfile('/usr/include/rpc/rpc.h'): print("WARNING: Header file /usr/include/rpc/rpc.h not present on this system.\n" + " Likely compilation will fail. Recommend to either install package\n" + " \"libtirpc-dev\", or disable RPC (network file system) support by\n" + " adding the following switch to myconfig.h:\n" + " #define NO_RPC\n", file=sys.stderr) extralibs += ["rpcsvc"] # ---------------------------------------------------------------------------- class MyClean(install): cwd = os.path.abspath(os.path.dirname(__file__)) def rmfile(self, apath): p = os.path.join(MyClean.cwd, apath) if os.path.isfile(p): os.remove(p) def rmtree(self, apath): p = os.path.join(MyClean.cwd, apath) if os.path.isdir(p): shutil.rmtree(p) def run(self): # files created by configuration stage self.rmfile('myconfig.h') # files created by build stage self.rmtree('build') # files created by test stage self.rmtree('FsQuota.egg-info') self.rmtree('__pycache__') for name in os.listdir(MyClean.cwd): if re.match(r"^.*\.so$", name): os.remove(os.path.join(MyClean.cwd, name)) self.rmfile('core') # files created by sdist stage self.rmtree('dist') # ---------------------------------------------------------------------------- # Finally execute the setup command with open(os.path.join(this_directory, 'doc/README.rst'), encoding='utf-8') as fh: long_description = fh.read() with open(os.path.join(this_directory, 'doc/FsQuota.rst'), encoding='utf-8') as fh: api_doc = fh.read() match = re.match(r"[\x00-\xff]*?(?=^SYNOPSIS$)", api_doc, re.MULTILINE) if match: long_description += "\n\n" + api_doc[match.end():] else: print("ERROR: Failed to find SYNOPSIS in FsQuota.rst", file=sys.stderr) if not os.path.isfile(myconfig_h): os.symlink(config, myconfig_h) # Enable work-around for bugs in PyStructSequence_NewType (i.e. for # creating named tuples in C module; causing crash in GC: # "Fatal Python error: type_traverse() called for non-heap type") # Known issue: https://bugs.python.org/issue28709 - fixed in 3.8 if (sys.version_info[0] == 3) and (sys.version_info[1] < 8): extradef += [('NAMED_TUPLE_GC_BUG', 1)] ext = Extension('FsQuota', sources = ['src/FsQuota.c'] + extrasrc, include_dirs = ['.'] + extrainc, define_macros = extradef, libraries = extralibs, library_dirs = extralibdirs, #undef_macros = ["NDEBUG"] # for debug build only ) setup(name='FsQuota', version='0.1.0', description='Interface to file system quotas on UNIX platforms', long_description=long_description, long_description_content_type="text/x-rst", author='T. Zoerner', author_email='tomzo@users.sourceforge.net', url='https://github.com/tomzox/Python-Quota', license = "GNU GPLv2+", classifiers=[ 'Development Status :: 4 - Beta', "Programming Language :: C", "Programming Language :: Python :: 3", 'Topic :: System :: Filesystems', 'Topic :: System :: Systems Administration', 'Intended Audience :: Developers', 'Intended Audience :: System Administrators', "Operating System :: POSIX :: Linux", "Operating System :: POSIX :: BSD", "Operating System :: POSIX :: AIX", "Operating System :: POSIX :: HP-UX", "Operating System :: POSIX :: IRIX", "Operating System :: POSIX :: SunOS/Solaris", "License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)" ], keywords="file-system, quota, quotactl, mtab, getmntent", platforms=['posix'], ext_modules=[ext], cmdclass={'clean': MyClean}, python_requires='>=3.2', ) python-fsquota-0.1.0+dfsg1/src/000077500000000000000000000000001400261757100163035ustar00rootroot00000000000000python-fsquota-0.1.0+dfsg1/src/FsQuota.c000066400000000000000000002053361400261757100200420ustar00rootroot00000000000000// ------------------------------------------------------------------------ // Copyright (C) 1995-2020 T. Zoerner // ------------------------------------------------------------------------ // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by the // Free Software Foundation. (Either version 2 of the GPL, or any later // version, see http://www.opensource.org/licenses/). // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // ------------------------------------------------------------------------ #define PY_SSIZE_T_CLEAN #include "Python.h" #include "myconfig.h" #ifdef AFSQUOTA #include "include/afsquota.h" #endif #ifdef SOLARIS_VXFS #include "include/vxquotactl.h" #endif #if defined (NAMED_TUPLE_GC_BUG) static PyTypeObject FsQuota_QuotaQueryTypeBuf; static PyTypeObject FsQuota_MntTabTypeBuf; static PyTypeObject * const FsQuota_QuotaQueryType = &FsQuota_QuotaQueryTypeBuf; static PyTypeObject * const FsQuota_MntTabType = &FsQuota_MntTabTypeBuf; #else static PyTypeObject * FsQuota_QuotaQueryType = NULL; static PyTypeObject * FsQuota_MntTabType = NULL; #endif static PyObject * FsQuotaError; // // This data structure encapsulates the entire internal state of an ongoing // mount table iteration. Member variables are specific to the implementation // for different platforms. // typedef struct { #ifndef AIX #ifndef NO_MNTENT FILE *mtab; #else /* NO_MNTENT */ #ifdef USE_STATVFS_MNTINFO struct statvfs *mntp; struct statvfs *mtab; #else struct statfs *mntp; struct statfs *mtab; #endif int mtab_size; char str_mnt_flag[100]; #endif /* NO_MNTENT */ #else /* AIX */ struct vmount *mtab; int aix_mtab_idx; int aix_mtab_count; #endif } T_MY_MNTENT_STATE; #ifndef NO_RPC // ---------------------------------------------------------------------------- // // This data structure contains configurable options for RPC // typedef struct { unsigned use_tcp; unsigned port; unsigned timeout; int auth_uid; int auth_gid; char auth_hostname[MAX_MACHINE_NAME + 1]; } T_QUOTA_RPC_OPT; #define RPC_DEFAULT_TIMEOUT 4000 #define RPC_AUTH_UGID_NON_INIT -1 // // This data structure defines the implementation-independent container used // internally for returning results from quota queries via RPC sub-functions. // typedef struct { uint64_t bhard; uint64_t bsoft; uint64_t bcur; time_t btime; uint64_t fhard; uint64_t fsoft; uint64_t fcur; time_t ftime; } T_QUOTA_RPC_RESULT; // // Execute RPC to remote host // static int callaurpc(char *host, int prognum, int versnum, int procnum, xdrproc_t inproc, char *in, xdrproc_t outproc, char *out, const T_QUOTA_RPC_OPT * opt, char ** p_errstr) { struct sockaddr_in remaddr; struct hostent *hp; enum clnt_stat clnt_stat; struct timeval rep_time, timeout; CLIENT *client; int socket = RPC_ANYSOCK; // // Get IP address; by default the port is determined via remote // portmap daemon; different ports and protocols can be configured // hp = gethostbyname(host); if (hp == NULL) { *p_errstr = clnt_sperrno(RPC_UNKNOWNHOST); return -1; } rep_time.tv_sec = opt->timeout / 1000; rep_time.tv_usec = (opt->timeout % 1000) * 1000; memcpy((char *)&remaddr.sin_addr, (char *)hp->h_addr, hp->h_length); remaddr.sin_family = AF_INET; remaddr.sin_port = htons(opt->port); // // Create client RPC handle // client = NULL; if (!opt->use_tcp) { client = (CLIENT *)clntudp_create(&remaddr, prognum, versnum, rep_time, &socket); } else { client = (CLIENT *)clnttcp_create(&remaddr, prognum, versnum, &socket, 0, 0); } if (client == NULL) { if (rpc_createerr.cf_stat != RPC_SUCCESS) *p_errstr = clnt_sperrno(rpc_createerr.cf_stat); else // should never happen (may be due to inconsistent symbol resolution) *p_errstr = "RPC creation failed for unknown reasons"; return -1; } // // Create an authentication handle // if ((opt->auth_uid >= 0) && (opt->auth_gid >= 0)) { client->cl_auth = authunix_create((char*)opt->auth_hostname, // cast to remove const opt->auth_uid, opt->auth_gid, 0, 0); } else { client->cl_auth = authunix_create_default(); } // // Call remote server // timeout.tv_sec = opt->timeout / 1000; timeout.tv_usec = (opt->timeout % 1000) * 1000; clnt_stat = clnt_call(client, procnum, inproc, in, outproc, out, timeout); if (client->cl_auth) { auth_destroy(client->cl_auth); client->cl_auth = NULL; } clnt_destroy(client); if (clnt_stat != RPC_SUCCESS) { *p_errstr = clnt_sperrno(clnt_stat); return -1; } else { *p_errstr = NULL; return 0; } } // // Fetch quota limits for NFS mount via RPC // static int getnfsquota( char *hostp, char *fsnamep, int uid, int is_grpquota, const T_QUOTA_RPC_OPT * opt, char ** rpc_err_str, T_QUOTA_RPC_RESULT *rslt ) { struct getquota_args gq_args; struct getquota_rslt gq_rslt; #ifdef USE_EXT_RQUOTA ext_getquota_args ext_gq_args; // // First try USE_EXT_RQUOTAPROG (Extended quota RPC) // ext_gq_args.gqa_pathp = fsnamep; ext_gq_args.gqa_type = (is_grpquota ? GQA_TYPE_GRP : GQA_TYPE_USR); ext_gq_args.gqa_id = uid; if (callaurpc(hostp, RQUOTAPROG, EXT_RQUOTAVERS, RQUOTAPROC_GETQUOTA, (xdrproc_t)xdr_ext_getquota_args, (char*) &ext_gq_args, (xdrproc_t)xdr_getquota_rslt, (char*) &gq_rslt, opt, rpc_err_str) != 0) #endif { if (!is_grpquota) { // // Fall back to RQUOTAPROG if the server (or client via compile switch) // doesn't support extended quota RPC (i.e. only supports user quota) // gq_args.gqa_pathp = fsnamep; gq_args.gqa_uid = uid; if (callaurpc(hostp, RQUOTAPROG, RQUOTAVERS, RQUOTAPROC_GETQUOTA, (xdrproc_t)xdr_getquota_args, (char*) &gq_args, (xdrproc_t)xdr_getquota_rslt, (char*) &gq_rslt, opt, rpc_err_str) != 0) { return -1; } } else { #ifndef USE_EXT_RQUOTA *rpc_err_str = "RPC: group quota not supported by RPC"; #endif return -1; } } switch (gq_rslt.GQR_STATUS) { case Q_OK: { struct timeval tv; int qb_fac; gettimeofday(&tv, NULL); #ifdef LINUX_RQUOTAD_BUG // Since Linux reports a bogus block size value (4k), we must not // use it. Thankfully Linux at least always uses 1k block sizes // for quota reports, so we just leave away all conversions. // If you have a mixed environment, you have a problem though. // Complain to the Linux authors or apply my patch (see INSTALL) // rslt->bhard = gq_rslt.GQR_RQUOTA.rq_bhardlimit; rslt->bsoft = gq_rslt.GQR_RQUOTA.rq_bsoftlimit; rslt->bcur = gq_rslt.GQR_RQUOTA.rq_curblocks; #else /* not buggy */ if (gq_rslt.GQR_RQUOTA.rq_bsize >= DEV_QBSIZE) { // assign first, multiply later: // so that mult works with the possibly larger type in rslt rslt->bhard = gq_rslt.GQR_RQUOTA.rq_bhardlimit; rslt->bsoft = gq_rslt.GQR_RQUOTA.rq_bsoftlimit; rslt->bcur = gq_rslt.GQR_RQUOTA.rq_curblocks; // we rely on the fact that block sizes are always powers of 2 // so the conversion factor will never be a fraction qb_fac = gq_rslt.GQR_RQUOTA.rq_bsize / DEV_QBSIZE; rslt->bhard *= qb_fac; rslt->bsoft *= qb_fac; rslt->bcur *= qb_fac; } else { if (gq_rslt.GQR_RQUOTA.rq_bsize != 0) qb_fac = DEV_QBSIZE / gq_rslt.GQR_RQUOTA.rq_bsize; else qb_fac = 1; rslt->bhard = gq_rslt.GQR_RQUOTA.rq_bhardlimit / qb_fac; rslt->bsoft = gq_rslt.GQR_RQUOTA.rq_bsoftlimit / qb_fac; rslt->bcur = gq_rslt.GQR_RQUOTA.rq_curblocks / qb_fac; } #endif /* LINUX_RQUOTAD_BUG */ rslt->fhard = gq_rslt.GQR_RQUOTA.rq_fhardlimit; rslt->fsoft = gq_rslt.GQR_RQUOTA.rq_fsoftlimit; rslt->fcur = gq_rslt.GQR_RQUOTA.rq_curfiles; // if time is given relative to actual time, add actual time // Note: all systems except Linux return relative times if (gq_rslt.GQR_RQUOTA.rq_btimeleft == 0) rslt->btime = 0; else if (gq_rslt.GQR_RQUOTA.rq_btimeleft + 10*365*24*60*60 < (u_int)tv.tv_sec) rslt->btime = tv.tv_sec + gq_rslt.GQR_RQUOTA.rq_btimeleft; else rslt->btime = gq_rslt.GQR_RQUOTA.rq_btimeleft; if (gq_rslt.GQR_RQUOTA.rq_ftimeleft == 0) rslt->ftime = 0; else if (gq_rslt.GQR_RQUOTA.rq_ftimeleft + 10*365*24*60*60 < (u_int)tv.tv_sec) rslt->ftime = tv.tv_sec + gq_rslt.GQR_RQUOTA.rq_ftimeleft; else rslt->ftime = gq_rslt.GQR_RQUOTA.rq_ftimeleft; return 0; } case Q_NOQUOTA: errno = ESRCH; break; case Q_EPERM: errno = EPERM; break; default: errno = EINVAL; break; } return -1; } #ifdef MY_XDR // // Transport encoding for quota RPC, in case not provided by system libraries // static const struct xdr_discrim gq_des[2] = { { (int)Q_OK, (xdrproc_t)xdr_rquota }, { 0, NULL } }; bool_t xdr_getquota_args( XDR *xdrs, struct getquota_args *gqp ) { return (xdr_string(xdrs, &gqp->gqa_pathp, 1024) && xdr_int(xdrs, &gqp->gqa_uid)); } bool_t xdr_getquota_rslt( XDR *xdrs, struct getquota_rslt *gqp ) { return (xdr_union(xdrs, (int *) &gqp->GQR_STATUS, (char *) &gqp->GQR_RQUOTA, (struct xdr_discrim *) gq_des, // cast to remove const (xdrproc_t) xdr_void)); } bool_t xdr_rquota( XDR *xdrs, struct rquota *rqp ) { return (xdr_int(xdrs, &rqp->rq_bsize) && xdr_bool(xdrs, &rqp->rq_active) && xdr_u_long(xdrs, (unsigned long *)&rqp->rq_bhardlimit) && xdr_u_long(xdrs, (unsigned long *)&rqp->rq_bsoftlimit) && xdr_u_long(xdrs, (unsigned long *)&rqp->rq_curblocks) && xdr_u_long(xdrs, (unsigned long *)&rqp->rq_fhardlimit) && xdr_u_long(xdrs, (unsigned long *)&rqp->rq_fsoftlimit) && xdr_u_long(xdrs, (unsigned long *)&rqp->rq_curfiles) && xdr_u_long(xdrs, (unsigned long *)&rqp->rq_btimeleft) && xdr_u_long(xdrs, (unsigned long *)&rqp->rq_ftimeleft) ); } #endif /* MY_XDR */ #ifdef USE_EXT_RQUOTA bool_t xdr_ext_getquota_args( XDR *xdrs, ext_getquota_args *objp ) { return (xdr_string(xdrs, &objp->gqa_pathp, RQ_PATHLEN) && xdr_int(xdrs, &objp->gqa_type) && xdr_int(xdrs, &objp->gqa_id)); } #endif /* USE_EXT_RQUOTA */ #endif /* !NO_RPC */ // ---------------------------------------------------------------------------- // Class "Quota" // ---------------------------------------------------------------------------- // // This enumeration is used to identify file systems that require special // handling in the query() and setqlim() methods. File systems for which the // default handling of the respective platform can be used are marked // "regular". // typedef enum { QUOTA_DEV_INVALID, QUOTA_DEV_REGULAR, QUOTA_DEV_NFS, QUOTA_DEV_XFS, QUOTA_DEV_VXFS, QUOTA_DEV_AFS, QUOTA_DEV_JFS2, } T_QUOTA_DEV_FS_TYPE; // // Container for instance state variables // typedef struct { PyObject_HEAD char * m_path; // path parameter passed to the constructor char * m_qcarg; // device parameter derived from path char * m_rpc_host; // rpc_host parameter passed to the constructor T_QUOTA_DEV_FS_TYPE m_dev_fs_type; // file system types that need special handling #ifndef NO_RPC T_QUOTA_RPC_OPT m_rpc_opt; // container for parameters set via rpc_opt() #endif } Quota_ObjectType; // forward declaration static int Quota_setqcarg(Quota_ObjectType *self); // // Helper function for raising an exception upon quotactl() error // static void * FsQuota_QuotaCtlException(Quota_ObjectType * self, int errnum, const char * str) { if (str == NULL) { if ((errnum == ENOENT) && (self->m_dev_fs_type == QUOTA_DEV_XFS)) str = "No quota for this user"; else if ((errnum == EINVAL) || (errnum == ENOTTY) || (errnum == ENOENT) || (errnum == ENOSYS)) str = "No quotas on this system"; else if (errnum == ENODEV) str = "Not a standard file system"; else if (errnum == EPERM) str = "Not privileged"; else if (errnum == EACCES) str = "Access denied"; else if (errnum == ESRCH) #ifdef Q_CTL_V3 /* Linux */ str = "Quotas not enabled, no quota for this user"; #else str = "No quota for this user"; #endif else if (errnum == EUSERS) str = "Quota table overflow"; else str = strerror(errnum); } PyObject * tuple = PyTuple_New(2); PyTuple_SetItem(tuple, 0, PyLong_FromLong(errnum)); PyTuple_SetItem(tuple, 1, PyUnicode_DecodeFSDefault(str)); PyErr_SetObject(FsQuotaError, tuple); // for convenience: to be assiged to caller's RETVAL return NULL; } // // Helper function for raising an exception upon errors returned by C library // functions. Meaning of parameters is equivalent to that of exception base // class "OSError". // static void * FsQuota_OsException(int errnum, const char * desc, const char * path) { PyObject * strerr = PyUnicode_DecodeFSDefault(strerror(errnum)); PyObject * tuple = PyTuple_New((path == NULL) ? 2 : 3); PyTuple_SetItem(tuple, 0, PyLong_FromLong(errnum)); PyTuple_SetItem(tuple, 1, PyUnicode_FromFormat("%s: %U", desc, strerr)); if (path != NULL) PyTuple_SetItem(tuple, 2, PyUnicode_DecodeFSDefault(path)); PyErr_SetObject(FsQuotaError, tuple); Py_DECREF(strerr); // for convenience: to be assiged to caller's RETVAL return NULL; } // // Helper function for allocating and filling a query result tuple with given // values. // static PyObject * FsQuota_BuildQuotaResult(uint64_t bc, uint64_t bs, uint64_t bh, uint32_t bt, uint64_t ic, uint64_t is, uint64_t ih, uint32_t it) { PyObject * RETVAL; RETVAL = PyStructSequence_New(FsQuota_QuotaQueryType); PyStructSequence_SetItem(RETVAL, 0, PyLong_FromLongLong(bc)); PyStructSequence_SetItem(RETVAL, 1, PyLong_FromLongLong(bs)); PyStructSequence_SetItem(RETVAL, 2, PyLong_FromLongLong(bh)); PyStructSequence_SetItem(RETVAL, 3, PyLong_FromLong (bt)); PyStructSequence_SetItem(RETVAL, 4, PyLong_FromLongLong(ic)); PyStructSequence_SetItem(RETVAL, 5, PyLong_FromLongLong(is)); PyStructSequence_SetItem(RETVAL, 6, PyLong_FromLongLong(ih)); PyStructSequence_SetItem(RETVAL, 7, PyLong_FromLong (it)); return RETVAL; } // // Implementation of the Quota.query() method // PyDoc_STRVAR(Quota_query__doc__, "query(uid, *, grpquota=False, projquota=False) -> FsQuota.QueryResult\n\n" "Query quota usage and limits for the given user.\n\n" "When either grpquota or projquota is set to True, the query returns " "group or project quotas instead of user quotas. Only one of these " "options should be True. Project quotas are supported only by XFS " "file systems."); static PyObject * Quota_query(Quota_ObjectType *self, PyObject *args, PyObject *kwds) { int uid = getuid(); int is_grpquota = FALSE; int is_prjquota = FALSE; static char * kwlist[] = {"uid", "grpquota", "prjquota", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "i|$pp", kwlist, &uid, &is_grpquota, &is_prjquota)) { return NULL; } PyObject * RETVAL = NULL; int err; if (self->m_dev_fs_type == QUOTA_DEV_INVALID) { RETVAL = FsQuota_QuotaCtlException(self, EINVAL, "FsQuota.Quota instance is uninitialized"); } else if (is_prjquota && (self->m_dev_fs_type != QUOTA_DEV_XFS)) { RETVAL = FsQuota_QuotaCtlException(self, ENOTSUP, "Project quotas are only supported by XFS"); } else #ifdef SGI_XFS if (self->m_dev_fs_type == QUOTA_DEV_XFS) { fs_disk_quota_t xfs_dqblk; #ifndef linux err = quotactl(Q_XGETQUOTA, self->m_qcarg, uid, CADR &xfs_dqblk); #else err = quotactl(QCMD(Q_XGETQUOTA, (is_prjquota ? XQM_PRJQUOTA : is_grpquota ? XQM_GRPQUOTA : XQM_USRQUOTA)), self->m_qcarg, uid, CADR &xfs_dqblk); #endif if (!err) { RETVAL = FsQuota_BuildQuotaResult(QX_DIV(xfs_dqblk.d_bcount), QX_DIV(xfs_dqblk.d_blk_softlimit), QX_DIV(xfs_dqblk.d_blk_hardlimit), xfs_dqblk.d_btimer, xfs_dqblk.d_icount, xfs_dqblk.d_ino_softlimit, xfs_dqblk.d_ino_hardlimit, xfs_dqblk.d_itimer); } else { FsQuota_QuotaCtlException(self, errno, NULL); } } else #endif /* SGI_XFS */ #ifdef SOLARIS_VXFS if (self->m_dev_fs_type == QUOTA_DEV_VXFS) { struct vx_dqblk vxfs_dqb; err = vx_quotactl(VX_GETQUOTA, self->m_qcarg, uid, CADR &vxfs_dqb); if (!err) { RETVAL = FsQuota_BuildQuotaResult(Q_DIV(vxfs_dqb.dqb_curblocks), Q_DIV(vxfs_dqb.dqb_bsoftlimit), Q_DIV(vxfs_dqb.dqb_bhardlimit), vxfs_dqb.dqb_btimelimit, vxfs_dqb.dqb_curfiles, vxfs_dqb.dqb_fsoftlimit, vxfs_dqb.dqb_fhardlimit, vxfs_dqb.dqb_ftimelimit); } else { FsQuota_QuotaCtlException(self, errno, NULL); } } else #endif /* SOLARIS_VXFS */ #ifdef AFSQUOTA if (self->m_dev_fs_type == QUOTA_DEV_AFS) { if (!afs_check()) // check is *required* as setup! { FsQuota_OsException(EINVAL, "AFS setup failed", NULL); } else { int maxQuota, blocksUsed; err = afs_getquota(self->m_qcarg, &maxQuota, &blocksUsed); if (!err) { RETVAL = FsQuota_BuildQuotaResult(blocksUsed, maxQuota, maxQuota, 0, 0, 0, 0, 0); } else { FsQuota_QuotaCtlException(self, errno, NULL); } } } else #endif /* AFSQUOTA */ #if defined(HAVE_JFS2) if (self->m_dev_fs_type == QUOTA_DEV_JFS2) { // AIX quotactl doesn't fail if path does not exist!? struct stat st; if (stat(self->m_qcarg, &st) == 0) { quota64_t user_quota; err = quotactl(self->m_qcarg, QCMD(Q_J2GETQUOTA, (is_grpquota ? GRPQUOTA : USRQUOTA)), uid, CADR &user_quota); if (!err) { RETVAL = FsQuota_BuildQuotaResult(user_quota.bused, user_quota.bsoft, user_quota.bhard, user_quota.btime, user_quota.ihard, user_quota.isoft, user_quota.iused, user_quota.itime); } } else err = 1; } else #endif /* HAVE_JFS2 */ #ifndef NO_RPC if (self->m_dev_fs_type == QUOTA_DEV_NFS) { T_QUOTA_RPC_RESULT rslt; char * rpc_err_str = NULL; err = getnfsquota(self->m_rpc_host, self->m_qcarg, uid, is_grpquota, &self->m_rpc_opt, &rpc_err_str, &rslt); if (!err) { RETVAL = FsQuota_BuildQuotaResult(Q_DIV(rslt.bcur), Q_DIV(rslt.bsoft), Q_DIV(rslt.bhard), rslt.btime, rslt.fcur, rslt.fsoft, rslt.fhard, rslt.ftime); } else if (rpc_err_str != NULL) { FsQuota_QuotaCtlException(self, EIO, rpc_err_str); } else { FsQuota_QuotaCtlException(self, errno, NULL); } } else #endif /* NO_RPC */ { #ifdef NETBSD_LIBQUOTA struct quotahandle *qh = quota_open(self->m_qcarg); if (qh != NULL) { struct quotakey qk_blocks, qk_files; struct quotaval qv_blocks, qv_files; qk_blocks.qk_idtype = /*fall-through*/ qk_files.qk_idtype = is_grpquota ? QUOTA_IDTYPE_GROUP : QUOTA_IDTYPE_USER; qk_blocks.qk_id = qk_files.qk_id = uid; qk_blocks.qk_objtype = QUOTA_OBJTYPE_BLOCKS; qk_files.qk_objtype = QUOTA_OBJTYPE_FILES; if ((quota_get(qh, &qk_blocks, &qv_blocks) >= 0) && (quota_get(qh, &qk_files, &qv_files) >= 0) ) { // adapt to common "unlimited" semantics if ((qv_blocks.qv_softlimit == QUOTA_NOLIMIT) && (qv_blocks.qv_hardlimit == QUOTA_NOLIMIT)) { qv_blocks.qv_hardlimit = qv_blocks.qv_softlimit = 0; } if ((qv_files.qv_softlimit == QUOTA_NOLIMIT) && (qv_files.qv_hardlimit == QUOTA_NOLIMIT)) { qv_files.qv_hardlimit = qv_files.qv_softlimit = 0; } RETVAL = FsQuota_BuildQuotaResult(Q_DIV(qv_blocks.qv_usage), Q_DIV(qv_blocks.qv_softlimit), Q_DIV(qv_blocks.qv_hardlimit), qv_blocks.qv_expiretime, qv_files.qv_usage, qv_files.qv_softlimit, qv_files.qv_hardlimit, qv_files.qv_expiretime); } else { FsQuota_QuotaCtlException(self, errno, NULL); } quota_close(qh); } else { FsQuota_QuotaCtlException(self, errno, NULL); } #else /* not NETBSD_LIBQUOTA */ struct dqblk dqblk; #ifdef USE_IOCTL struct quotactl qp; int fd = -1; qp.op = Q_GETQUOTA; qp.uid = uid; qp.addr = (char *)&dqblk; if ((fd = open(self->m_qcarg, O_RDONLY)) != -1) { err = (ioctl(fd, Q_QUOTACTL, &qp) == -1); close(fd); } else { err = 1; } #else /* not USE_IOCTL */ #ifdef Q_CTL_V3 /* Linux */ err = linuxquota_query(self->m_qcarg, uid, is_grpquota, &dqblk); #else /* not Q_CTL_V3 */ #ifdef Q_CTL_V2 #ifdef AIX // AIX quotactl doesn't fail if path does not exist!? struct stat st; if (stat(self->m_qcarg, &st) != 0) { err = 1; } else #endif /* AIX */ err = quotactl(self->m_qcarg, QCMD(Q_GETQUOTA, (is_grpquota ? GRPQUOTA : USRQUOTA)), uid, CADR &dqblk); #else /* not Q_CTL_V2 */ err = quotactl(Q_GETQUOTA, self->m_qcarg, uid, CADR &dqblk); #endif /* not Q_CTL_V2 */ #endif /* Q_CTL_V3 */ #endif /* not USE_IOCTL */ if (!err && (RETVAL == NULL)) { RETVAL = FsQuota_BuildQuotaResult(Q_DIV(dqblk.QS_BCUR), Q_DIV(dqblk.QS_BSOFT), Q_DIV(dqblk.QS_BHARD), dqblk.QS_BTIME, dqblk.QS_FCUR, dqblk.QS_FSOFT, dqblk.QS_FHARD, dqblk.QS_FTIME); } else if (err) { FsQuota_QuotaCtlException(self, errno, NULL); } #endif /* not NETBSD_LIBQUOTA */ } return RETVAL; } // // Implementation of the Quota.seqlim() method // PyDoc_STRVAR(Quota_setqlim__doc__, "setqlim(uid, bsoft, bhard, isoft, ihard, *, grpquota=False, projquota=False)\n\n" "Set the given block and inode quota limits for the given user\n\n" "When either grpquota or projquota is set to True, the query returns " "group or project quotas instead of user quotas. Only one of these " "options should be True. Project quotas are supported only by XFS " "file systems.\n\n" "Limit parameters may also be specified in form of keyword parameters " "using the names given in the signature above. Omitted values default " "to zero."); static PyObject * Quota_setqlim(Quota_ObjectType *self, PyObject *args, PyObject *kwds) { int uid = -1; unsigned long long bs = 0; unsigned long long bh = 0; unsigned long long fs = 0; unsigned long long fh = 0; int timelimflag = 0; int is_grpquota = FALSE; int is_prjquota = FALSE; static char * kwlist[] = {"uid", "bsoft", "bhard", "isoft", "ihard", "timereset", "grpquota", "prjquota", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "i|KKKK$ppp", kwlist, &uid, &bs, &bh, &fs, &fh, &timelimflag, &is_grpquota, &is_prjquota)) { return NULL; } PyObject * RETVAL = Py_None; if (self->m_dev_fs_type == QUOTA_DEV_INVALID) { RETVAL = FsQuota_QuotaCtlException(self, EINVAL, "FsQuota.Quota instance is uninitialized"); } else if (self->m_dev_fs_type == QUOTA_DEV_NFS) { RETVAL = FsQuota_QuotaCtlException(self, ENOTSUP, "Setting quota on NFS-mount is not supported"); } else if (is_prjquota && (self->m_dev_fs_type != QUOTA_DEV_XFS)) { RETVAL = FsQuota_QuotaCtlException(self, ENOTSUP, "Project quotas are only supported by XFS"); } else #ifdef SGI_XFS if (self->m_dev_fs_type == QUOTA_DEV_XFS) { fs_disk_quota_t xfs_dqblk; xfs_dqblk.d_blk_softlimit = QX_MUL(bs); xfs_dqblk.d_blk_hardlimit = QX_MUL(bh); xfs_dqblk.d_btimer = timelimflag; xfs_dqblk.d_ino_softlimit = fs; xfs_dqblk.d_ino_hardlimit = fh; xfs_dqblk.d_itimer = timelimflag; xfs_dqblk.d_fieldmask = FS_DQ_LIMIT_MASK; xfs_dqblk.d_flags = XFS_USER_QUOTA; #ifndef linux int err = quotactl(Q_XSETQLIM, self->m_qcarg, uid, CADR &xfs_dqblk); #else int err = quotactl(QCMD(Q_XSETQLIM, (is_prjquota ? XQM_PRJQUOTA : (is_grpquota ? XQM_GRPQUOTA : XQM_USRQUOTA))), self->m_qcarg, uid, CADR &xfs_dqblk); #endif if (err) { RETVAL = FsQuota_QuotaCtlException(self, errno, NULL); } } else // if not xfs, than it's a classic IRIX efs file system #endif #ifdef SOLARIS_VXFS if (self->m_dev_fs_type == QUOTA_DEV_VXFS) { struct vx_dqblk vxfs_dqb; vxfs_dqb.dqb_bsoftlimit = Q_MUL(bs); vxfs_dqb.dqb_bhardlimit = Q_MUL(bh); vxfs_dqb.dqb_btimelimit = timelimflag; vxfs_dqb.dqb_fsoftlimit = fs; vxfs_dqb.dqb_fhardlimit = fh; vxfs_dqb.dqb_ftimelimit = timelimflag; int err = vx_quotactl(VX_SETQUOTA, self->m_qcarg, uid, CADR &vxfs_dqb); if (err) { RETVAL = FsQuota_QuotaCtlException(self, errno, NULL); } } else #endif #ifdef AFSQUOTA if (self->m_dev_fs_type == QUOTA_DEV_AFS) { if (!afs_check()) // check is *required* as setup! { RETVAL = FsQuota_QuotaCtlException(self, EINVAL, "AFS setup via afc_check failed"); } else { int err = afs_setqlim(self->m_qcarg, bh); if (err) { RETVAL = FsQuota_QuotaCtlException(self, errno, NULL); } } } else #endif #if defined(HAVE_JFS2) if (self->m_dev_fs_type == QUOTA_DEV_JFS2) { quota64_t user_quota; int err = quotactl(self->m_qcarg, QCMD(Q_J2GETQUOTA, (is_grpquota ? GRPQUOTA : USRQUOTA)), uid, CADR &user_quota); if (err == 0) { user_quota.bsoft = bs; user_quota.bhard = bh; user_quota.btime = timelimflag; user_quota.isoft = fs; user_quota.ihard = fh; user_quota.itime = timelimflag; err = quotactl(self->m_qcarg, QCMD(Q_J2PUTQUOTA, (is_grpquota ? GRPQUOTA : USRQUOTA)), uid, CADR &user_quota); } if (err) { RETVAL = FsQuota_QuotaCtlException(self, errno, NULL); } } else #endif /* HAVE_JFS2 */ { #ifdef NETBSD_LIBQUOTA struct quotahandle *qh; struct quotakey qk; struct quotaval qv; qh = quota_open(self->m_qcarg); if (qh != NULL) { qk.qk_idtype = is_grpquota ? QUOTA_IDTYPE_GROUP : QUOTA_IDTYPE_USER; qk.qk_id = uid; qk.qk_objtype = QUOTA_OBJTYPE_BLOCKS; // set the grace period for blocks if (timelimflag) // seven days { qv.qv_grace = 7*24*60*60; } else if (quota_get(qh, &qk, &qv) >= 0) // use user's current setting { // OK } else if (qk.qk_id = QUOTA_DEFAULTID, quota_get(qh, &qk, &qv) >= 0) // use default setting { // OK, reset qk_id qk.qk_id = uid; } else { qv.qv_grace = 0; } qv.qv_usage = 0; qv.qv_hardlimit = Q_MUL(bh); qv.qv_softlimit = Q_MUL(bs); qv.qv_expiretime = 0; if (quota_put(qh, &qk, &qv) >= 0) { qk.qk_objtype = QUOTA_OBJTYPE_FILES; // set the grace period for files, see comments above if (timelimflag) { qv.qv_grace = 7*24*60*60; } else if (quota_get(qh, &qk, &qv) >= 0) { // OK } else if (qk.qk_id = QUOTA_DEFAULTID, quota_get(qh, &qk, &qv) >= 0) { // OK, reset qk_id qk.qk_id = uid; } else { qv.qv_grace = 0; } qv.qv_usage = 0; qv.qv_hardlimit = fh; qv.qv_softlimit = fs; qv.qv_expiretime = 0; if (quota_put(qh, &qk, &qv) < 0) { RETVAL = FsQuota_QuotaCtlException(self, errno, NULL); } } else { RETVAL = FsQuota_QuotaCtlException(self, errno, NULL); } quota_close(qh); } else { RETVAL = FsQuota_QuotaCtlException(self, errno, NULL); } #else /* not NETBSD_LIBQUOTA */ struct dqblk dqblk; memset(&dqblk, 0, sizeof(dqblk)); dqblk.QS_BSOFT = Q_MUL(bs); dqblk.QS_BHARD = Q_MUL(bh); dqblk.QS_BTIME = timelimflag; dqblk.QS_FSOFT = fs; dqblk.QS_FHARD = fh; dqblk.QS_FTIME = timelimflag; // check for truncation during assignment if ((sizeof(dqblk.QS_BSOFT) < sizeof(uint64_t)) && ((bs|bh|fs|fh) & 0xFFFFFFFF00000000ULL)) { RETVAL = FsQuota_OsException(EINVAL, "Device supports only 32-bit quota", NULL); } else { #ifdef USE_IOCTL int fd; if ((fd = open(self->m_qcarg, O_RDONLY)) != -1) { struct quotactl qp; qp.op = Q_SETQLIM; qp.uid = uid; qp.addr = (char *)&dqblk; if (ioctl(fd, Q_QUOTACTL, &qp) != 0) { RETVAL = FsQuota_QuotaCtlException(self, errno, NULL); } close(fd); } else { RETVAL = FsQuota_OsException(errno, "opening device", self->m_qcarg); } #else /* not USE_IOCTL */ #ifdef Q_CTL_V3 /* Linux */ int err = linuxquota_setqlim (self->m_qcarg, uid, is_grpquota, &dqblk); #else #ifdef Q_CTL_V2 int err = quotactl (self->m_qcarg, QCMD(Q_SETQUOTA,(is_grpquota ? GRPQUOTA : USRQUOTA)), uid, CADR &dqblk); #else int err = quotactl (Q_SETQLIM, self->m_qcarg, uid, CADR &dqblk); #endif /* Q_CTL_V2 */ #endif /* Q_CTL_V3 */ if (err) { RETVAL = FsQuota_QuotaCtlException(self, errno, NULL); } #endif /* not USE_IOCTL */ } #endif /* not NETBSD_LIBQUOTA */ } return RETVAL; } // // Implementation of the Quota.sync() method // PyDoc_STRVAR(Quota_sync__doc__, "quota()\n\n" "Sync quota changes to disk."); static PyObject * Quota_sync(Quota_ObjectType *self, PyObject *args) { if (!PyArg_ParseTuple(args, "")) { return NULL; } PyObject * RETVAL = Py_None; if (self->m_dev_fs_type == QUOTA_DEV_INVALID) { RETVAL = FsQuota_QuotaCtlException(self, EINVAL, "FsQuota.Quota instance is uninitialized"); } else #ifdef SOLARIS_VXFS if (self->m_dev_fs_type == QUOTA_DEV_VXFS) { if (vx_quotactl(VX_QSYNCALL, self->m_qcarg, 0, NULL) != 0) { RETVAL = FsQuota_QuotaCtlException(self, errno, NULL); } } else #endif #ifdef AFSQUOTA if (self->m_dev_fs_type == QUOTA_DEV_AFS) { if (!afs_check()) { RETVAL = FsQuota_QuotaCtlException(self, EINVAL, "AFS setup via afc_check failed"); } else { int foo1, foo2; if (afs_getquota(self->m_qcarg, &foo1, &foo2) != 0) { RETVAL = FsQuota_QuotaCtlException(self, EINVAL, NULL); } } } else #endif #ifdef NETBSD_LIBQUOTA // NOP / not supported #else /* !NETBSD_LIBQUOTA */ #ifdef USE_IOCTL { struct quotactl qp; int fd; qp.op = Q_SYNC; if ((fd = open(self->m_qcarg, O_RDONLY)) != -1) { if (ioctl(fd, Q_QUOTACTL, &qp) != 0) { if (errno == ESRCH) { RETVAL = FsQuota_QuotaCtlException(self, EINVAL, NULL); } else { RETVAL = FsQuota_QuotaCtlException(self, errno, NULL); } } close(fd); } else { RETVAL = FsQuota_OsException(errno, "opening device", self->m_qcarg); } } #else /* !USE_IOCTL */ { #ifdef Q_CTL_V3 /* Linux */ #ifdef SGI_XFS if (self->m_dev_fs_type == QUOTA_DEV_XFS) { if (quotactl(QCMD(Q_XQUOTASYNC, XQM_USRQUOTA), self->m_qcarg, 0, NULL) != 0) { RETVAL = FsQuota_QuotaCtlException(self, errno, NULL); } } else #endif /* SGI_XFS */ if (linuxquota_sync(self->m_qcarg, FALSE) != 0) { RETVAL = FsQuota_QuotaCtlException(self, errno, NULL); } #else /* !Q_CTL_V3 */ #ifdef Q_CTL_V2 #ifdef AIX struct stat st; #endif #ifdef AIX if (stat(self->m_qcarg, &st)) { RETVAL = FsQuota_OsException(errno, "accessing device", self->m_qcarg); } else #endif /* AIX */ if (quotactl(self->m_qcarg, QCMD(Q_SYNC, USRQUOTA), 0, NULL) != 0) { RETVAL = FsQuota_QuotaCtlException(self, errno, NULL); } #else /* !Q_CTL_V2 */ #ifdef SGI_XFS #define XFS_UQUOTA (XFS_QUOTA_UDQ_ACCT|XFS_QUOTA_UDQ_ENFD) // Q_SYNC is not supported on XFS filesystems, so emulate it if (self->m_dev_fs_type == QUOTA_DEV_XFS) { fs_quota_stat_t fsq_stat; sync(); if (quotactl(Q_GETQSTAT, self->m_qcarg, 0, CADR &fsq_stat) != 0) { if ((fsq_stat.qs_flags & XFS_UQUOTA) != XFS_UQUOTA) { RETVAL = FsQuota_QuotaCtlException(self, ENOENT, NULL); } else { RETVAL = FsQuota_QuotaCtlException(self, errno, NULL); } } } else #endif /* SGI_XFS */ if (quotactl(Q_SYNC, self->m_qcarg, 0, NULL) != 0) { RETVAL = FsQuota_QuotaCtlException(self, errno, NULL); } #endif /* !Q_CTL_V2 */ #endif /* !Q_CTL_V3 */ } #endif /* !USE_IOCTL */ #endif /* NETBSD_LIBQUOTA */ return RETVAL; } // // Implementation of the Quota.rpc_opt() method // PyDoc_STRVAR(Quota_rpc_opt__doc__, "rpc_opt(*)\n\n" "Set networking and authentication parameters for RPC\n" "Please refer to the documentation for a list of options."); static PyObject * Quota_rpc_opt(Quota_ObjectType *self, PyObject *args, PyObject *kwds) { PyObject * RETVAL = Py_None; #ifndef NO_RPC static char * kwlist[] = {"rpc_port", "rpc_use_tcp", "rpc_timeout", "auth_uid", "auth_gid", "auth_hostname", NULL}; char * p_hostname = NULL; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|$IpIiis", kwlist, &self->m_rpc_opt.port, &self->m_rpc_opt.use_tcp, &self->m_rpc_opt.timeout, &self->m_rpc_opt.auth_uid, &self->m_rpc_opt.auth_gid, &p_hostname )) { return NULL; } if ((self->m_rpc_opt.auth_uid < 0) && (self->m_rpc_opt.auth_gid >= 0)) { self->m_rpc_opt.auth_uid = getuid(); } if ((self->m_rpc_opt.auth_gid < 0) && (self->m_rpc_opt.auth_uid >= 0)) { self->m_rpc_opt.auth_gid = getgid(); } if ((p_hostname != NULL) && (*p_hostname != 0)) { if (strlen(p_hostname) < MAX_MACHINE_NAME) { strcpy(self->m_rpc_opt.auth_hostname, p_hostname); } else { RETVAL = FsQuota_OsException(ENAMETOOLONG, "hostname is too long", p_hostname); } } else if (self->m_rpc_opt.auth_uid >= 0) { if (gethostname(self->m_rpc_opt.auth_hostname, MAX_MACHINE_NAME) != 0) { RETVAL = FsQuota_OsException(errno, "gethostname", NULL); } } #endif return RETVAL; } // // Allocate a new "Quota" object and initialize the C state struct // static PyObject * Quota_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { Quota_ObjectType *self; self = (Quota_ObjectType *) type->tp_alloc(type, 0); #ifndef NO_RPC self->m_rpc_opt.timeout = RPC_DEFAULT_TIMEOUT; self->m_rpc_opt.auth_uid = RPC_AUTH_UGID_NON_INIT; self->m_rpc_opt.auth_gid = RPC_AUTH_UGID_NON_INIT; #endif return (PyObject *) self; } // // De-allocate a "Quota" object and internal resources // static void Quota_dealloc(Quota_ObjectType *self) { if (self->m_path != NULL) { free(self->m_path); } if (self->m_qcarg != NULL) { free(self->m_qcarg); } if (self->m_rpc_host != NULL) { free(self->m_rpc_host); } Py_TYPE(self)->tp_free((PyObject *) self); } // // Implementation of the standard "__init__" function: Usually called after // "new"; may be called again to re-initialize the object. // static int Quota_init(Quota_ObjectType *self, PyObject *args, PyObject *kwds) { static char * kwlist[] = {"path", "rpc_host", NULL}; char * p_path = NULL; char * p_rpc_host = NULL; // reset state in case the module is already initialized if (self->m_path != NULL) { free(self->m_path); self->m_path = NULL; } if (self->m_qcarg != NULL) { free(self->m_qcarg); self->m_qcarg = NULL; } if (self->m_rpc_host != NULL) { free(self->m_rpc_host); self->m_rpc_host = NULL; } self->m_dev_fs_type = QUOTA_DEV_INVALID; if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|s", kwlist, &p_path, &p_rpc_host)) { return -1; } if (p_rpc_host != NULL) { #ifndef NO_RPC self->m_rpc_host = strdup(p_rpc_host); self->m_qcarg = strdup(p_path); self->m_path = strdup("n/a"); self->m_dev_fs_type = QUOTA_DEV_NFS; #else FsQuota_QuotaCtlException(self, ENOTSUP, "RPC not supported for this platform"); return -1; #endif } else { // dup is needed as the string buffer lives only as long as object passed as parameter self->m_path = strdup(p_path); if (Quota_setqcarg(self) != 0) { return -1; } } return 0; } // // Implementation of the standard "repr" function: Returns a "string // representation" of the object. Should include all parameters. // static PyObject * Quota_Repr(Quota_ObjectType *self) { if (self->m_dev_fs_type == QUOTA_DEV_INVALID) { return PyUnicode_FromString(""); } #ifndef NO_RPC else if (self->m_dev_fs_type == QUOTA_DEV_NFS) { return PyUnicode_FromFormat("", self->m_path, self->m_rpc_host, self->m_qcarg, self->m_rpc_opt.port, self->m_rpc_opt.use_tcp, self->m_rpc_opt.timeout, self->m_rpc_opt.auth_uid, self->m_rpc_opt.auth_gid, self->m_rpc_opt.auth_hostname ); } #endif else { const char * typn; switch (self->m_dev_fs_type) { case QUOTA_DEV_NFS: typn = "NFS"; break; // never reached, handled above case QUOTA_DEV_XFS: typn = "XFS"; break; case QUOTA_DEV_VXFS: typn = "VXFS"; break; case QUOTA_DEV_AFS: typn = "AFS"; break; case QUOTA_DEV_JFS2: typn = "JFS2"; break; default: typn = "no"; break; } return PyUnicode_FromFormat("", self->m_path, self->m_qcarg, typn); } } // // Implementation of the standard "__getattr__" function: This function adds // "virtual" attributes "dev" and "is_nfs" on top of the regular attributes // returned by the generic handler of this interface. // static PyObject * Quota_GetAttr(Quota_ObjectType *self, PyObject * attr) { PyObject * RETVAL = PyObject_GenericGetAttr((PyObject*)self, attr); if (RETVAL == NULL) { if (PyUnicode_Check(attr) == 1) { if (strcmp("dev", PyUnicode_AsUTF8(attr)) == 0) { PyErr_Clear(); if (self->m_rpc_host != NULL) RETVAL = PyUnicode_FromFormat("%s:%s", self->m_rpc_host, self->m_qcarg); else if (self->m_qcarg != NULL) RETVAL = PyUnicode_FromString(self->m_qcarg); } else if (strcmp("is_nfs", PyUnicode_AsUTF8(attr)) == 0) { PyErr_Clear(); RETVAL = PyLong_FromLong(self->m_dev_fs_type == QUOTA_DEV_NFS); } } } return RETVAL; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - static PyMethodDef Quota_MethodsDef[] = { {"query", (PyCFunction) Quota_query, METH_VARARGS | METH_KEYWORDS, Quota_query__doc__ }, {"setqlim", (PyCFunction) Quota_setqlim, METH_VARARGS | METH_KEYWORDS, Quota_setqlim__doc__ }, {"sync", (PyCFunction) Quota_sync, METH_VARARGS, Quota_sync__doc__ }, {"rpc_opt", (PyCFunction) Quota_rpc_opt, METH_VARARGS | METH_KEYWORDS, Quota_rpc_opt__doc__ }, {NULL} /* Sentinel */ }; static PyTypeObject QuotaTypeDef = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "FsQuota.Quota", .tp_doc = PyDoc_STR("Class providing access to file-system quota"), .tp_basicsize = sizeof(Quota_ObjectType), .tp_itemsize = 0, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, .tp_new = Quota_new, .tp_init = (initproc) Quota_init, .tp_dealloc = (destructor) Quota_dealloc, .tp_repr = (PyObject * (*)(PyObject*)) Quota_Repr, .tp_getattro = (PyObject * (*)(PyObject*, PyObject*))Quota_GetAttr, .tp_methods = Quota_MethodsDef, //.tp_members = Quota_Members, }; static PyStructSequence_Field QuotaQueryType_Members[] = { { "bcount", PyDoc_STR("Number of blocks currently used") }, { "bsoft", PyDoc_STR("Soft limit for block count (or 0 if none)") }, { "bhard", PyDoc_STR("Hard limit for block count (or 0 if none)") }, { "btime", PyDoc_STR("Time when an exceeded soft block limit turns into " "a hard limit (or n/a when not exceeded)") }, { "icount", PyDoc_STR("Number of inodes (i.e. files) currently used") }, { "isoft", PyDoc_STR("Soft limit for inode count (or 0 if none)") }, { "ihard", PyDoc_STR("Hard limit for inode count (or 0 if none)") }, { "itime", PyDoc_STR("Time when an exceeded soft inode limit turns into " "a hard limit (or n/a when not exceeded)") }, { NULL, NULL } }; static PyStructSequence_Desc QuotaQuery_Desc = { "FsQuota.QueryResult", PyDoc_STR("Named tuple type returned by Quota.query(), containing quota usage and limits"), QuotaQueryType_Members, 8 }; // ---------------------------------------------------------------------------- // Sub-functions for iterating across the mount table typedef struct { const char * fsname; const char * path; const char * fstyp; const char * fsopt; } T_MY_MNTENT_BUF; // // Portable setmntent(): This function must be called once at the start of // iteration. // int my_setmntent(T_MY_MNTENT_STATE * state) { int RETVAL = 0; #ifndef AIX #ifndef NO_MNTENT #ifndef NO_OPEN_MNTTAB if (state->mtab != NULL) endmntent(state->mtab); if ((state->mtab = setmntent(MOUNTED, "r")) == NULL) #else if (state->mtab != NULL) fclose(state->mtab); if ((state->mtab = fopen (MOUNTED,"r")) == NULL) #endif RETVAL = -1; else RETVAL = 0; #else /* NO_MNTENT */ // if (state->mtab != NULL) free(state->mtab); state->mtab_size = getmntinfo(&state->mtab, MNT_NOWAIT); RETVAL = ((state->mtab_size <= 0) ? -1 : 0); state->mntp = state->mtab; #endif #else /* AIX */ int count, space; if (state->mtab != NULL) { free(state->mtab); } count = mntctl(MCTL_QUERY, sizeof(space), (char *) &space); if (count == 0) { state->mtab = (struct vmount *) malloc(space); if (state->mtab != NULL) { count = mntctl(MCTL_QUERY, space, (char *) state->mtab); if (count > 0) { state->aix_mtab_count = count; state->aix_mtab_idx = 0; RETVAL = 0; } else // error, or size changed between calls { if (count == 0) errno = EINTR; RETVAL = -1; } } else { RETVAL = -1; } } else if (count < 0) { RETVAL = -1; } else // should never happen { errno = ENOENT; RETVAL = -1; } #endif return RETVAL; } // // Portable getmntent(): This function fills the given buffers with string // pointers describing the next mount table entry. Note the strings are // located in static memory owned by the function and must not be freed by // the caller; they are invalidated upon the next call. // int my_getmntent(T_MY_MNTENT_STATE * state, T_MY_MNTENT_BUF * str_buf) { int RETVAL = -1; #ifndef AIX #ifndef NO_MNTENT #ifndef NO_OPEN_MNTTAB struct mntent *mntp; if (state->mtab != NULL) { mntp = getmntent(state->mtab); if (mntp != NULL) { str_buf->fsname = mntp->mnt_fsname; str_buf->path = mntp->mnt_dir; str_buf->fstyp = mntp->mnt_type; str_buf->fsopt = mntp->mnt_opts; RETVAL = 0; } } else { errno = EBADF; } #else /* NO_OPEN_MNTTAB */ struct mnttab mntp; if (state->mtab != NULL) { if (getmntent(state->mtab, &mntp) == 0) { str_buf->fsname = mntp->mnt_special; str_buf->path = mntp->mnt_mountp; str_buf->fstyp = mntp->mnt_ftype; str_buf->fsopt = mntp->mnt_mntopts; RETVAL = 0; } } #endif /* NO_OPEN_MNTTAB */ #else /* NO_MNTENT */ if ((state->mtab != NULL) && state->mtab_size) { str_buf->fsname = state->mntp->f_mntfromname; str_buf->path = state->mntp->f_mntonname; #ifdef OSF_QUOTA char *fstype = getvfsbynumber((int)state->mntp->f_type); if (fstype != (char *) -1) { str_buf->fstyp = fstype; } else #endif { str_buf->fstyp = state->mntp->f_fstypename; } snprintf(state->str_mnt_flag, sizeof(state->str_mnt_flag), "%s%s%s%s%s%s%s", ((state->mntp->MNTINFO_FLAG_EL & MNT_LOCAL) ? "local" : "non-local"), ((state->mntp->MNTINFO_FLAG_EL & MNT_RDONLY) ? ",read-only" : ""), ((state->mntp->MNTINFO_FLAG_EL & MNT_SYNCHRONOUS) ? ",sync" : ""), ((state->mntp->MNTINFO_FLAG_EL & MNT_NOEXEC) ? ",noexec" : ""), ((state->mntp->MNTINFO_FLAG_EL & MNT_NOSUID) ? ",nosuid" : ""), ((state->mntp->MNTINFO_FLAG_EL & MNT_ASYNC) ? ",async" : ""), ((state->mntp->MNTINFO_FLAG_EL & MNT_QUOTA) ? ",quotas" : "")); state->str_mnt_flag[sizeof(state->str_mnt_flag) - 1] = 0; str_buf->fsopt = state->str_mnt_flag; RETVAL = 0; state->mtab_size--; state->mntp++; } #endif #else /* AIX */ struct vmount *vmp; char *cp; int i; if ((state->mtab != NULL) && (state->aix_mtab_idx < state->aix_mtab_count)) { cp = (char *) state->mtab; for (i=0; iaix_mtab_idx; i++) { vmp = (struct vmount *) cp; cp += vmp->vmt_length; } vmp = (struct vmount *) cp; state->aix_mtab_idx += 1; if ((vmp->vmt_gfstype != MNT_NFS) && (vmp->vmt_gfstype != MNT_NFS3)) { cp = vmt2dataptr(vmp, VMT_OBJECT); str_buf->fsname = cp; } else { uchar *mp, *cp2; cp = vmt2dataptr(vmp, VMT_HOST); cp2 = vmt2dataptr(vmp, VMT_OBJECT); mp = malloc(strlen(cp) + strlen(cp2) + 2); if (mp != NULL) { strcpy(mp, cp); strcat(mp, ":"); strcat(mp, cp2); str_buf->fsname = mp; free(mp); } else { cp = "?"; str_buf->fsname = cp; } } cp = vmt2dataptr(vmp, VMT_STUB); str_buf->path = cp; switch(vmp->vmt_gfstype) { case MNT_NFS: cp = "nfs"; break; case MNT_NFS3: cp = "nfs"; break; case MNT_JFS: cp = "jfs"; break; #if defined(MNT_AIX) && defined(MNT_J2) && (MNT_AIX==MNT_J2) case MNT_J2: cp = "jfs2"; break; #else #if defined(MNT_J2) case MNT_J2: cp = "jfs2"; break; #endif case MNT_AIX: cp = "aix"; break; #endif case 4: cp = "afs"; break; case MNT_CDROM: cp = "cdrom,ignore"; break; default: cp = "unknown,ignore"; break; } str_buf->fstyp = cp; cp = vmt2dataptr(vmp, VMT_ARGS); str_buf->fsopt = cp; RETVAL = 0; } #endif /* AIX */ return RETVAL; } // // Portable endmntent(): This function end the iteration and frees resources // (if needed). This function should be called once for each call to // setmntent(). getmntent must not be called afterward. // void my_endmntent(T_MY_MNTENT_STATE * state) { if (state->mtab != NULL) { #ifndef AIX #ifndef NO_MNTENT #ifndef NO_OPEN_MNTTAB endmntent(state->mtab); // returns always 1 in SunOS #else fclose (state->mtab); #endif // else: if (state->mtab != NULL) free(state->mtab); #endif #else /* AIX */ free(state->mtab); #endif state->mtab = NULL; } } // ---------------------------------------------------------------------------- // Class "MntTab" // ---------------------------------------------------------------------------- // // Container for instance state variables // typedef struct { PyObject_HEAD T_MY_MNTENT_STATE mntent; // transparent state used by my_getmntent() int iterIndex; // index tracking calls of __next__() } MntTab_ObjectType; // // Allocate a new object and initialize the iteration state. // Raise an exception if setmntent reports an error. // static PyObject * MntTab_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { MntTab_ObjectType *self; self = (MntTab_ObjectType *) type->tp_alloc(type, 0); if (my_setmntent(&self->mntent) != 0) { Py_DECREF(self); return FsQuota_OsException(errno, "setmntent", NULL); } self->iterIndex = 0; return (PyObject *) self; } // // De-allocate an object and internal resources // static void MntTab_dealloc(MntTab_ObjectType *self) { my_endmntent(&self->mntent); Py_TYPE(self)->tp_free((PyObject *) self); } // // Implementation of the standard "__init__" function: Usually called after // "new": in this case the function does nothing as initialization is already // done within "new"; may be called again to re-initialize the object: if // __next__() was called at least once, iteration is re-initialized. // static int MntTab_init(MntTab_ObjectType *self, PyObject *args, PyObject *kwds) { char *kwlist[] = {NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "", kwlist)) { return -1; } if (self->iterIndex != 0) { // re-initialize iterator if (my_setmntent(&self->mntent) != 0) { FsQuota_OsException(errno, "setmntent", NULL); return -1; } self->iterIndex = 0; } return 0; } // // Implementation of the standard "repr" function: Returns a "string // representation" of the object. Should include all parameters; in this // case we can only show the iteration index. // static PyObject * MntTab_Repr(MntTab_ObjectType *self) { if (self->iterIndex >= 0) { return PyUnicode_FromFormat("", self->iterIndex); } else { return PyUnicode_FromFormat(""); } } // // Implementation of the standard "__iter__" function: Simply returns a // reference to itself, as the object is already set up for iteration. // static PyObject * MntTab_Iter(MntTab_ObjectType *self) { Py_INCREF(self); return (PyObject *) self; } // // Implementation of the standard "__next__" function: Call C getmntent() and // return the resulting strings in form of a tuple, or raise an exception upon // end of iteration. // static PyObject * MntTab_IterNext(MntTab_ObjectType *self) { T_MY_MNTENT_BUF str_buf; PyObject * RETVAL = NULL; if ((self->iterIndex >= 0) && my_getmntent(&self->mntent, &str_buf) == 0) { RETVAL = PyStructSequence_New(FsQuota_MntTabType); if (str_buf.fsname != NULL) PyStructSequence_SetItem(RETVAL, 0, PyUnicode_DecodeFSDefault(str_buf.fsname)); if (str_buf.path != NULL) PyStructSequence_SetItem(RETVAL, 1, PyUnicode_DecodeFSDefault(str_buf.path)); if (str_buf.fstyp != NULL) PyStructSequence_SetItem(RETVAL, 2, PyUnicode_DecodeFSDefault(str_buf.fstyp)); if (str_buf.fsopt != NULL) PyStructSequence_SetItem(RETVAL, 3, PyUnicode_DecodeFSDefault(str_buf.fsopt)); self->iterIndex += 1; } else { PyErr_SetNone(PyExc_StopIteration); self->iterIndex = -1; } return RETVAL; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - static PyTypeObject MntTabTypeDef = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "FsQuota.MntTab", .tp_doc = PyDoc_STR("Class providing iterator for the file-system mount table"), .tp_basicsize = sizeof(MntTab_ObjectType), .tp_itemsize = 0, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, .tp_new = MntTab_new, .tp_init = (initproc) MntTab_init, .tp_dealloc = (destructor) MntTab_dealloc, .tp_repr = (PyObject * (*)(PyObject*)) MntTab_Repr, .tp_iter = (PyObject * (*)(PyObject*)) MntTab_Iter, .tp_iternext = (PyObject * (*)(PyObject*)) MntTab_IterNext, //.tp_members = MntTab_Members, //.tp_methods = MntTab_MethodsDef, }; static PyStructSequence_Field MntEntType_Members[] = { { "mnt_fsname", PyDoc_STR("Name of the filesystem (e.g. device name)") }, { "mnt_dir", PyDoc_STR("Filesystem path prefix (aka mount point)") }, { "mnt_type", PyDoc_STR("Mount type (aka file system type)") }, { "mnt_opts", PyDoc_STR("Mount options, separated by colon.") }, { NULL, NULL } }; static PyStructSequence_Desc MntEntType_Desc = { "FsQuota.MntEnt", PyDoc_STR("Mount table entry type, as returned by iterator of class FsQuota.MntTab"), MntEntType_Members, 4 }; // ---------------------------------------------------------------------------- // // Determine "device" argument for the "Quota" class methods // static int Quota_setqcarg(Quota_ObjectType *self) { struct stat statbuf_target; // keep complete struct for comparison b/c type of st_dev varies b/w platforms struct stat statbuf_ent; // determine device ID at the given path for later comparison with mount points if (stat(self->m_path, &statbuf_target) != 0) { FsQuota_OsException(errno, "Failed to access path", self->m_path); return -1; } T_MY_MNTENT_STATE l_mntab; memset(&l_mntab, 0, sizeof(l_mntab)); if (my_setmntent(&l_mntab) != 0) { FsQuota_OsException(errno, "setmntent", NULL); return -1; } // loop to search the given path's entry in the mount table T_MY_MNTENT_BUF mntent; while (my_getmntent(&l_mntab, &mntent) == 0) { if ((strcmp(mntent.fstyp, "lofs") == 0) || (strcmp(mntent.fstyp, "ignore") == 0) || (strcmp(mntent.fstyp, "proc") == 0) || (strcmp(mntent.fstyp, "rootfs") == 0) || (strncmp(mntent.fstyp, "auto", 4) == 0) ) { continue; } // compare device ID of mount point with that of target path if ((stat(mntent.path, &statbuf_ent) == 0) && (statbuf_target.st_dev == statbuf_ent.st_dev)) { const char * p = NULL; // NFS host:/path if ((mntent.fsname[0] != '/') && ((p = strchr(mntent.fsname, ':')) != NULL) && (p[1] == '/')) { #ifndef NO_RPC self->m_rpc_host = strdup(mntent.fsname); self->m_rpc_host[p - mntent.fsname] = 0; self->m_qcarg = strdup(p + 1); self->m_dev_fs_type = QUOTA_DEV_NFS; #endif } // NFS /path@host -> swap to "host:/path" else if ((strncmp(mntent.fstyp, "nfs", 3) == 0) && (mntent.fsname[0] == '/') && ((p = strchr(mntent.fsname, '@')) != NULL) && (strchr(p + 1, '/') == NULL) ) { #ifndef NO_RPC self->m_qcarg = (char*) malloc(strlen(mntent.fsname) + 1 + 1); sprintf(self->m_qcarg, "%s:%.*s", p + 1, (int)(p - mntent.fsname), mntent.fsname); self->m_qcarg = strdup(mntent.fsname); self->m_qcarg[p - mntent.fsname] = 0; self->m_rpc_host = strdup(p + 1); self->m_dev_fs_type = QUOTA_DEV_NFS; #endif } else // local device { self->m_dev_fs_type = QUOTA_DEV_REGULAR; // XFS, VxFS and AFS quotas require separate access methods #if defined (SGI_XFS) // (optional for VxFS: later versions use 'normal' quota interface) if (strcmp(mntent.fstyp, "xfs") == 0) self->m_dev_fs_type = QUOTA_DEV_XFS; #endif #if defined (SOLARIS_VXFS) if (strcmp(mntent.fstyp, "vxfs") == 0) self->m_dev_fs_type = QUOTA_DEV_VXFS; #endif #ifdef AFSQUOTA if ((strcmp(mntent.fstyp, "afs") == 0) && (strcmp(mntent.fsname, "AFS") == 0)) self->m_dev_fs_type = QUOTA_DEV_AFS; #endif #if defined(HAVE_JFS2) if (strcmp(mntent.fstyp, "jfs2") == 0) self->m_dev_fs_type = QUOTA_DEV_JFS2; #endif #if defined(USE_IOCTL) || defined(QCARG_MNTPT) // use mount point self->m_qcarg = strdup(mntent.path); #elif defined(HAVE_JFS2) || defined(AIX) || defined(OSF_QUOTA) // use path of any file in the file system self->m_qcarg = strdup(target_path); #elif defined (Q_CTL_V2) // use path of "quotas" file directly under fs root path self->m_qcarg = (char *) malloc(strlen(mntent.path) + 7 + 1); strcpy(self->m_qcarg, mntent.path); strcat(self->m_qcarg, "/quotas"); #else // use device path // check for special case: Linux mount -o loop if (((p = strstr(mntent.fsopt, "loop=/dev/")) != NULL) && ((p == mntent.fsopt) || (*(p - 1) == ','))) { const char * pe = strchr(p, ','); if (pe != NULL) { self->m_qcarg = strdup(p); self->m_qcarg[pe - p] = 0; } else { self->m_qcarg = strdup(p); } } else { self->m_qcarg = strdup(mntent.fsname); } #endif } // getmntent loop done break; } } my_endmntent(&l_mntab); if (self->m_qcarg == NULL) { FsQuota_OsException(EINVAL, "Mount path not found or device unsupported", self->m_qcarg); self->m_dev_fs_type = QUOTA_DEV_INVALID; return -1; } return 0; } // ---------------------------------------------------------------------------- // Top-level definition of the module // - the module only contains the two classes, but no methods directly static struct PyModuleDef FsQuota_module = { PyModuleDef_HEAD_INIT, .m_name = "FsQuota", .m_doc = PyDoc_STR("The FsQuota module provides the Quota and MntTab classes"), .m_size = -1, //.m_methods = FsQuota_Methods }; PyMODINIT_FUNC PyInit_FsQuota(void) { if ((PyType_Ready(&QuotaTypeDef) < 0) || (PyType_Ready(&MntTabTypeDef) < 0)) { return NULL; } PyObject * module = PyModule_Create(&FsQuota_module); if (module == NULL) { return NULL; } // create exception class "FsQuota.error", derived from OSError FsQuotaError = PyErr_NewException("FsQuota.error", PyExc_OSError, NULL); Py_XINCREF(FsQuotaError); if (PyModule_AddObject(module, "error", FsQuotaError) < 0) { Py_XDECREF(FsQuotaError); Py_CLEAR(FsQuotaError); Py_DECREF(module); return NULL; } // create class "FsQuota.Quota" Py_INCREF(&QuotaTypeDef); if (PyModule_AddObject(module, "Quota", (PyObject *) &QuotaTypeDef) < 0) { Py_DECREF(&QuotaTypeDef); Py_XDECREF(FsQuotaError); Py_CLEAR(FsQuotaError); Py_DECREF(module); return NULL; } // create class "FsQuota.MntTab" Py_INCREF(&MntTabTypeDef); if (PyModule_AddObject(module, "MntTab", (PyObject *) &MntTabTypeDef) < 0) { Py_DECREF(&MntTabTypeDef); Py_DECREF(&QuotaTypeDef); Py_XDECREF(FsQuotaError); Py_CLEAR(FsQuotaError); Py_DECREF(module); return NULL; } #if defined (NAMED_TUPLE_GC_BUG) if (PyStructSequence_InitType2(&FsQuota_QuotaQueryTypeBuf, &QuotaQuery_Desc) != 0) #else FsQuota_QuotaQueryType = PyStructSequence_NewType(&QuotaQuery_Desc); if (FsQuota_QuotaQueryType == NULL) #endif { Py_DECREF(&MntTabTypeDef); Py_XDECREF(FsQuotaError); Py_CLEAR(FsQuotaError); Py_DECREF(module); return NULL; } #if defined (NAMED_TUPLE_GC_BUG) if (PyStructSequence_InitType2(&FsQuota_MntTabTypeBuf, &MntEntType_Desc) != 0) #else FsQuota_MntTabType = PyStructSequence_NewType(&MntEntType_Desc); if (FsQuota_MntTabType == NULL) #endif { #if !defined (NAMED_TUPLE_GC_BUG) Py_DECREF(FsQuota_QuotaQueryType); #endif Py_DECREF(&MntTabTypeDef); Py_XDECREF(FsQuotaError); Py_CLEAR(FsQuotaError); Py_DECREF(module); return NULL; } return module; } python-fsquota-0.1.0+dfsg1/src/afsquota.c000066400000000000000000000066451400261757100203050ustar00rootroot00000000000000/* * Interface to OpenAFS * * Contributed 1998,2003 by Wolfgang Friebel */ #if defined( __hpux) #define IGNORE_STDS_H #endif #include #include #include #include #define MAXSIZE 2048 #define MAXDNAME 2048 #include #include #include #ifdef SunOS4 #define _VICEIOCTL(id) ((unsigned int ) _IOW(V, id, struct ViceIoctl)) #endif /* * Check if AFS is installed */ int afs_check(void) { struct ViceIoctl vi; int32_t code; char space[MAXSIZE]; vi.in_size = 0; vi.out_size = MAXSIZE; vi.out = (caddr_t) space; code = pioctl(NULL, VIOC_GET_WS_CELL, &vi, 0); return ! code; } /* * report quota */ int afs_getquota(char *path, int *maxQuota, int *blocksUsed) { struct ViceIoctl a_params; struct VolumeStatus *vs; a_params.in_size = 0; a_params.out_size = MAXSIZE; a_params.in = NULL; a_params.out = malloc(MAXSIZE); if (a_params.out == NULL) { errno = ENOMEM; return -1; } if (pioctl(path, VIOCGETVOLSTAT,&a_params,1) == -1) { free(a_params.out); return -1; } vs = (struct VolumeStatus *) a_params.out; *maxQuota = vs->MaxQuota; *blocksUsed = vs->BlocksInUse; free(a_params.out); return 0; } /* * Usage: fs sq */ int afs_setqlim(char *path, int maxQuota) { struct ViceIoctl a_params; struct VolumeStatus *vs; int insize; a_params.in_size = 0; a_params.out_size = MAXSIZE; a_params.in = NULL; a_params.out = malloc(MAXSIZE); if (a_params.out == NULL) { errno = ENOMEM; return -1; } /* Read the old volume status */ if(pioctl(path,VIOCGETVOLSTAT,&a_params,1) == -1) { free(a_params.out); return -1; } insize = sizeof(struct VolumeStatus) + strlen(path) + 2; a_params.in_size = ((MAXSIZE < insize) ? MAXSIZE : insize); a_params.out_size = 0; a_params.in = a_params.out; a_params.out = NULL; vs = (struct VolumeStatus *) a_params.in; vs->MaxQuota = maxQuota; if(pioctl(path,VIOCSETVOLSTAT,&a_params,1) == -1) { free(a_params.in); return -1; } free(a_params.in); return 0; } #ifdef __hpux int sigvec( int sig, struct sigvec *vec, struct sigvec *ovec ) { return sigvector(sig, vec, ovec); } #endif #ifdef STAND_ALONE int main(int argc, char **argv) { int usage, limit; if (afs_check()) { if (afs_getquota("/afs/ifh.de/user/z/zorner", &limit, &usage) == 0) { printf("limit=%d usage=%d\n", limit, usage); } else perror("Not an AFS filesystem"); } else { printf("No AFS available\n"); } } #endif /* * Compiler options for standalone compilation */ /* AIX: cc afsquota.c -L/products/security/athena/lib -lkafs -ldes -lkrb -lroken -lld IRIX: cc afsquota.c -L/products/security/athena/lib -lkafs -ldes -lkrb \ -Wl,-rpath -Wl,/products/security/athena/lib Linux: cc afsquota.c -L/products/security/athena/lib -lkafs -ldes -lkrb \ -Wl,-rpath -Wl,/products/security/athena/lib HP-UX: cc -Ae afsquota.c -L/products/security/athena/lib -lkafs -ldes -lkrb Solaris: (Workshop compiler) cc afsquota.c -L/products/security/athena/lib -lkafs -ldes -lkrb \ -Wl,-R -Wl,/products/security/athena/lib SunOS: acc afsquota.c -U__STDC__ -DSunOS4 \ -L/products/security/athena/lib -lkafs -ldes -lkrb */ python-fsquota-0.1.0+dfsg1/src/afsquota.h000066400000000000000000000002711400261757100202770ustar00rootroot00000000000000/* * prototype declarations for AFS quota interface */ int afs_check(void); int afs_getquota(char *path, int *maxQuota, int *blocksUsed); int afs_setqlim(char *path, int maxQuota); python-fsquota-0.1.0+dfsg1/src/linuxapi.c000066400000000000000000000260171400261757100203060ustar00rootroot00000000000000/* ** Linux quotactl wrapper ** Required to support 3 official and intermediate quotactl() versions */ #include #include #include #include #include #include #include "myconfig.h" /* API v1 command definitions */ #define Q_V1_GETQUOTA 0x0300 #define Q_V1_SYNC 0x0600 #define Q_V1_SETQLIM 0x0700 #define Q_V1_GETSTATS 0x0800 /* API v2 command definitions */ #define Q_V2_SYNC 0x0600 #define Q_V2_SETQLIM 0x0700 #define Q_V2_GETQUOTA 0x0D00 #define Q_V2_GETSTATS 0x1100 /* proc API command definitions */ #define Q_V3_SYNC 0x800001 #define Q_V3_GETQUOTA 0x800007 #define Q_V3_SETQUOTA 0x800008 /* Interface versions */ #define IFACE_UNSET 0 #define IFACE_VFSOLD 1 #define IFACE_VFSV0 2 #define IFACE_GENERIC 3 /* format supported by current kernel */ static int kernel_iface = IFACE_UNSET; /* * Quota structure used for communication with userspace via quotactl * Following flags are used to specify which fields 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) /* ** Copy of struct declarations in the v2 quota.h header file ** (with structure names changed to avoid conflicts with v2 headers). ** This is required to be able to compile with v1 kernel headers. */ /* ** Packed into wrapper for compatibility of 32-bit clients with 64-bit kernels: ** 64-bit compilers add 4 padding bytes at the end of the struct, so a memcpy ** corrupts the 4 bytes following the struct in the 32-bit clients userspace */ union dqblk_v3_wrap { struct dqblk_v3 { u_int64_t dqb_bhardlimit; u_int64_t dqb_bsoftlimit; u_int64_t dqb_curspace; u_int64_t dqb_ihardlimit; u_int64_t dqb_isoftlimit; u_int64_t dqb_curinodes; u_int64_t dqb_btime; u_int64_t dqb_itime; u_int32_t dqb_valid; } dqblk; u_int64_t foo[9]; }; struct dqstats_v2 { u_int32_t lookups; u_int32_t drops; u_int32_t reads; u_int32_t writes; u_int32_t cache_hits; u_int32_t allocated_dquots; u_int32_t free_dquots; u_int32_t syncs; u_int32_t version; }; struct dqblk_v2 { unsigned int dqb_ihardlimit; unsigned int dqb_isoftlimit; unsigned int dqb_curinodes; unsigned int dqb_bhardlimit; unsigned int dqb_bsoftlimit; qsize_t dqb_curspace; time_t dqb_btime; time_t dqb_itime; }; struct dqblk_v1 { u_int32_t dqb_bhardlimit; u_int32_t dqb_bsoftlimit; u_int32_t dqb_curblocks; u_int32_t dqb_ihardlimit; u_int32_t dqb_isoftlimit; u_int32_t dqb_curinodes; time_t dqb_btime; time_t dqb_itime; }; /* ** Check kernel quota version ** Taken from quota-tools 3.08 by Jan Kara */ static void linuxquota_get_api( void ) { #ifndef LINUX_API_VERSION struct stat st; if (stat("/proc/sys/fs/quota", &st) == 0) { kernel_iface = IFACE_GENERIC; } else { struct dqstats_v2 v2_stats; struct sigaction sig; struct sigaction oldsig; /* This signal handling is needed because old kernels send us SIGSEGV as they try to resolve the device */ sig.sa_handler = SIG_IGN; sig.sa_sigaction = NULL; sig.sa_flags = 0; sigemptyset(&sig.sa_mask); if (sigaction(SIGSEGV, &sig, &oldsig) < 0) { fprintf(stderr, "linuxapi.c warning: cannot set SEGV signal handler: %s\n", strerror(errno)); goto failure; } if (quotactl(QCMD(Q_V2_GETSTATS, 0), NULL, 0, (void *)&v2_stats) >= 0) { kernel_iface = IFACE_VFSV0; } else if (errno != ENOSYS && errno != ENOTSUP) { /* RedHat 7.1 (2.4.2-2) newquota check * Q_V2_GETSTATS in it's old place, Q_GETQUOTA in the new place * (they haven't moved Q_GETSTATS to its new value) */ int err_stat = 0; int err_quota = 0; char tmp[1024]; /* Just temporary buffer */ if (quotactl(QCMD(Q_V1_GETSTATS, 0), NULL, 0, tmp)) err_stat = errno; if (quotactl(QCMD(Q_V1_GETQUOTA, 0), "/dev/null", 0, tmp)) err_quota = errno; /* On a RedHat 2.4.2-2 we expect 0, EINVAL * On a 2.4.x we expect 0, ENOENT * On a 2.4.x-ac we wont get here */ if (err_stat == 0 && err_quota == EINVAL) { kernel_iface = IFACE_VFSV0; } else { kernel_iface = IFACE_VFSOLD; } } else { /* This branch is *not* in quota-tools 3.08 ** but without it quota version is not correctly ** identified for the original SuSE 8.0 kernel */ unsigned int vers_no; FILE * qf; if ((qf = fopen("/proc/fs/quota", "r"))) { if (fscanf(qf, "Version %u", &vers_no) == 1) { if ( (vers_no == (6*10000 + 5*100 + 0)) || (vers_no == (6*10000 + 5*100 + 1)) ) { kernel_iface = IFACE_VFSV0; } } fclose(qf); } } if (sigaction(SIGSEGV, &oldsig, NULL) < 0) { fprintf(stderr, "linuxapi.c warning: cannot reset signal handler: %s\n", strerror(errno)); goto failure; } } failure: if (kernel_iface == IFACE_UNSET) kernel_iface = IFACE_VFSOLD; #else /* defined LINUX_API_VERSION */ kernel_iface = LINUX_API_VERSION; #endif } /* ** Wrapper for the quotactl(GETQUOTA) call. ** For API v2 the results are copied back into a v1 structure. */ int linuxquota_query( const char * dev, int uid, int isgrp, struct dqblk * dqb ) { int ret; if (kernel_iface == IFACE_UNSET) linuxquota_get_api(); if (kernel_iface == IFACE_GENERIC) { union dqblk_v3_wrap dqb3; ret = quotactl(QCMD(Q_V3_GETQUOTA, (isgrp ? GRPQUOTA : USRQUOTA)), dev, uid, (caddr_t) &dqb3.dqblk); if (ret == 0) { dqb->dqb_bhardlimit = dqb3.dqblk.dqb_bhardlimit; dqb->dqb_bsoftlimit = dqb3.dqblk.dqb_bsoftlimit; dqb->dqb_curblocks = dqb3.dqblk.dqb_curspace / DEV_QBSIZE; dqb->dqb_ihardlimit = dqb3.dqblk.dqb_ihardlimit; dqb->dqb_isoftlimit = dqb3.dqblk.dqb_isoftlimit; dqb->dqb_curinodes = dqb3.dqblk.dqb_curinodes; dqb->dqb_btime = dqb3.dqblk.dqb_btime; dqb->dqb_itime = dqb3.dqblk.dqb_itime; } } else if (kernel_iface == IFACE_VFSV0) { struct dqblk_v2 dqb2; ret = quotactl(QCMD(Q_V2_GETQUOTA, (isgrp ? GRPQUOTA : USRQUOTA)), dev, uid, (caddr_t) &dqb2); if (ret == 0) { dqb->dqb_bhardlimit = dqb2.dqb_bhardlimit; dqb->dqb_bsoftlimit = dqb2.dqb_bsoftlimit; dqb->dqb_curblocks = dqb2.dqb_curspace / DEV_QBSIZE; dqb->dqb_ihardlimit = dqb2.dqb_ihardlimit; dqb->dqb_isoftlimit = dqb2.dqb_isoftlimit; dqb->dqb_curinodes = dqb2.dqb_curinodes; dqb->dqb_btime = dqb2.dqb_btime; dqb->dqb_itime = dqb2.dqb_itime; } } else /* if (kernel_iface == IFACE_VFSOLD) */ { struct dqblk_v1 dqb1; ret = quotactl(QCMD(Q_V1_GETQUOTA, (isgrp ? GRPQUOTA : USRQUOTA)), dev, uid, (caddr_t) &dqb1); if (ret == 0) { dqb->dqb_bhardlimit = dqb1.dqb_bhardlimit; dqb->dqb_bsoftlimit = dqb1.dqb_bsoftlimit; dqb->dqb_curblocks = dqb1.dqb_curblocks; dqb->dqb_ihardlimit = dqb1.dqb_ihardlimit; dqb->dqb_isoftlimit = dqb1.dqb_isoftlimit; dqb->dqb_curinodes = dqb1.dqb_curinodes; dqb->dqb_btime = dqb1.dqb_btime; dqb->dqb_itime = dqb1.dqb_itime; } } return ret; } /* ** Wrapper for the quotactl(GETQUOTA) call. ** For API v2 and v3 the parameters are copied into the internal structure. */ int linuxquota_setqlim( const char * dev, int uid, int isgrp, struct dqblk * dqb ) { int ret; if (kernel_iface == IFACE_UNSET) linuxquota_get_api(); if (kernel_iface == IFACE_GENERIC) { union dqblk_v3_wrap dqb3; dqb3.dqblk.dqb_bhardlimit = dqb->dqb_bhardlimit; dqb3.dqblk.dqb_bsoftlimit = dqb->dqb_bsoftlimit; dqb3.dqblk.dqb_curspace = 0; dqb3.dqblk.dqb_ihardlimit = dqb->dqb_ihardlimit; dqb3.dqblk.dqb_isoftlimit = dqb->dqb_isoftlimit; dqb3.dqblk.dqb_curinodes = 0; dqb3.dqblk.dqb_btime = dqb->dqb_btime; dqb3.dqblk.dqb_itime = dqb->dqb_itime; dqb3.dqblk.dqb_valid = (QIF_BLIMITS | QIF_ILIMITS); ret = quotactl (QCMD(Q_V3_SETQUOTA, (isgrp ? GRPQUOTA : USRQUOTA)), dev, uid, (caddr_t) &dqb3.dqblk); } else if (kernel_iface == IFACE_VFSV0) { struct dqblk_v2 dqb2; dqb2.dqb_bhardlimit = dqb->dqb_bhardlimit; dqb2.dqb_bsoftlimit = dqb->dqb_bsoftlimit; dqb2.dqb_curspace = 0; dqb2.dqb_ihardlimit = dqb->dqb_ihardlimit; dqb2.dqb_isoftlimit = dqb->dqb_isoftlimit; dqb2.dqb_curinodes = 0; dqb2.dqb_btime = dqb->dqb_btime; dqb2.dqb_itime = dqb->dqb_itime; ret = quotactl (QCMD(Q_V2_SETQLIM, (isgrp ? GRPQUOTA : USRQUOTA)), dev, uid, (caddr_t) &dqb2); } else /* if (kernel_iface == IFACE_VFSOLD) */ { struct dqblk_v1 dqb1; dqb1.dqb_bhardlimit = dqb->dqb_bhardlimit; dqb1.dqb_bsoftlimit = dqb->dqb_bsoftlimit; dqb1.dqb_curblocks = 0; dqb1.dqb_ihardlimit = dqb->dqb_ihardlimit; dqb1.dqb_isoftlimit = dqb->dqb_isoftlimit; dqb1.dqb_curinodes = 0; dqb1.dqb_btime = dqb->dqb_btime; dqb1.dqb_itime = dqb->dqb_itime; ret = quotactl (QCMD(Q_V1_SETQLIM, (isgrp ? GRPQUOTA : USRQUOTA)), dev, uid, (caddr_t) &dqb1); } return ret; } /* ** Wrapper for the quotactl(SYNC) call. */ int linuxquota_sync( const char * dev, int isgrp ) { int ret; if (kernel_iface == IFACE_UNSET) linuxquota_get_api(); if (kernel_iface == IFACE_GENERIC) { ret = quotactl (QCMD(Q_V3_SYNC, (isgrp ? GRPQUOTA : USRQUOTA)), dev, 0, NULL); } else if (kernel_iface == IFACE_VFSV0) { ret = quotactl (QCMD(Q_V2_SYNC, (isgrp ? GRPQUOTA : USRQUOTA)), dev, 0, NULL); } else /* if (kernel_iface == IFACE_VFSOLD) */ { ret = quotactl (QCMD(Q_V1_SYNC, (isgrp ? GRPQUOTA : USRQUOTA)), dev, 0, NULL); } return ret; } #if 0 #define DEVICE_PATH "/dev/hda6" main() { struct dqblk dqb; linuxquota_get_api(); printf("API=%d\n", kernel_iface); if (linuxquota_sync(DEVICE_PATH, FALSE) != 0) perror("Q_SYNC"); if (linuxquota_query(DEVICE_PATH, getuid(), 0, &dqb) == 0) { printf("blocks: usage %d soft %d hard %d expire %s", dqb.dqb_curblocks, dqb.dqb_bhardlimit, dqb.dqb_bsoftlimit, ((dqb.dqb_btime != 0) ? (char*)ctime(&dqb.dqb_btime) : "n/a\n")); printf("inodes: usage %d soft %d hard %d expire %s", dqb.dqb_curinodes, dqb.dqb_ihardlimit, dqb.dqb_isoftlimit, ((dqb.dqb_itime != 0) ? (char*)ctime(&dqb.dqb_itime) : "n/a\n")); } else perror("Q_GETQUOTA"); } #endif python-fsquota-0.1.0+dfsg1/src/quotaio_xfs.h000066400000000000000000000214301400261757100210150ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1+ WITH Linux-syscall-note */ /* * Copyright (c) 1995-2001,2004 Silicon Graphics, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesset General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _QUOTAIO_XFS_H #define _QUOTAIO_XFS_H #include /* * Disk quota - quotactl(2) commands for the XFS Quota Manager (XQM). */ #define XQM_CMD(x) (('X'<<8)+(x)) /* note: forms first QCMD argument */ #define XQM_COMMAND(x) (((x) & (0xff<<8)) == ('X'<<8)) /* test if for XFS */ #define XQM_USRQUOTA 0 /* system call user quota type */ #define XQM_GRPQUOTA 1 /* system call group quota type */ #define XQM_PRJQUOTA 2 /* system call project quota type */ #define XQM_MAXQUOTAS 3 #define Q_XQUOTAON XQM_CMD(1) /* enable accounting/enforcement */ #define Q_XQUOTAOFF XQM_CMD(2) /* disable accounting/enforcement */ #define Q_XGETQUOTA XQM_CMD(3) /* get disk limits and usage */ #define Q_XSETQLIM XQM_CMD(4) /* set disk limits */ #define Q_XGETQSTAT XQM_CMD(5) /* get quota subsystem status */ #define Q_XQUOTARM XQM_CMD(6) /* free disk space used by dquots */ #define Q_XQUOTASYNC XQM_CMD(7) /* delalloc flush, updates dquots */ #define Q_XGETQSTATV XQM_CMD(8) /* newer version of get quota */ #define Q_XGETNEXTQUOTA XQM_CMD(9) /* get disk limits and usage >= ID */ /* * fs_disk_quota structure: * * This contains the current quota information regarding a user/proj/group. * It is 64-bit aligned, and all the blk units are in BBs (Basic Blocks) of * 512 bytes. */ #define FS_DQUOT_VERSION 1 /* fs_disk_quota.d_version */ typedef struct fs_disk_quota { __s8 d_version; /* version of this structure */ __s8 d_flags; /* FS_{USER,PROJ,GROUP}_QUOTA */ __u16 d_fieldmask; /* field specifier */ __u32 d_id; /* user, project, or group ID */ __u64 d_blk_hardlimit;/* absolute limit on disk blks */ __u64 d_blk_softlimit;/* preferred limit on disk blks */ __u64 d_ino_hardlimit;/* maximum # allocated inodes */ __u64 d_ino_softlimit;/* preferred inode limit */ __u64 d_bcount; /* # disk blocks owned by the user */ __u64 d_icount; /* # inodes owned by the user */ __s32 d_itimer; /* zero if within inode limits */ /* if not, we refuse service */ __s32 d_btimer; /* similar to above; for disk blocks */ __u16 d_iwarns; /* # warnings issued wrt num inodes */ __u16 d_bwarns; /* # warnings issued wrt disk blocks */ __s32 d_padding2; /* padding2 - for future use */ __u64 d_rtb_hardlimit;/* absolute limit on realtime blks */ __u64 d_rtb_softlimit;/* preferred limit on RT disk blks */ __u64 d_rtbcount; /* # realtime blocks owned */ __s32 d_rtbtimer; /* similar to above; for RT disk blks */ __u16 d_rtbwarns; /* # warnings issued wrt RT disk blks */ __s16 d_padding3; /* padding3 - for future use */ char d_padding4[8]; /* yet more padding */ } fs_disk_quota_t; /* * These fields are sent to Q_XSETQLIM to specify fields that need to change. */ #define FS_DQ_ISOFT (1<<0) #define FS_DQ_IHARD (1<<1) #define FS_DQ_BSOFT (1<<2) #define FS_DQ_BHARD (1<<3) #define FS_DQ_RTBSOFT (1<<4) #define FS_DQ_RTBHARD (1<<5) #define FS_DQ_LIMIT_MASK (FS_DQ_ISOFT | FS_DQ_IHARD | FS_DQ_BSOFT | \ FS_DQ_BHARD | FS_DQ_RTBSOFT | FS_DQ_RTBHARD) /* * These timers can only be set in super user's dquot. For others, timers are * automatically started and stopped. Superusers timer values set the limits * for the rest. In case these values are zero, the DQ_{F,B}TIMELIMIT values * defined below are used. * These values also apply only to the d_fieldmask field for Q_XSETQLIM. */ #define FS_DQ_BTIMER (1<<6) #define FS_DQ_ITIMER (1<<7) #define FS_DQ_RTBTIMER (1<<8) #define FS_DQ_TIMER_MASK (FS_DQ_BTIMER | FS_DQ_ITIMER | FS_DQ_RTBTIMER) /* * Warning counts are set in both super user's dquot and others. For others, * warnings are set/cleared by the administrators (or automatically by going * below the soft limit). Superusers warning values set the warning limits * for the rest. In case these values are zero, the DQ_{F,B}WARNLIMIT values * defined below are used. * These values also apply only to the d_fieldmask field for Q_XSETQLIM. */ #define FS_DQ_BWARNS (1<<9) #define FS_DQ_IWARNS (1<<10) #define FS_DQ_RTBWARNS (1<<11) #define FS_DQ_WARNS_MASK (FS_DQ_BWARNS | FS_DQ_IWARNS | FS_DQ_RTBWARNS) /* * Accounting values. These can only be set for filesystem with * non-transactional quotas that require quotacheck(8) in userspace. */ #define FS_DQ_BCOUNT (1<<12) #define FS_DQ_ICOUNT (1<<13) #define FS_DQ_RTBCOUNT (1<<14) #define FS_DQ_ACCT_MASK (FS_DQ_BCOUNT | FS_DQ_ICOUNT | FS_DQ_RTBCOUNT) /* * Various flags related to quotactl(2). */ #define FS_QUOTA_UDQ_ACCT (1<<0) /* user quota accounting */ #define FS_QUOTA_UDQ_ENFD (1<<1) /* user quota limits enforcement */ #define FS_QUOTA_GDQ_ACCT (1<<2) /* group quota accounting */ #define FS_QUOTA_GDQ_ENFD (1<<3) /* group quota limits enforcement */ #define FS_QUOTA_PDQ_ACCT (1<<4) /* project quota accounting */ #define FS_QUOTA_PDQ_ENFD (1<<5) /* project quota limits enforcement */ #define XFS_USER_QUOTA (1<<0) /* user quota type */ #define XFS_PROJ_QUOTA (1<<1) /* project quota type */ #define XFS_GROUP_QUOTA (1<<2) /* group quota type */ /* * fs_quota_stat is the struct returned in Q_XGETQSTAT for a given file system. * Provides a centralized way to get meta information about the quota subsystem. * eg. space taken up for user and group quotas, number of dquots currently * incore. */ #define FS_QSTAT_VERSION 1 /* fs_quota_stat.qs_version */ /* * Some basic information about 'quota files'. */ typedef struct fs_qfilestat { __u64 qfs_ino; /* inode number */ __u64 qfs_nblks; /* number of BBs 512-byte-blks */ __u32 qfs_nextents; /* number of extents */ } fs_qfilestat_t; typedef struct fs_quota_stat { __s8 qs_version; /* version number for future changes */ __u16 qs_flags; /* FS_QUOTA_{U,P,G}DQ_{ACCT,ENFD} */ __s8 qs_pad; /* unused */ fs_qfilestat_t qs_uquota; /* user quota storage information */ fs_qfilestat_t qs_gquota; /* group quota storage information */ __u32 qs_incoredqs; /* number of dquots incore */ __s32 qs_btimelimit; /* limit for blks timer */ __s32 qs_itimelimit; /* limit for inodes timer */ __s32 qs_rtbtimelimit;/* limit for rt blks timer */ __u16 qs_bwarnlimit; /* limit for num warnings */ __u16 qs_iwarnlimit; /* limit for num warnings */ } fs_quota_stat_t; /* * fs_quota_statv is used by Q_XGETQSTATV for a given file system. It provides * a centralized way to get meta information about the quota subsystem. eg. * space taken up for user, group, and project quotas, number of dquots * currently incore. * * This version has proper versioning support with appropriate padding for * future expansions, and ability to expand for future without creating any * backward compatibility issues. * * Q_XGETQSTATV uses the passed in value of the requested version via * fs_quota_statv.qs_version to determine the return data layout of * fs_quota_statv. The kernel will fill the data fields relevant to that * version. * * If kernel does not support user space caller specified version, EINVAL will * be returned. User space caller can then reduce the version number and retry * the same command. */ #define FS_QSTATV_VERSION1 1 /* fs_quota_statv.qs_version */ /* * Some basic information about 'quota files' for Q_XGETQSTATV command */ struct fs_qfilestatv { __u64 qfs_ino; /* inode number */ __u64 qfs_nblks; /* number of BBs 512-byte-blks */ __u32 qfs_nextents; /* number of extents */ __u32 qfs_pad; /* pad for 8-byte alignment */ }; struct fs_quota_statv { __s8 qs_version; /* version for future changes */ __u8 qs_pad1; /* pad for 16bit alignment */ __u16 qs_flags; /* FS_QUOTA_.* flags */ __u32 qs_incoredqs; /* number of dquots incore */ struct fs_qfilestatv qs_uquota; /* user quota information */ struct fs_qfilestatv qs_gquota; /* group quota information */ struct fs_qfilestatv qs_pquota; /* project quota information */ __s32 qs_btimelimit; /* limit for blks timer */ __s32 qs_itimelimit; /* limit for inodes timer */ __s32 qs_rtbtimelimit;/* limit for rt blks timer */ __u16 qs_bwarnlimit; /* limit for num warnings */ __u16 qs_iwarnlimit; /* limit for num warnings */ __u64 qs_pad2[8]; /* for future proofing */ }; #endif /* _QUOTAIO_XFS_H */ python-fsquota-0.1.0+dfsg1/src/rquota.h000066400000000000000000000034231400261757100177710ustar00rootroot00000000000000 #ifndef _RQUOTA_H_RPCGEN #define _RQUOTA_H_RPCGEN #include #define RQ_PATHLEN 1024 struct getquota_args { char *gqa_pathp; int gqa_uid; }; typedef struct getquota_args getquota_args; struct ext_getquota_args { char *gqa_pathp; int gqa_type; int gqa_id; }; typedef struct ext_getquota_args ext_getquota_args; struct rquota { int rq_bsize; bool_t rq_active; u_int rq_bhardlimit; u_int rq_bsoftlimit; u_int rq_curblocks; u_int rq_fhardlimit; u_int rq_fsoftlimit; u_int rq_curfiles; u_int rq_btimeleft; u_int rq_ftimeleft; }; typedef struct rquota rquota; enum gqr_status { Q_OK = 1, Q_NOQUOTA = 2, Q_EPERM = 3 }; typedef enum gqr_status gqr_status; struct getquota_rslt { gqr_status status; union { rquota gqr_rquota; } getquota_rslt_u; }; typedef struct getquota_rslt getquota_rslt; #define RQUOTAPROG ((unsigned long)(100011)) #define RQUOTAVERS ((unsigned long)(1)) #define RQUOTAPROC_GETQUOTA ((unsigned long)(1)) extern getquota_rslt * rquotaproc_getquota_1(getquota_args *, CLIENT *); extern getquota_rslt * rquotaproc_getquota_1_svc(getquota_args *, struct svc_req *); #define RQUOTAPROC_GETACTIVEQUOTA ((unsigned long)(2)) extern getquota_rslt * rquotaproc_getactivequota_1(getquota_args *, CLIENT *); extern getquota_rslt * rquotaproc_getactivequota_1_svc(getquota_args *, struct svc_req *); extern int rquotaprog_1_freeresult(SVCXPRT *, xdrproc_t, caddr_t); /* the xdr functions */ extern bool_t xdr_getquota_args(XDR *, getquota_args*); extern bool_t xdr_rquota(XDR *, rquota*); extern bool_t xdr_gqr_status(XDR *, gqr_status*); extern bool_t xdr_getquota_rslt(XDR *, getquota_rslt*); #define EXT_RQUOTAVERS ((unsigned long)(2)) extern bool_t xdr_ext_getquota_args(XDR *, ext_getquota_args*); #endif /* !_RQUOTA_H_RPCGEN */ python-fsquota-0.1.0+dfsg1/tests/000077500000000000000000000000001400261757100166565ustar00rootroot00000000000000python-fsquota-0.1.0+dfsg1/tests/README.md000066400000000000000000000014141400261757100201350ustar00rootroot00000000000000# Test scripts for the Python Quota module This directory contains a number of tests that allow exercising most of the functionality provided by the FsQuota module. However these are not unit-tests per-se, because: * the module functionality depends entirely on the environment (i.e. which file-systems are present, is quota even enabled on any of these, which users/groups do have quota limits set etc.) - so we cannot determine which results are correct; * a large portion of the interface can only be use in a meaningful way when run by a user with admin capabilities. Therefore, the provided tests are either interactive - i.e. ask you for paths and user IDs at run-time, or contain configuration variables that need to be edited by you before running the test. python-fsquota-0.1.0+dfsg1/tests/test_64bit.py000066400000000000000000000031531400261757100212210ustar00rootroot00000000000000#!/usr/bin/python3 # # Testing reading and setting 64-bit quota limits. # This is supported by UFS on FreeBSD # # Author: T. Zoerner # # This program is in the public domain and can be used and # redistributed without restrictions. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. import sys import FsQuota # set random value beyond 32-bit max. new_bs = 0x150000000 new_bh = 0x160000000 uid = 32000 path = "." dogrp = False typnam = "GID" if dogrp else "UID" try: # Create quota access object for the given path qObj = FsQuota.Quota(path) print("Using device \"%s\"" % qObj.dev) # Get quota for user try: (bc, bs, bh, bt, fc, fs, fh, ft) = qObj.query(uid, grpquota=dogrp) print("CUR %s:%d - %d(0x%X) - %d(0x%X) - %d(0x%X) - %d" % (typnam, uid, bc,bc, bs,bs, bh,bh, bt)) except FsQuota.error as e: print("CUR %s:%d no quota yet (error %s)" % (typnam, uid, e)) print("SET %s:%d bsoft=%d(0x%X), bhard=%d(0x%X)" % (typnam, uid, new_bs,new_bs, new_bh,new_bh)) qObj.setqlim(uid, bsoft=new_bs, bhard=new_bh, grpquota=dogrp) print("SET successfully - now reading back") (bc, bs, bh, bt, fc, fs, fh, ft) = qObj.query(uid, grpquota=dogrp) print("NEW %s:%d - bsoft:%d(0x%X) bhard:%d(0x%X) (btime:%d)" % (typnam, uid, bs,bs, bh,bh, bt)) print("OK - values match" if (bs == new_bs) and (bh==new_bh) else "ERROR - not matching") except FsQuota.error as e: print("ERROR using %s:%d: %s" % (typnam, uid, e), file=sys.stderr) python-fsquota-0.1.0+dfsg1/tests/test_ALL_interactively.py000066400000000000000000000175531400261757100236540ustar00rootroot00000000000000#!/usr/bin/python3 # ---------------------------------------------------------------------------- # Interactive test and demo script for the Python FsQuota extension module # ---------------------------------------------------------------------------- # Author: T. Zoerner 1995-2020 # # This program (test.py) is in the public domain and can be used and # redistributed without restrictions. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # ---------------------------------------------------------------------------- import os import sys import errno import time import re import FsQuota # # Exit immediately when input/output is not a terminal # as this script is interactive and cannot be run in automated testing # if not sys.stdin.isatty() or not sys.stdout.isatty(): print("\nThis is an interactive test script - input and output must be a tty\nExiting now.\n", file=sys.stderr) sys.exit(0) # # Helper function for printing quota query results # def fmt_quota_vals(qtup): if qtup.btime: tm = time.localtime(qtup.btime) bt_str = ("%04d-%02d-%02d/%02d:%02d" % (tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min)) else: bt_str = "0" if qtup.itime: tm = time.localtime(qtup.itime) ft_str = ("%04d-%02d-%02d/%02d:%02d" % (tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min)) else: ft_str = "0" return ("%d (%d,%d,%s) %d (%d,%d,%s)" % (qtup.bcount, qtup.bsoft, qtup.bhard, bt_str, qtup.icount, qtup.isoft, qtup.ihard, ft_str)) # # Ask user for choosing user or group quotas # is_group = False while True: is_group = input("\nQuery user [u] or group [g] quota? (default: user)? ") match = re.match(r"^([ug]?)$", is_group) if match: is_group = (match.group(1) == "g") break print("invalid response (not 'u' or 'g'), please try again", file=sys.stderr) n_uid_gid = "GID" if is_group else "UID" # # Ask user for a path to a file system with quotas # qObj = None while True: path = input("\nEnter path to get quota for (NFS possible; default '.'): ") if path == "": path = "." while qObj is None: try: qObj = FsQuota.Quota(path) except FsQuota.error as e: print("%s: %s" % (path, e), file=sys.stderr) if os.path.isdir(path) and not path.endswith("/."): # # try to append "/." to get past automounter fs # path += "/." print("Trying %s instead..." % path) # continue loop else: break if qObj is None: continue print("Using device \"%s\"" % qObj.dev) ## ## Check if quotas are present on this filesystem ## if qObj.is_nfs: print("Is a remote file system") break else: try: qObj.sync() print("Quotas are present on this filesystem (sync ok)") break except FsQuota.error as e: if (e.errno is not errno.EPERM): # ignore EPERM print("FsQuota.sync failed: %s" % e, file=sys.stderr) print("Choose another file system - quotas not functional on this one", file=sys.stderr) else: break ## ## Test quota query for current user (should always succeed) ## uid_val = os.getgid() if is_group else os.getuid() print("\nQuerying this fs with process (real) %s %d" % (n_uid_gid, uid_val)) try: qtup = qObj.query(uid_val, grpquota=is_group) print("Your usage and limits are %s" % fmt_quota_vals(qtup)) except FsQuota.error as e: print("FsQuota.query(%s) failed: %s" % (qObj.dev, e), file=sys.stderr) ## ## Test quota query for another UID/GID (only succeeds with admin capability) ## while True: uid_val = input("\nEnter a different %s to query quota for: " % n_uid_gid) try: uid_val = int(uid_val) break except: print("You have to enter a decimal 32-bit value here.") try: qtup = qObj.query(uid_val, grpquota=is_group) print("Usage and limits for %s %d are %s" % (n_uid_gid, uid_val, fmt_quota_vals(qtup))) except FsQuota.error as e: print("FsQuota.query(%s,%d,%d) failed: %s" % (qObj.dev, uid_val, is_group, e), file=sys.stderr) ## ## Test querying quota via forced RPC ## remhost = 'localhost'; if qObj.is_nfs: # path is already mounted via NFS: get server-side mount point to avoid recursion match = re.match(r"^([^:]+):(/.*)$", qObj.dev) if match: remhost = match.group(1) path = match.group(2) else: # should never happen path = "/" else: path = os.path.abspath(path) print("\nEnter host:path for querying via forced RPC (default \"%s:%s\")" % (remhost, path)) while True: hap = input("Enter host:path, empty for default, or \":\" to skip: ") if not hap: # accept default break if hap == ":" or hap == ".": # skip remhost = "" break match = re.match(r"^([^:]+):(/.*)$", hap) if match: remhost = match.group(1) path = match.group(2) break else: print("Invalid input: not in format \"host:path\"") if remhost: qObj = FsQuota.Quota(path, rpc_host=remhost) try: qtup = qObj.query(os.getuid(), grpquota=is_group) print("Your usage and limits are %s" % fmt_quota_vals(qtup)) except FsQuota.error as e: print("RPC query failed: %s" % e) print("\nQuerying %s %d from %s:%s via RPC." % (n_uid_gid, uid_val, remhost, path)) try: qtup = qObj.query(uid_val, grpquota=is_group) print("Usage and limits for %s %d are %s" % (n_uid_gid, uid_val, fmt_quota_vals(qtup))) except FsQuota.error as e: print("Failed to query via RPC: %s" % e) print("Retrying with fake authentication for %s %d." % (n_uid_gid, uid_val)) qObj.rpc_opt(auth_uid=uid_val, rpc_use_tcp=1) try: qtup = qObj.query(uid_val, grpquota=is_group) print("Usage and limits for %s %d are %s" % (n_uid_gid, uid_val, fmt_quota_vals(qtup))) except FsQuota.error as e: print("Failed to query RPC again: %s" % e) qObj.rpc_opt(auth_uid=-1, auth_gid=-1) # reset to default ## ## Test setting quota limits ## while True: path = input("\nEnter path to set quota (empty to skip): ") if path == "": break try: qObj = FsQuota.Quota(path) if qObj.is_nfs: print("Heads-up: Trying to set quota for remote path will fail") break except FsQuota.error as e: print("%s: mount point not found" % path, file=sys.stderr) if path: bs = None while True: line = input("Enter new quota limits bs,bh,fs,fh for %s %d (empty to abort): " % (n_uid_gid, uid_val)) match = re.match(r"^\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*$", line) if match: (bs,bh,fs,fh) = (int(match.group(1)), int(match.group(2)), int(match.group(3)), int(match.group(4))) break print("Invalid parameters: expect 4 comma-separated numerical values") if bs is not None: try: qObj.setqlim(uid_val, bs,bh,fs,fh, timereset=1, grpquota=is_group) print("Quota set successfully for %s %d" % (n_uid_gid, uid_val)) try: qtup = qObj.query(uid_val, grpquota=is_group) print("Read-back modified limits: %s" % fmt_quota_vals(qtup)) except FsQuota.error as e: print("Failed to read back changed quota limits: %s" % e) except FsQuota.error as e: print("Failed to set quota: %s" % e, file=sys.stderr) ## ## Test quota sync to disk ## if not qObj.is_nfs: try: qObj.sync() except FsQuota.error as e: print("FsQuota.sync failed: %s" % e, file=sys.stderr) python-fsquota-0.1.0+dfsg1/tests/test_ALL_smoke.py000066400000000000000000000051701400261757100221000ustar00rootroot00000000000000#!/usr/bin/python3 # # Smoke-test for automated testing: # - iterate across mount table # - for each entry try creating a Quota class instance # - when successful, try sync and query UID twice, GID once # - note setqlim is omitted intentionally (usually will fail as no sane # automation would run as root, but if so quotas would be corrupted) # - test may fail only upon crash or mismatch in repeated UID query; # cannot verify failures or query results otherwise # - tester should manually compare output with that of "quota -v" # # Author: T. Zoerner # # This program is in the public domain and can be used and # redistributed without restrictions. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. import os import sys from stat import * import subprocess import FsQuota my_uid = os.getuid() my_gid = os.getgid() print("OS: "); subprocess.call(['uname', '-rs']); print("------------------------------------------------------------------\n" + "Output of quota -v:") subprocess.call(['quota', '-v']); print("------------------------------------------------------------------\n" + "Output of quota -v -g %s:" % my_gid) subprocess.call(['quota', '-v', '-g', str(my_gid)]); print("------------------------------------------------------------------") for fsname, path, fstyp, opt in FsQuota.MntTab(): print("\n%s:\n- fsname/typ: %s, %s\n- options: %s" % (path, fsname, fstyp, opt)) try: qObj = FsQuota.Quota(path) print("- Quota.dev: " + qObj.dev) try: qObj.sync(); print("- Quota.sync: OK") except FsQuota.error as e: print("- Quota.sync failed: %s" % e) try: qtup = qObj.query(my_uid) print("- Quota.query default (EUID): " + str(qtup)) qtup2 = qObj.query(my_uid); try: print("- Quota.query UID %d: %s" % (my_uid, str(qtup))) if not qtup == qtup2: print("ERROR: mismatching query results") exit(1) except FsQuota.error as e: print("- repeated Quota.query failed: %s" % e) exit(1) except FsQuota.error as e: print("- Quota.query failed: %s" % e) try: qtup = qObj.query(my_gid, grpquota=True) print("- Quota.query GID %d: %s" % (my_gid, str(qtup))) except FsQuota.error as e: print("- Quota.query GID %d failed: %s" % (my_gid, e)) except: print("- Quota::getqcarg: UNDEF") python-fsquota-0.1.0+dfsg1/tests/test_getqcarg.py000066400000000000000000000015331400261757100220660ustar00rootroot00000000000000#!/usr/bin/python3 # # Author: T. Zoerner # # This program is in the public domain and can be used and # redistributed without restrictions. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. import os import sys from stat import * import FsQuota try: for fsname, path, fstyp, opt in FsQuota.MntTab(): try: qObj = FsQuota.Quota(path) qcarg = qObj.dev except: qcarg = "*UNDEF*" try: st_dev = os.stat(path).st_dev except OSError: st_dev = -1 print("%s # %s # %s # %s # %d # %s #" % (fsname, path, fstyp, opt, st_dev, qcarg)) except FsQuota.error as e: print("ERROR: %s" % e, file=sys.stderr) python-fsquota-0.1.0+dfsg1/tests/test_group.py000066400000000000000000000042511400261757100214250ustar00rootroot00000000000000#!/usr/bin/python3 # # Author: T. Zoerner # # Testing group quota support (Apr/02/1999) # # This program is in the public domain and can be used and # redistributed without restrictions. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. import sys import time import FsQuota ## ## insert your test case constants here: ## path = "." ugid = 2001 dogrp = True setq = [123, 124, 50, 100] typnam = "GID" if dogrp else "UID" def fmt_quota_vals(qtup): if qtup.btime: tm = time.localtime(qtup.btime) bt_str = ("%04d-%02d-%02d/%02d:%02d" % (tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min)) else: bt_str = "0" if qtup.itime: tm = time.localtime(qtup.itime) ft_str = ("%04d-%02d-%02d/%02d:%02d" % (tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min)) else: ft_str = "0" return ("%d (%d,%d,%s) %d (%d,%d,%s)" % (qtup.bcount, qtup.bsoft, qtup.bhard, bt_str, qtup.icount, qtup.isoft, qtup.ihard, ft_str)) try: qObj = FsQuota.Quota(path) print("Using device \"%s\"" % qObj.dev) print("Checking quota sync (may fail if quotas not enabled)...") qObj.sync() try: print("Query quotas for %s %d" % (typnam, ugid)) qtup = qObj.query(ugid, grpquota=dogrp) print("Quota usage and limits for %s %d are %s" % (typnam, ugid, fmt_quota_vals(qtup))) except FsQuota.error as e: print("Query %s %d failed: %s" % (typnam, ugid, e), file=sys.stderr) ## ## set quota block & file limits for user ## print("Setting new quota limits...") qObj.setqlim(ugid, *setq, timereset=1, grpquota=dogrp) print("Quotas set successfully for %s %d" % (typnam, ugid)) print("Reading back new quota limits...") qtup = qObj.query(ugid, grpquota=dogrp) print("Quota usage and limits for %s %d are %s" % (typnam, ugid, fmt_quota_vals(qtup))) print("Finally checking quota sync again") qObj.sync() except FsQuota.error as e: print("ERROR using %s %d: %s" % (typnam, ugid, e), file=sys.stderr) python-fsquota-0.1.0+dfsg1/tests/test_mnttab.py000066400000000000000000000010541400261757100215540ustar00rootroot00000000000000#!/usr/bin/python3 # # Author: T. Zoerner # # This program is in the public domain and can be used and # redistributed without restrictions. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. import sys import FsQuota try: for fsname, path, fstyp, opt in FsQuota.MntTab(): print("# %s # %s # %s # %s #" % (fsname, path, fstyp, opt)) except FsQuota.error as e: print("ERROR: %s" % e, file=sys.stderr) python-fsquota-0.1.0+dfsg1/tests/test_rpc.py000066400000000000000000000113141400261757100210530ustar00rootroot00000000000000#!/usr/bin/python3 # # Author: T. Zoerner # # Testing RPC support # # This program is in the public domain and can be used and # redistributed without restrictions. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. import os import sys import time import FsQuota ## ## insert your test case constants here: ## path = "/mnt" remote_path = "/data/tmp/qtest" remote_host = "localhost" unused_port = 29875; # for RPC error testing unknown_host = "UNKNOWN_HOSTNAME"; # for RPC error testing dogrp = False ugid = os.getgid() if dogrp else os.getuid() other_ugid = 2001 if dogrp else 32000 # for permission test when not run by admin typnam = "GID" if dogrp else "UID" def fmt_quota_vals(qtup): if qtup.btime: tm = time.localtime(qtup.btime) bt_str = ("%04d-%02d-%02d/%02d:%02d" % (tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min)) else: bt_str = "0" if qtup.itime: tm = time.localtime(qtup.itime) ft_str = ("%04d-%02d-%02d/%02d:%02d" % (tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min)) else: ft_str = "0" return ("%d (%d,%d,%s) %d (%d,%d,%s)" % (qtup.bcount, qtup.bsoft, qtup.bhard, bt_str, qtup.icount, qtup.isoft, qtup.ihard, ft_str)) # ---------------------------------------------------------------------------- try: print(">>> stage 1: test locally mounted NFS fs: %s" % path) qObj = FsQuota.Quota(path) print("Using device \"%s\"" % qObj.dev) try: print("Query quotas for %s %d" % (typnam, ugid)) qtup = qObj.query(ugid, grpquota=dogrp) print("Quota usage and limits for %s %d are %s" % (typnam, ugid, fmt_quota_vals(qtup))) print(">>> stage 1b: Repeat with TCP") qObj.rpc_opt(rpc_use_tcp=True) qtup2 = qObj.query(ugid, grpquota=dogrp) if qtup != qtup2: print("ERROR - result not equal: %s" % fmt_quota_vals(qtup)) print(">>> stage 1c: Repeat with explicit authentication") qObj.rpc_opt(rpc_use_tcp=False, auth_uid=os.getuid(), auth_gid=os.getgid(), auth_hostname="localhost") qtup2 = qObj.query(ugid, grpquota=dogrp) if qtup != qtup2: print("ERROR - result not equal: %s" % fmt_quota_vals(qtup)) except FsQuota.error as e: print("Query %s %d failed: %s" % (typnam, ugid, e), file=sys.stderr) # ------------------------------------------------------------------------- try: print(">>> state 2: repeat with different %s %d" % (typnam, other_ugid)) qtup = qObj.query(other_ugid, grpquota=dogrp) print("Quota usage and limits for %s %d are %s" % (typnam, other_ugid, fmt_quota_vals(qtup))) except FsQuota.error as e: print("Query %s %d failed: %s" % (typnam, other_ugid, e), file=sys.stderr) try: print(">>> stage 2b: Same with fake authentication") auth_pat = {'auth_gid': other_ugid} if dogrp else {'auth_uid': other_ugid} qObj.rpc_opt(rpc_use_tcp=False, **auth_pat, auth_hostname="localhost") qtup = qObj.query(other_ugid, grpquota=dogrp) print("Quota usage and limits for %s %d are %s" % (typnam, other_ugid, fmt_quota_vals(qtup))) except FsQuota.error as e: print("Query %s %d failed: %s" % (typnam, other_ugid, e), file=sys.stderr) # ------------------------------------------------------------------------- print(">>> stage 3: force use of RPC to %s:%s" % (remote_host, remote_path)) qObj = FsQuota.Quota(remote_path, rpc_host=remote_host) print("Using device \"%s\"" % qObj.dev) try: print("Query quotas for %s %d" % (typnam, ugid)) qtup = qObj.query(ugid, grpquota=dogrp) print("Quota usage and limits for %s %d are %s" % (typnam, ugid, fmt_quota_vals(qtup))) except FsQuota.error as e: print("Query %s %d failed: %s" % (typnam, ugid, e), file=sys.stderr) # ------------------------------------------------------------------------- print(">>> stage 4: force use of inactive remote port") qObj.rpc_opt(rpc_port=unused_port, rpc_timeout=2000, rpc_use_tcp=True) try: qtup = qObj.query(ugid, grpquota=dogrp) except FsQuota.error as e: print("Query failed (expected): %s" % (e), file=sys.stderr) qObj.rpc_opt(rpc_port=0) print(">>> stage 4b: force use of unknown remote host") qObj = FsQuota.Quota(remote_path, rpc_host=unknown_host) try: qtup = qObj.query(ugid, grpquota=dogrp) except FsQuota.error as e: print("Query failed (expected): %s" % (e), file=sys.stderr) except FsQuota.error as e: print("ERROR instantiating: %s" % e, file=sys.stderr)