From: Nathan Scott Date: Tue, 19 Apr 2005 15:00:30 +0000 (+0000) Subject: Initial version of xfs quota utility. Knows how to do user/group/project quota,... X-Git-Tag: v2.7.0~34 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5aead01d223a5c4a2526d4cbe2e11bd1945959fe;p=thirdparty%2Fxfsprogs-dev.git Initial version of xfs quota utility. Knows how to do user/group/project quota, provides missing freespace reporting for realtime on Linux, and for project quotas. Allows all current and future XFS quota functionality to be exposed without complicating the standard quota tools. Merge of master-melb:xfs-cmds:22274a by kenmcd. --- diff --git a/quota/Makefile b/quota/Makefile new file mode 100644 index 000000000..894fe8ae7 --- /dev/null +++ b/quota/Makefile @@ -0,0 +1,66 @@ +# +# Copyright (c) 2005 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it would be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# +# Further, this software is distributed without any warranty that it is +# free of the rightful claim of any third person regarding infringement +# or the like. Any license provided herein, whether implied or +# otherwise, applies only to this software file. Patent licenses, if +# any, provided herein do not apply to combinations of this program with +# other software, or any other product whatsoever. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write the Free Software Foundation, Inc., 59 +# Temple Place - Suite 330, Boston MA 02111-1307, USA. +# +# Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, +# Mountain View, CA 94043, or: +# +# http://www.sgi.com +# +# For further information regarding this notice, see: +# +# http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ +# + +TOPDIR = .. +include $(TOPDIR)/include/builddefs + +LTCOMMAND = xfs_quota +HFILES = init.h quota.h +CFILES = init.c util.c \ + edit.c free.c path.c project.c quot.c quota.c report.c state.c + +CFILES += $(PKG_PLATFORM).c +PCFILES = darwin.c freebsd.c irix.c linux.c +LSRCFILES = $(shell echo $(PCFILES) | sed -e "s/$(PKG_PLATFORM).c//g") + +LLDLIBS = $(LIBXCMD) +LTDEPENDENCIES = $(LIBXCMD) +LLDFLAGS = -static + +ifeq ($(ENABLE_READLINE),yes) +LLDLIBS += $(LIBREADLINE) $(LIBTERMCAP) +CFLAGS += -DENABLE_READLINE +endif + +ifeq ($(ENABLE_EDITLINE),yes) +LLDLIBS += $(LIBEDITLINE) $(LIBTERMCAP) +CFLAGS += -DENABLE_EDITLINE +endif + +default: $(LTCOMMAND) + +include $(BUILDRULES) + +install: default + $(INSTALL) -m 755 -d $(PKG_BIN_DIR) + $(LTINSTALL) -m 755 $(LTCOMMAND) $(PKG_BIN_DIR) +install-dev: diff --git a/quota/darwin.c b/quota/darwin.c new file mode 100644 index 000000000..b1d6fe526 --- /dev/null +++ b/quota/darwin.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2005 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + */ + +#include "quota.h" +#include + +int +xfsquotactl( + int command, + const char *device, + uint type, + uint id, + void *addr) +{ + /* return quotactl(device, QCMD(command, type), id, addr); */ + errno = -ENOSYS; + return -1; +} diff --git a/quota/edit.c b/quota/edit.c new file mode 100644 index 000000000..5c52e354d --- /dev/null +++ b/quota/edit.c @@ -0,0 +1,765 @@ +/* + * Copyright (c) 2005 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + */ + +#include +#include +#include +#include +#include +#include "init.h" +#include "quota.h" + +static cmdinfo_t limit_cmd; +static cmdinfo_t restore_cmd; +static cmdinfo_t timer_cmd; +static cmdinfo_t warn_cmd; + +static void +limit_help(void) +{ + printf(_( +"\n" +" modify quota limits for the specified user\n" +"\n" +" Example:\n" +" 'limit bsoft=100m bhard=110m tanya\n" +"\n" +" Changes the soft and/or hard block limits, inode limits and/or realtime\n" +" block limits that are currently being used for the specified user, group,\n" +" or project. The filesystem identified by the current path is modified.\n" +" -d -- set the default values, used the first time a file is created\n" +" -g -- modify group quota limits\n" +" -p -- modify project quota limits\n" +" -u -- modify user quota limits\n" +" The block limit values can be specified with a units suffix - accepted\n" +" units are: k (kilobytes), m (megabytes), g (gigabytes), and t (terabytes).\n" +" The user/group/project can be specified either by name or by number.\n" +"\n")); +} + +static void +timer_help(void) +{ + printf(_( +"\n" +" modify quota enforcement timeout for the specified user\n" +"\n" +" Example:\n" +" 'timer -i 3days sam'\n" +" (soft inode limit timer for user 'sam' is changed to 3 days)\n" +"\n" +" Changes the timeout value associated with the block limits, inode limits\n" +" and/or realtime block limits for the specified user, group, or project.\n" +" As soon as a user consumes the amount of space or number of inodes set as\n" +" the soft limit, a timer is started. If the timer expires and the user is\n" +" still over the soft limit, the soft limit is enforced as the hard limit.\n" +" The default timeout is 7 days.\n" +" -d -- set the default values, used the first time a file is created\n" +" -g -- modify group quota timer\n" +" -p -- modify project quota timer\n" +" -u -- modify user quota timer\n" +" -b -- modify the blocks-used timer\n" +" -i -- modify the inodes-used timer\n" +" -r -- modify the blocks-used timer for the (optional) realtime subvolume\n" +" The timeout value is specified as a number of seconds, by default.\n" +" However, a suffix may be used to alternatively specify minutes (m),\n" +" hours (h), days (d), or weeks (w) - either the full word or the first\n" +" letter of the word can be used.\n" +" The user/group/project can be specified either by name or by number.\n" +"\n")); +} + +static void +warn_help(void) +{ + printf(_( +"\n" +" modify the number of quota warnings sent to the specified user\n" +"\n" +" Example:\n" +" 'warn 2 jimmy'\n" +" (tell the quota system that two warnings have been sent to user jimmy)\n" +"\n" +" Changes the warning count associated with the block limits, inode limits\n" +" and/or realtime block limits for the specified user, group, or project.\n" +" When a user has been warned the maximum number of times allowed, the soft\n" +" limit is enforced as the hard limit. It is intended as an alternative to\n" +" the timeout system, where the system administrator updates a count of the\n" +" number of warnings issued to people, and they are penalised if the warnings\n" +" are ignored.\n" +" -d -- set maximum warning count, which triggers soft limit enforcement\n" +" -g -- set group quota warning count\n" +" -p -- set project quota warning count\n" +" -u -- set user quota warning count\n" +" -b -- set the blocks-used warning count\n" +" -i -- set the inodes-used warning count\n" +" -r -- set the blocks-used warn count for the (optional) realtime subvolume\n" +" The user/group/project can be specified either by name or by number.\n" +"\n")); +} + +static void +set_limits( + __uint32_t id, + uint type, + uint mask, + char *dev, + __uint64_t *bsoft, + __uint64_t *bhard, + __uint64_t *isoft, + __uint64_t *ihard, + __uint64_t *rtbsoft, + __uint64_t *rtbhard) +{ + fs_disk_quota_t d; + + memset(&d, 0, sizeof(d)); + d.d_version = FS_DQUOT_VERSION; + d.d_id = id; + d.d_flags = type; + d.d_fieldmask = mask; + d.d_blk_hardlimit = *bhard; + d.d_blk_softlimit = *bsoft; + d.d_ino_hardlimit = *ihard; + d.d_ino_softlimit = *isoft; + d.d_rtb_hardlimit = *rtbhard; + d.d_rtb_softlimit = *rtbsoft; + + if (xfsquotactl(XFS_SETQLIM, dev, type, id, (void *)&d) < 0) + fprintf(stderr, _("%s: cannot set limits: %s\n"), + progname, strerror(errno)); +} + +static void +set_user_limits( + char *name, + uint type, + uint mask, + __uint64_t *bsoft, + __uint64_t *bhard, + __uint64_t *isoft, + __uint64_t *ihard, + __uint64_t *rtbsoft, + __uint64_t *rtbhard) +{ + uid_t uid = uid_from_string(name); + + if (uid == -1) + fprintf(stderr, _("%s: invalid user name: %s\n"), + progname, name); + else + set_limits(uid, type, mask, fs_path->fs_name, + bsoft, bhard, isoft, ihard, rtbsoft, rtbhard); +} + +static void +set_group_limits( + char *name, + uint type, + uint mask, + __uint64_t *bsoft, + __uint64_t *bhard, + __uint64_t *isoft, + __uint64_t *ihard, + __uint64_t *rtbsoft, + __uint64_t *rtbhard) +{ + gid_t gid = gid_from_string(name); + + if (gid == -1) + fprintf(stderr, _("%s: invalid group name: %s\n"), + progname, name); + else + set_limits(gid, type, mask, fs_path->fs_name, + bsoft, bhard, isoft, ihard, rtbsoft, rtbhard); +} + +static void +set_project_limits( + char *name, + uint type, + uint mask, + __uint64_t *bsoft, + __uint64_t *bhard, + __uint64_t *isoft, + __uint64_t *ihard, + __uint64_t *rtbsoft, + __uint64_t *rtbhard) +{ + prid_t prid = prid_from_string(name); + + if (prid == -1) + fprintf(stderr, _("%s: invalid project name: %s\n"), + progname, name); + else + set_limits(prid, type, mask, fs_path->fs_name, + bsoft, bhard, isoft, ihard, rtbsoft, rtbhard); +} + +static int +extract( + char *string, + const char *prefix, + int length, + uint blocksize, + uint sectorsize, + __uint64_t *value) +{ + __uint64_t v; + char *s = string; + + if (strncmp(string, prefix, length) == 0) { + s = string + length + 1; + v = (__uint64_t)cvtnum(blocksize, sectorsize, s); + *value = v >> 9; /* syscalls use basic blocks */ + return 1; + } + return 0; +} + +static int +limit_f( + int argc, + char **argv) +{ + char *name; + __uint64_t bsoft, bhard, isoft, ihard, rtbsoft, rtbhard; + int c, type = 0, mask = 0, flags = 0; + uint bsize, ssize, endoptions; + + init_cvtnum(&bsize, &ssize); + bsoft = bhard = isoft = ihard = rtbsoft = rtbhard = 0; + while ((c = getopt(argc, argv, "dgpu")) != EOF) { + switch (c) { + case 'd': + flags |= DEFAULTS_FLAG; + break; + case 'g': + type = XFS_GROUP_QUOTA; + break; + case 'p': + type = XFS_PROJ_QUOTA; + break; + case 'u': + type = XFS_USER_QUOTA; + break; + default: + return command_usage(&limit_cmd); + } + } + + /* + * In the usual case, we need at least 2 more arguments - + * one (or more) limits and a user name/id. + * For setting defaults (-d) we don't want a user name/id. + */ + if (flags & DEFAULTS_FLAG) { + if (argc < optind + 1) + return command_usage(&limit_cmd); + endoptions = 1; + } else if (argc < optind + 2) { + return command_usage(&limit_cmd); + } else { + endoptions = 2; + } + + /* + * Extract limit values from remaining optional arguments. + */ + while (argc > optind + endoptions - 1) { + char *s = argv[optind++]; + if (extract(s, "bsoft=", 5, bsize, ssize, &bsoft)) + mask |= FS_DQ_BSOFT; + else if (extract(s, "bhard=", 5, bsize, ssize, &bhard)) + mask |= FS_DQ_BHARD; + else if (extract(s, "isoft=", 5, bsize, ssize, &isoft)) + mask |= FS_DQ_ISOFT; + else if (extract(s, "ihard=", 5, bsize, ssize, &ihard)) + mask |= FS_DQ_IHARD; + else if (extract(s, "rtbsoft=", 7, bsize, ssize, &rtbsoft)) + mask |= FS_DQ_RTBSOFT; + else if (extract(s, "rtbhard=", 7, bsize, ssize, &rtbhard)) + mask |= FS_DQ_RTBHARD; + else { + fprintf(stderr, _("%s: unrecognised argument %s\n"), + progname, s); + return 0; + } + } + if (!mask) { + fprintf(stderr, _("%s: cannot find any valid arguments\n"), + progname); + return 0; + } + + name = (flags & DEFAULTS_FLAG) ? "0" : argv[optind++]; + + if (!type) + type = XFS_USER_QUOTA; + + switch (type) { + case XFS_USER_QUOTA: + set_user_limits(name, type, mask, + &bsoft, &bhard, &isoft, &ihard, &rtbsoft, &rtbhard); + break; + case XFS_GROUP_QUOTA: + set_group_limits(name, type, mask, + &bsoft, &bhard, &isoft, &ihard, &rtbsoft, &rtbhard); + break; + case XFS_PROJ_QUOTA: + set_project_limits(name, type, mask, + &bsoft, &bhard, &isoft, &ihard, &rtbsoft, &rtbhard); + break; + } + return 0; +} + +/* + * Iterate through input file, restoring the limits. + * File format is as follows: + * fs = + * bsoft bhard isoft ihard [rtbsoft rtbhard] + */ +static void +restore_file( + FILE *fp, + uint type) +{ + char buffer[512]; + char devbuffer[512]; + char *dev = NULL; + uint mask; + int cnt; + __uint32_t id; + __uint64_t bsoft, bhard, isoft, ihard, rtbsoft, rtbhard; + + while (fgets(buffer, sizeof(buffer), fp) != NULL) { + if (strncmp("fs = ", buffer, 5) == 0) { + dev = strncpy(devbuffer, buffer+5, sizeof(devbuffer)); + dev[strlen(dev) - 1] = '\0'; + continue; + } + rtbsoft = rtbhard = 0; + cnt = sscanf(buffer, "%u %llu %llu %llu %llu %llu %llu\n", + &id, + &bsoft, &bhard, + &isoft, &ihard, + &rtbsoft, &rtbhard); + if (cnt == 5 || cnt == 7) { + mask = FS_DQ_ISOFT|FS_DQ_IHARD|FS_DQ_BSOFT|FS_DQ_BHARD; + if (cnt == 7) + mask |= FS_DQ_RTBSOFT|FS_DQ_RTBHARD; + set_limits(id, type, mask, dev, &bsoft, &bhard, + &isoft, &ihard, &rtbsoft, &rtbhard); + } + } +} + +static int +restore_f( + int argc, + char **argv) +{ + FILE *fp = stdin; + char *fname = NULL; + int c, type = 0; + + while ((c = getopt(argc, argv, "f:gpu")) != EOF) { + switch (c) { + case 'f': + fname = optarg; + break; + case 'g': + type = XFS_GROUP_QUOTA; + break; + case 'p': + type = XFS_PROJ_QUOTA; + break; + case 'u': + type = XFS_USER_QUOTA; + break; + default: + return command_usage(&restore_cmd); + } + } + + if (argc < optind) + return command_usage(&restore_cmd); + + if (!type) + type = XFS_USER_QUOTA; + + if (fname) { + if ((fp = fopen(fname, "r")) == NULL) { + fprintf(stderr, _("%s: fopen on %s failed: %s\n"), + progname, fname, strerror(errno)); + return 0; + } + } + + restore_file(fp, type); + + if (fname) + fclose(fp); + return 0; +} + +static void +set_timer( + __uint32_t id, + uint type, + uint mask, + char *dev, + uint value) +{ + fs_disk_quota_t d; + + memset(&d, 0, sizeof(d)); + d.d_version = FS_DQUOT_VERSION; + d.d_id = id; + d.d_flags = type; + d.d_fieldmask = mask; + d.d_itimer = value; + d.d_btimer = value; + d.d_rtbtimer = value; + + if (xfsquotactl(XFS_SETQLIM, dev, type, id, (void *)&d) < 0) + fprintf(stderr, _("%s: cannot set timer: %s\n"), + progname, strerror(errno)); +} + +static void +set_user_timer( + char *name, + uint type, + uint mask, + uint value) +{ + uid_t uid = uid_from_string(name); + + if (uid == -1) + fprintf(stderr, _("%s: invalid user name: %s\n"), + progname, name); + else + set_timer(uid, type, mask, fs_path->fs_name, value); +} + +static void +set_group_timer( + char *name, + uint type, + uint mask, + uint value) +{ + gid_t gid = gid_from_string(name); + + if (gid == -1) + fprintf(stderr, _("%s: invalid group name: %s\n"), + progname, name); + else + set_timer(gid, type, mask, fs_path->fs_name, value); +} + +static void +set_project_timer( + char *name, + uint type, + uint mask, + uint value) +{ + prid_t prid = prid_from_string(name); + + if (prid == -1) + fprintf(stderr, _("%s: invalid project name: %s\n"), + progname, name); + else + set_timer(prid, type, mask, fs_path->fs_name, value); +} + +static int +timer_f( + int argc, + char **argv) +{ + char *name; + uint value; + int c, flags = 0, type = 0, mask = 0; + + while ((c = getopt(argc, argv, "bdgipru")) != EOF) { + switch (c) { + case 'd': + flags |= DEFAULTS_FLAG; + break; + case 'b': + mask |= FS_DQ_BTIMER; + break; + case 'i': + mask |= FS_DQ_ITIMER; + break; + case 'r': + mask |= FS_DQ_RTBTIMER; + break; + case 'g': + type = XFS_GROUP_QUOTA; + break; + case 'p': + type = XFS_PROJ_QUOTA; + break; + case 'u': + type = XFS_USER_QUOTA; + break; + default: + return command_usage(&timer_cmd); + } + } + + /* + * In the usual case, we need at least 2 more arguments - + * one (or more) value and a user name/id. + * For setting defaults (-d) we don't want a user name/id. + */ + if (flags & DEFAULTS_FLAG) { + if (argc != optind + 1) + return command_usage(&timer_cmd); + } else if (argc != optind + 2) { + return command_usage(&timer_cmd); + } + + value = cvttime(argv[optind++]); + name = (flags & DEFAULTS_FLAG) ? "0" : argv[optind++]; + + if (!mask) + mask = FS_DQ_TIMER_MASK; + + if (!type) + type = XFS_USER_QUOTA; + + switch (type) { + case XFS_USER_QUOTA: + set_user_timer(name, type, mask, value); + break; + case XFS_GROUP_QUOTA: + set_group_timer(name, type, mask, value); + break; + case XFS_PROJ_QUOTA: + set_project_timer(name, type, mask, value); + break; + } + return 0; +} + +static void +set_warnings( + __uint32_t id, + uint type, + uint mask, + char *dev, + uint value) +{ + fs_disk_quota_t d; + + memset(&d, 0, sizeof(d)); + d.d_version = FS_DQUOT_VERSION; + d.d_id = id; + d.d_flags = type; + d.d_fieldmask = mask; + d.d_iwarns = value; + d.d_bwarns = value; + d.d_rtbwarns = value; + + if (xfsquotactl(XFS_SETQLIM, dev, type, id, (void *)&d) < 0) + fprintf(stderr, _("%s: cannot set warnings: %s\n"), + progname, strerror(errno)); +} + +static void +set_user_warnings( + char *name, + uint type, + uint mask, + uint value) +{ + uid_t uid = uid_from_string(name); + + if (uid == -1) + fprintf(stderr, _("%s: invalid user name: %s\n"), + progname, name); + else + set_warnings(uid, type, mask, fs_path->fs_name, value); +} + +static void +set_group_warnings( + char *name, + uint type, + uint mask, + uint value) +{ + gid_t gid = gid_from_string(name); + + if (gid == -1) + fprintf(stderr, _("%s: invalid group name: %s\n"), + progname, name); + else + set_warnings(gid, type, mask, fs_path->fs_name, value); +} + +static void +set_project_warnings( + char *name, + uint type, + uint mask, + uint value) +{ + prid_t prid = prid_from_string(name); + + if (prid == -1) + fprintf(stderr, _("%s: invalid project name: %s\n"), + progname, name); + else + set_warnings(prid, type, mask, fs_path->fs_name, value); +} + +static int +warn_f( + int argc, + char **argv) +{ + char *name; + uint value; + int c, flags = 0, type = 0, mask = 0; + + while ((c = getopt(argc, argv, "bdgipru")) != EOF) { + switch (c) { + case 'd': + flags |= DEFAULTS_FLAG; + break; + case 'b': + mask |= FS_DQ_BWARNS; + break; + case 'i': + mask |= FS_DQ_IWARNS; + break; + case 'r': + mask |= FS_DQ_RTBWARNS; + break; + case 'g': + type = XFS_GROUP_QUOTA; + break; + case 'p': + type = XFS_PROJ_QUOTA; + break; + case 'u': + type = XFS_USER_QUOTA; + break; + default: + return command_usage(&warn_cmd); + } + } + + /* + * In the usual case, we need at least 2 more arguments - + * one (or more) value and a user name/id. + * For setting defaults (-d) we don't want a user name/id. + */ + if (flags & DEFAULTS_FLAG) { + if (argc != optind + 1) + return command_usage(&warn_cmd); + } else if (argc != optind + 2) { + return command_usage(&warn_cmd); + } + + value = atoi(argv[optind++]); + name = (flags & DEFAULTS_FLAG) ? "0" : argv[optind++]; + + if (!mask) + mask = FS_DQ_WARNS_MASK; + + if (!type) + type = XFS_USER_QUOTA; + + switch (type) { + case XFS_USER_QUOTA: + set_user_warnings(name, type, mask, value); + break; + case XFS_GROUP_QUOTA: + set_group_warnings(name, type, mask, value); + break; + case XFS_PROJ_QUOTA: + set_project_warnings(name, type, mask, value); + break; + } + return 0; +} + +void +edit_init(void) +{ + limit_cmd.name = _("limit"); + limit_cmd.cfunc = limit_f; + limit_cmd.argmin = 2; + limit_cmd.argmax = -1; + limit_cmd.args = \ + _("[-gpu] bsoft|bhard|isoft|ihard|rtbsoft|rtbhard=N -d|id|name"); + limit_cmd.oneline = _("modify quota limits"); + limit_cmd.help = limit_help; + + restore_cmd.name = _("restore"); + restore_cmd.cfunc = restore_f; + restore_cmd.argmin = 0; + restore_cmd.argmax = -1; + restore_cmd.args = _("[-gpu] [-f file]"); + restore_cmd.oneline = _("restore quota limits from a backup file"); + + timer_cmd.name = _("timer"); + timer_cmd.cfunc = timer_f; + timer_cmd.argmin = 2; + timer_cmd.argmax = -1; + timer_cmd.args = _("[-bir] [-gpu] value -d|id|name"); + timer_cmd.oneline = _("get/set quota enforcement timeouts"); + timer_cmd.help = timer_help; + + warn_cmd.name = _("warn"); + warn_cmd.cfunc = warn_f; + warn_cmd.argmin = 2; + warn_cmd.argmax = -1; + warn_cmd.args = _("[-bir] [-gpu] value -d|id|name"); + warn_cmd.oneline = _("get/set enforcement warning counter"); + warn_cmd.help = warn_help; + + if (expert) { + add_command(&limit_cmd); + add_command(&restore_cmd); + add_command(&timer_cmd); + add_command(&warn_cmd); + } +} diff --git a/quota/free.c b/quota/free.c new file mode 100644 index 000000000..df42954bc --- /dev/null +++ b/quota/free.c @@ -0,0 +1,379 @@ +/* + * Copyright (c) 2005 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + */ + +#include +#include "init.h" +#include "quota.h" + +static cmdinfo_t free_cmd; + +static void +free_help(void) +{ + printf(_( +"\n" +" reports the number of free disk blocks and inodes\n" +"\n" +" This command reports the number of total, used, and available disk blocks.\n" +" It can optionally report the same set of numbers for inodes and realtime\n" +" disk blocks, and will report on all known XFS filesystem mount points and\n" +" project quota paths by default (see 'print' command for a list).\n" +" -b -- report the block count values\n" +" -i -- report the inode count values\n" +" -r -- report the realtime block count values\n" +" -h -- report in a human-readable format\n" +" -n -- suppress the header from the output\n" +"\n")); +} + +static int +mount_free_space_data( + struct fs_path *mount, + __uint64_t *bcount, + __uint64_t *bused, + __uint64_t *bfree, + __uint64_t *icount, + __uint64_t *iused, + __uint64_t *ifree, + __uint64_t *rcount, + __uint64_t *rused, + __uint64_t *rfree) +{ + struct xfs_fsop_counts fscounts; + struct xfs_fsop_geom fsgeo; + struct statfs st; + __uint64_t logsize, count, free; + int fd; + + if ((fd = open(mount->fs_dir, O_RDONLY)) < 0) { + fprintf(stderr, "%s: cannot open %s: %s\n", + progname, mount->fs_dir, strerror(errno)); + return 0; + } + + if (platform_fstatfs(fd, &st) < 0) { + perror("fstatfs"); + close(fd); + return 0; + } + if ((xfsctl(mount->fs_dir, fd, XFS_IOC_FSGEOMETRY_V1, &fsgeo)) < 0) { + perror("XFS_IOC_FSGEOMETRY_V1"); + close(fd); + return 0; + } + if ((xfsctl(mount->fs_dir, fd, XFS_IOC_FSCOUNTS, &fscounts)) < 0) { + perror("XFS_IOC_FSCOUNTS"); + close(fd); + return 0; + } + + logsize = fsgeo.logstart ? fsgeo.logblocks : 0; + count = (fsgeo.datablocks - logsize) * fsgeo.blocksize; + free = fscounts.freedata * fsgeo.blocksize; + *bcount = BTOBB(count); + *bfree = BTOBB(free); + *bused = BTOBB(count - free); + + *icount = st.f_files; + *ifree = st.f_ffree; + *iused = st.f_files - st.f_ffree; + + count = fsgeo.rtextents * fsgeo.rtextsize; + free = fscounts.freertx * fsgeo.rtextsize; + *rcount = BTOBB(count); + *rfree = BTOBB(free); + *rused = BTOBB(count - free); + + close(fd); + return 1; +} + +static int +projects_free_space_data( + struct fs_path *path, + __uint64_t *bcount, + __uint64_t *bused, + __uint64_t *bfree, + __uint64_t *icount, + __uint64_t *iused, + __uint64_t *ifree, + __uint64_t *rcount, + __uint64_t *rused, + __uint64_t *rfree) +{ + fs_disk_quota_t d; + struct fsxattr fsx; + __uint32_t projid; + uint type = XFS_PROJ_QUOTA; + char *dev = path->fs_name; + int fd; + + if ((fd = open(path->fs_dir, O_RDONLY)) < 0) { + fprintf(stderr, "%s: cannot open %s: %s\n", + progname, path->fs_dir, strerror(errno)); + return 0; + } + + if ((xfsctl(path->fs_dir, fd, XFS_IOC_FSGETXATTR, &fsx)) < 0) { + perror("XFS_IOC_FSGETXATTR"); + close(fd); + return 0; + } + if (!(fsx.fsx_xflags & XFS_XFLAG_PROJINHERIT)) { + fprintf(stderr, _("%s: project quota flag not set on %s\n"), + progname, path->fs_dir); + close(fd); + return 0; + } + + if ((xfsctl(path->fs_dir, fd, XFS_IOC_GETPROJID, &projid)) < 0) { + perror("XFS_IOC_GETPROJID"); + close(fd); + return 0; + } + if (path->fs_prid != projid) { + fprintf(stderr, + _("%s: project ID %u (%s) doesn't match ID %u (%s)\n"), + progname, path->fs_prid, projects_file, + projid, path->fs_dir); + close(fd); + return 0; + } + + if (xfsquotactl(XFS_GETQUOTA, dev, type, projid, (void *)&d) < 0) { + perror("XFS_GETQUOTA"); + close(fd); + return 0; + } + + /* If no softlimit is set for any of blk/ino/rt, get actual usage */ + if (!d.d_blk_softlimit || !d.d_ino_softlimit || !d.d_rtb_softlimit) { + mount_free_space_data(path, bcount, bused, bfree, + icount, iused, ifree, rcount, rused, rfree); + } + + if (d.d_blk_softlimit) { + *bcount = d.d_blk_softlimit << 1; + *bfree = (d.d_blk_softlimit - d.d_bcount) << 1; + } + *bused = d.d_bcount << 1; + if (d.d_ino_softlimit) { + *icount = d.d_ino_softlimit; + *ifree = (d.d_ino_softlimit - d.d_icount); + } + *iused = d.d_icount; + if (d.d_rtb_softlimit) { + *rcount = d.d_rtb_softlimit << 1; + *rfree = (d.d_rtb_softlimit - d.d_rtbcount) << 1; + } + *rcount = d.d_rtbcount << 1; + + close(fd); + return 1; +} + +static int +free_space( + FILE *fp, + uint form, + fs_path_t *path, + uint flags) +{ + __uint64_t bcount, bused, bfree; + __uint64_t icount, iused, ifree; + __uint64_t rcount, rused, rfree; + char a[8], s[8], u[8], p[8]; + int count; + + count = (path->fs_flags & FS_PROJECT_PATH) ? + projects_free_space_data(path, &bcount, &bused, &bfree, + &icount, &iused, &ifree, + &rcount, &rused, &rfree) : + mount_free_space_data(path, &bcount, &bused, &bfree, + &icount, &iused, &ifree, + &rcount, &rused, &rfree); + if (!count) + return 0; + + if (!(flags & NO_HEADER_FLAG)) { + fprintf(fp, (flags & HUMAN_FLAG) ? + _("Filesystem ") : _("Filesystem ")); + if (form & (XFS_BLOCK_QUOTA|XFS_RTBLOCK_QUOTA)) + fprintf(fp, (flags & HUMAN_FLAG) ? + _(" Size Used Avail Use%%") : + _(" 1K-blocks Used Available Use%%")); + else if (form & XFS_INODE_QUOTA) + fprintf(fp, (flags & HUMAN_FLAG) ? + _(" Inodes Used Free Use%%") : + _(" Inodes IUsed IFree IUse%%")); + fprintf(fp, _(" Pathname\n")); + } + + if (flags & HUMAN_FLAG) { + count = fprintf(fp, "%-12s", path->fs_name); + if (count > 13) + fprintf(fp, "\n%12s", " "); + } else { + count = fprintf(fp, "%-19s", path->fs_name); + if (count > 20) + fprintf(fp, "\n%19s", " "); + } + + if (form & XFS_BLOCK_QUOTA) { + if (flags & HUMAN_FLAG) + fprintf(fp, " %6s %6s %6s %3s%%", + bbs_to_string(bcount, s, sizeof(s)), + bbs_to_string(bused, u, sizeof(u)), + bbs_to_string(bfree, a, sizeof(a)), + pct_to_string(bused, bcount, p, sizeof(p))); + else + fprintf(fp, " %10llu %10llu %10llu %3s%%", + (unsigned long long)bcount >> 1, + (unsigned long long)bused >> 1, + (unsigned long long)bfree >> 1, + pct_to_string(bused, bcount, p, sizeof(p))); + } else if (form & XFS_INODE_QUOTA) { + if (flags & HUMAN_FLAG) + fprintf(fp, " %6s %6s %6s %3s%%", + num_to_string(icount, s, sizeof(s)), + num_to_string(iused, u, sizeof(u)), + num_to_string(ifree, a, sizeof(a)), + pct_to_string(iused, icount, p, sizeof(p))); + else + fprintf(fp, " %10llu %10llu %10llu %3s%%", + (unsigned long long)icount, + (unsigned long long)iused, + (unsigned long long)ifree, + pct_to_string(iused, icount, p, sizeof(p))); + } else if (form & XFS_RTBLOCK_QUOTA) { + if (flags & HUMAN_FLAG) + fprintf(fp, " %6s %6s %6s %3s%%", + bbs_to_string(rcount, s, sizeof(s)), + bbs_to_string(rused, u, sizeof(u)), + bbs_to_string(rfree, a, sizeof(a)), + pct_to_string(rused, rcount, p, sizeof(p))); + else + fprintf(fp, " %10llu %10llu %10llu %3s%%", + (unsigned long long)rcount >> 1, + (unsigned long long)rused >> 1, + (unsigned long long)rfree >> 1, + pct_to_string(rused, rcount, p, sizeof(p))); + } + fprintf(fp, " %s\n", path->fs_dir); + return 1; +} + +static void +free_space_list( + FILE *fp, + uint form, + uint type, + char *dir, + uint flags) +{ + fs_cursor_t cursor; + fs_path_t *path; + + fs_cursor_initialise(dir, type, &cursor); + while ((path = fs_cursor_next_entry(&cursor))) { + if (free_space(fp, form, path, flags)) + flags |= NO_HEADER_FLAG; + } +} + +static int +free_f( + int argc, + char **argv) +{ + FILE *fp = NULL; + char *fname = NULL; + int c, flags = 0, form = 0, type = 0; + + while ((c = getopt(argc, argv, "bf:hnir")) != EOF) { + switch (c) { + case 'f': + fname = optarg; + break; + case 'b': + form |= XFS_BLOCK_QUOTA; + break; + case 'i': + form |= XFS_INODE_QUOTA; + break; + case 'r': + form |= XFS_RTBLOCK_QUOTA; + break; + case 'h': + flags |= HUMAN_FLAG; + break; + case 'n': + flags |= NO_HEADER_FLAG; + break; + default: + return command_usage(&free_cmd); + } + } + + if (!form) + form = XFS_BLOCK_QUOTA; + + if (!type) + type = FS_MOUNT_POINT|FS_PROJECT_PATH; + + if ((fp = fopen_write_secure(fname)) == NULL) + return 0; + + if (argc == optind) + free_space_list(fp, form, type, NULL, flags); + else while (argc > optind) + free_space_list(fp, form, type, argv[optind++], flags); + + if (fname) + fclose(fp); + return 0; +} + +void +free_init(void) +{ + free_cmd.name = _("df"); + free_cmd.altname = _("free"); + free_cmd.cfunc = free_f; + free_cmd.argmin = 0; + free_cmd.argmax = -1; + free_cmd.args = _("[-bir] [-hn] [-f file]"); + free_cmd.oneline = _("show free and used counts for blocks and inodes"); + free_cmd.help = free_help; + + add_command(&free_cmd); +} diff --git a/quota/freebsd.c b/quota/freebsd.c new file mode 100644 index 000000000..e8c10dcb7 --- /dev/null +++ b/quota/freebsd.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2005 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + */ + +#include "quota.h" +#include + +int +xfsquotactl( + int command, + const char *device, + uint type, + uint id, + void *addr) +{ + errno = -ENOSYS; + return -1; +} diff --git a/quota/init.c b/quota/init.c new file mode 100644 index 000000000..b3ddeadca --- /dev/null +++ b/quota/init.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2003-2005 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + */ + +#include +#include +#include +#include "init.h" + +char *progname; +int exitcode; +int expert; + +static char **projopts; /* table of project names (cmdline) */ +static int nprojopts; /* number of entries in name table. */ + +static void +add_project_opt( + char *optarg) +{ + nprojopts++; + projopts = realloc(projopts, sizeof(char*) * nprojopts); + if (!projopts) { + perror("realloc"); + exit(1); + } + projopts[nprojopts - 1] = optarg; +} + +static void +usage(void) +{ + fprintf(stderr, + _("Usage: %s [-p prog] [-c cmd]... [-d project]... [path]\n"), + progname); + exit(1); +} + +void +init_cvtnum( + int *blocksize, + int *sectsize) +{ + *blocksize = 4096; + *sectsize = 512; +} + +static void +init_commands(void) +{ + edit_init(); + free_init(); + help_init(); + path_init(); + project_init(); + quot_init(); + quota_init(); + quit_init(); + report_init(); + state_init(); +} + +static int +init_args_command( + int index) +{ + if (index >= fs_count) + return 0; + + do { + fs_path = &fs_table[index++]; + } while ((fs_path->fs_flags & FS_PROJECT_PATH) && index < fs_count); + + if (index > fs_count) + return 0; + return index; +} + +static void +init( + int argc, + char **argv) +{ + int c; + + progname = basename(argv[0]); + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + while ((c = getopt(argc, argv, "c:d:D:P:p:t:xV")) != EOF) { + switch (c) { + case 'c': /* commands */ + add_user_command(optarg); + break; + case 'd': + add_project_opt(optarg); + break; + case 't': + mtab_file = optarg; + break; + case 'D': + projects_file = optarg; + break; + case 'P': + projid_file = optarg; + break; + case 'p': + progname = optarg; + break; + case 'x': + expert++; + break; + case 'V': + printf(_("%s version %s\n"), progname, VERSION); + exit(0); + default: + usage(); + } + } + + if (optind == argc) + fs_table_initialise(); + else while (optind < argc) { + fs_table_insert_mount(argv[optind++]); + if (!nprojopts) + fs_table_insert_project(NULL); + else + for (c = 0; c < nprojopts; c++) + fs_table_insert_project(projopts[c]); + } + if (projopts) + free(projopts); + + init_commands(); + add_args_command(init_args_command); +} + +int +main( + int argc, + char **argv) +{ + init(argc, argv); + command_loop(); + return exitcode; +} diff --git a/quota/init.h b/quota/init.h new file mode 100644 index 000000000..476f34b23 --- /dev/null +++ b/quota/init.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2003-2005 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + */ + +extern char *progname; +extern int exitcode; +extern int expert; + +extern void edit_init(void); +extern void free_init(void); +extern void path_init(void); +extern void project_init(void); +extern void quot_init(void); +extern void quota_init(void); +extern void report_init(void); +extern void state_init(void); + +extern void init_cvtnum(int *, int *); diff --git a/quota/irix.c b/quota/irix.c new file mode 100644 index 000000000..e568f3000 --- /dev/null +++ b/quota/irix.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2005 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + */ + +#include "quota.h" +#include + +static int +xcommand_to_qcommand( + uint command, + uint type) +{ + switch (command) { + case XFS_QUOTAON: + return Q_XQUOTAON; + case XFS_QUOTAOFF: + return Q_XQUOTAOFF; + case XFS_GETQUOTA: + if (type == XFS_GRPQUOTA) + return Q_XGETGQUOTA; + if (type == XFS_PRJQUOTA) + return Q_XGETPQUOTA; + return Q_XGETQUOTA; + case XFS_SETQLIM: + if (type == XFS_GRPQUOTA) + return Q_XSETGQLIM; + if (type == XFS_PRJQUOTA) + return Q_XSETPQLIM; + return Q_XSETQLIM; + case XFS_GETQSTAT: + return Q_XGETQSTAT; + case XFS_QUOTARM: + return Q_XQUOTARM; + } + return 0; +} + +int +xfsquotactl( + int command, + const char *device, + uint type, + uint id, + void *addr) +{ + int qcommand; + + qcommand = xcommand_to_qcommand(command, type); + return quotactl(qcommand, device, id, addr); +} diff --git a/quota/linux.c b/quota/linux.c new file mode 100644 index 000000000..9cbc3fef1 --- /dev/null +++ b/quota/linux.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2005 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + */ + +#include "quota.h" +#include + +#ifndef PRJQUOTA +#define PRJQUOTA 2 +#endif + +static int +xtype_to_qtype( + uint type) +{ + switch (type) { + case XFS_USER_QUOTA: + return USRQUOTA; + case XFS_GROUP_QUOTA: + return GRPQUOTA; + case XFS_PROJ_QUOTA: + return PRJQUOTA; + } + return 0; +} + +static int +xcommand_to_qcommand( + uint command) +{ + switch (command) { + case XFS_QUOTAON: + return Q_XQUOTAON; + case XFS_QUOTAOFF: + return Q_XQUOTAOFF; + case XFS_GETQUOTA: + return Q_XGETQUOTA; + case XFS_SETQLIM: + return Q_XSETQLIM; + case XFS_GETQSTAT: + return Q_XGETQSTAT; + case XFS_QUOTARM: + return Q_XQUOTARM; + } + return 0; +} + +int +xfsquotactl( + int command, + const char *device, + uint type, + uint id, + void *addr) +{ + int qcommand, qtype; + + qtype = xtype_to_qtype(type); + qcommand = xcommand_to_qcommand(command); + + return quotactl(QCMD(qcommand, qtype), device, id, addr); +} diff --git a/quota/path.c b/quota/path.c new file mode 100644 index 000000000..7b985fb0e --- /dev/null +++ b/quota/path.c @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2005 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + */ + +#include +#include +#include "init.h" +#include "quota.h" + +static cmdinfo_t path_cmd; +static cmdinfo_t print_cmd; + +static void +printpath( + struct fs_path *path, + int index, + int number, + int braces) +{ + fs_quota_stat_t qstat; + fs_project_t *prj; + int c; + + if (index == 0) { + printf(_("%sFilesystem Pathname\n"), + number ? _(" ") : ""); + } + if (number) { + printf(_("%c%03d%c "), braces? '[':' ', index, braces? ']':' '); + } + printf(_("%-19s %s"), path->fs_dir, path->fs_name); + if (path->fs_flags & FS_PROJECT_PATH) { + prj = getprprid(path->fs_prid); + printf(_(" (project %u"), path->fs_prid); + if (prj) + printf(_(", %s"), prj->pr_name); + printf(")"); + } else if (xfsquotactl(XFS_GETQSTAT, path->fs_name, 0, 0, + (void *)&qstat) == 0 && qstat.qs_flags) { + c = 0; + printf(" ("); + if (qstat.qs_flags & XFS_QUOTA_UDQ_ENFD) + c = printf("%suquota", c ? ", " : ""); + else if (qstat.qs_flags & XFS_QUOTA_UDQ_ACCT) + c = printf("%suqnoenforce", c ? ", " : ""); + if (qstat.qs_flags & XFS_QUOTA_GDQ_ENFD) + c = printf("%sgquota", c ? ", " : ""); + else if (qstat.qs_flags & XFS_QUOTA_GDQ_ACCT) + c = printf("%sgqnoenforce", c ? ", " : ""); + if (qstat.qs_flags & XFS_QUOTA_PDQ_ENFD) + c = printf("%spquota", c ? ", " : ""); + else if (qstat.qs_flags & XFS_QUOTA_PDQ_ACCT) + c = printf("%spqnoenforce", c ? ", " : ""); + printf(")"); + } + printf("\n"); +} + +static int +pathlist_f(void) +{ + int i; + + for (i = 0; i < fs_count; i++) + printpath(&fs_table[i], i, 1, &fs_table[i] == fs_path); + return 0; +} + +static int +print_f( + int argc, + char **argv) +{ + int i; + + for (i = 0; i < fs_count; i++) + printpath(&fs_table[i], i, 0, 0); + return 0; +} + +static int +path_f( + int argc, + char **argv) +{ + if (argc <= 1) + return pathlist_f(); + + int i = atoi(argv[1]); + + if (i < 0 || i >= fs_count) { + printf(_("value %d is out of range (0-%d)\n"), + i, fs_count-1); + } else { + fs_path = &fs_table[i]; + pathlist_f(); + } + return 0; +} + +void +path_init(void) +{ + path_cmd.name = _("path"); + path_cmd.altname = _("paths"); + path_cmd.args = _("[N]"); + path_cmd.cfunc = path_f; + path_cmd.argmin = 0; + path_cmd.argmax = 1; + path_cmd.oneline = _("set current path, or show the list of paths"); + + print_cmd.name = _("print"); + print_cmd.altname = _("p"); + print_cmd.cfunc = print_f; + print_cmd.argmin = 0; + print_cmd.argmax = 0; + print_cmd.oneline = _("list known mount points and projects"); + + if (expert) + add_command(&path_cmd); + add_command(&print_cmd); +} diff --git a/quota/project.c b/quota/project.c new file mode 100644 index 000000000..d998c80e6 --- /dev/null +++ b/quota/project.c @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2005 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + */ + +#include +#include +#include "init.h" +#include "quota.h" + +static cmdinfo_t project_cmd; +static prid_t prid; + +enum { + CHECK_PROJECT = 0x1, + SETUP_PROJECT = 0x2, + CLEAR_PROJECT = 0x4, +}; + +static void +project_help(void) +{ + printf(_( +"\n" +" list projects or setup a project tree for tree quota management\n" +"\n" +" Example:\n" +" 'project -c logfiles'\n" +" (match project 'logfiles' to a directory, and setup the directory tree)\n" +"\n" +" Without arguments, report all projects found in the /etc/projects file.\n" +" The project quota mechanism in XFS can be used to implement a form of\n" +" directory tree quota, where a specified directory and all of the files\n" +" and subdirectories below it (i.e. a tree) can be restricted to using a\n" +" subset of the available space in the filesystem.\n" +"\n" +" A managed tree must be setup initially using the -c option with a project.\n" +" The specified project name or identifier is matched to one or more trees\n" +" defined in /etc/projects, and these trees are then recursively descended\n" +" to mark the affected inodes as being part of that tree - which sets inode\n" +" flags and the project identifier on every file.\n" +" Once this has been done, new files created in the tree will automatically\n" +" be accounted to the tree based on their project identifier. An attempt to\n" +" create a hard link to a file in the tree will only succeed if the project\n" +" identifier matches the project identifer for the tree. The xfs_io utility\n" +" can be used to set the project ID for an arbitrary file, but this can only\n" +" be done by a privileged user.\n" +"\n" +" A previously setup tree can be cleared from project quota control through\n" +" use of the -C option, which will recursively descend the tree, clearing\n" +" the affected inodes from project quota control.\n" +"\n" +" The -c option can be used to check whether a tree is setup, it reports\n" +" nothing if the tree is correct, otherwise it reports the paths of inodes\n" +" which do not have the project ID of the rest of the tree, or if the inode\n" +" flag is not set.\n" +"\n" +" The /etc/projid and /etc/projects file formats are simple, and described\n" +" on the xfs_quota man page.\n" +"\n")); +} + +static int +check_project( + const char *path, + const struct stat *stat, + int status, + struct FTW *data) +{ + struct fsxattr fsx; + prid_t prj; + int fd; + + if ((fd = open(path, O_RDONLY|O_NOCTTY)) == -1) + fprintf(stderr, _("%s: cannot open %s: %s\n"), + progname, path, strerror(errno)); + else if ((xfsctl(path, fd, XFS_IOC_FSGETXATTR, &fsx)) < 0) + fprintf(stderr, _("%s: cannot get flags on %s: %s\n"), + progname, path, strerror(errno)); + else if (getprojid(path, fd, &prj) < 0) + fprintf(stderr, _("%s: cannot get project ID on %s: %s\n"), + progname, path, strerror(errno)); + else { + if (prj != prid) + printf(_("%s - project identifier is not set" + " (inode=%u, tree=%u)\n"), + path, prj, prid); + if (!(fsx.fsx_xflags & XFS_XFLAG_PROJINHERIT)) + printf(_("%s - project inheritance flag is not set\n"), + path); + } + if (fd != -1) + close(fd); + return 0; +} + +static int +clear_project( + const char *path, + const struct stat *stat, + int status, + struct FTW *data) +{ + struct fsxattr fsx; + int fd; + + if ((fd = open(path, O_RDONLY|O_NOCTTY)) == -1) { + fprintf(stderr, _("%s: cannot open %s: %s\n"), + progname, path, strerror(errno)); + return 0; + } else if (xfsctl(path, fd, XFS_IOC_FSGETXATTR, &fsx) < 0) { + fprintf(stderr, _("%s: cannot get flags on %s: %s\n"), + progname, path, strerror(errno)); + close(fd); + return 0; + } + + fsx.fsx_xflags &= ~XFS_XFLAG_PROJINHERIT; + + if (setprojid(path, fd, 0) < 0) + fprintf(stderr, _("%s: cannot clear project ID on %s: %s\n"), + progname, path, strerror(errno)); + else if (xfsctl(path, fd, XFS_IOC_FSSETXATTR, &fsx) < 0) + fprintf(stderr, _("%s: cannot clear flags on %s: %s\n"), + progname, path, strerror(errno)); + close(fd); + return 0; +} + +static int +setup_project( + const char *path, + const struct stat *stat, + int status, + struct FTW *data) +{ + struct fsxattr fsx; + int fd; + + if ((fd = open(path, O_RDONLY|O_NOCTTY)) == -1) { + fprintf(stderr, _("%s: cannot open %s: %s\n"), + progname, path, strerror(errno)); + return 0; + } else if (xfsctl(path, fd, XFS_IOC_FSGETXATTR, &fsx) < 0) { + fprintf(stderr, _("%s: cannot get flags on %s: %s\n"), + progname, path, strerror(errno)); + close(fd); + return 0; + } + + fsx.fsx_xflags |= XFS_XFLAG_PROJINHERIT; + + if (xfsctl(path, fd, XFS_IOC_FSSETXATTR, &fsx) < 0) + fprintf(stderr, _("%s: cannot set flags on %s: %s\n"), + progname, path, strerror(errno)); + else if (setprojid(path, fd, prid) < 0) + fprintf(stderr, _("%s: cannot set project ID on %s: %s\n"), + progname, path, strerror(errno)); + close(fd); + return 0; +} + +static void +project_operations( + char *project, + char *dir, + int type) +{ + switch (type) { + case CHECK_PROJECT: + printf(_("Checking project %s (path %s)...\n"), project, dir); + nftw(dir, check_project, 100, FTW_PHYS|FTW_MOUNT|FTW_DEPTH); + break; + case SETUP_PROJECT: + printf(_("Setting up project %s (path %s)...\n"), project, dir); + nftw(dir, setup_project, 100, FTW_PHYS|FTW_MOUNT|FTW_DEPTH); + break; + case CLEAR_PROJECT: + printf(_("Clearing project %s (path %s)...\n"), project, dir); + nftw(dir, clear_project, 100, FTW_PHYS|FTW_MOUNT|FTW_DEPTH); + break; + } +} + +static void +project( + char *project, + int type) +{ + fs_cursor_t cursor; + fs_path_t *path; + int count = 0; + + fs_cursor_initialise(NULL, FS_PROJECT_PATH, &cursor); + while ((path = fs_cursor_next_entry(&cursor))) { + if (prid != path->fs_prid) + continue; + project_operations(project, path->fs_dir, type); + count++; + } + + printf(_("Processed %d %s paths for project %s\n"), + count, projects_file, project); +} + +static int +project_f( + int argc, + char **argv) +{ + int c, type = 0; + + while ((c = getopt(argc, argv, "csC")) != EOF) { + switch (c) { + case 'c': + type = CHECK_PROJECT; + break; + case 's': + type = SETUP_PROJECT; + break; + case 'C': + type = CLEAR_PROJECT; + break; + default: + return command_usage(&project_cmd); + } + } + + if (argc == optind) + return command_usage(&project_cmd); + + /* no options - just check the given projects */ + if (!type) + type = CHECK_PROJECT; + + setprfiles(); + if (access(projects_file, F_OK) != 0) { + fprintf(stderr, _("projects file \"%s\" doesn't exist\n"), + projects_file); + return 0; + } + + while (argc > optind) { + prid = prid_from_string(argv[optind]); + if (prid == -1) + fprintf(stderr, _("%s - no such project in %s\n"), + argv[optind], projects_file); + else + project(argv[optind], type); + optind++; + } + + return 0; +} + +void +project_init(void) +{ + project_cmd.name = _("project"); + project_cmd.altname = _("tree"); + project_cmd.cfunc = project_f; + project_cmd.args = _("[-c|-s|-C] project ..."); + project_cmd.argmin = 1; + project_cmd.argmax = -1; + project_cmd.oneline = _("check, setup or clear project quota trees"); + project_cmd.help = project_help; + + if (expert) + add_command(&project_cmd); +} diff --git a/quota/quot.c b/quota/quot.c new file mode 100644 index 000000000..068c40970 --- /dev/null +++ b/quota/quot.c @@ -0,0 +1,430 @@ +/* + * Copyright (c) 2005 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + */ + +#include +#include +#include +#include +#include "init.h" +#include "quota.h" + +typedef struct du { + struct du *next; + __uint64_t blocks; + __uint64_t blocks30; + __uint64_t blocks60; + __uint64_t blocks90; + __uint64_t nfiles; + __uint32_t id; +} du_t; + +#define TSIZE 500 +static __uint64_t sizes[TSIZE]; +static __uint64_t overflow; + +#define NDU 60000 +#define DUHASH 8209 +static du_t du[3][NDU]; +static du_t *duhash[3][DUHASH]; +static int ndu[3]; /* #usr/grp/prj */ + +#define NBSTAT 4069 + +static time_t now; +static cmdinfo_t quot_cmd; + +static void +quot_help(void) +{ + printf(_( +"\n" +" display a summary of filesystem ownership\n" +"\n" +" -a -- summarise for all local XFS filesystem mount points\n" +" -c -- display three columns giving file size in kilobytes, number of files\n" +" of that size, and cumulative total of kilobytes in that size or\n" +" smaller file. The last row is used as an overflow bucket and is the\n" +" total of all files greater than 500 kilobytes.\n" +" -v -- display three columns containing the number of kilobytes not\n" +" accessed in the last 30, 60, and 90 days.\n" +" -g -- display group summary\n" +" -p -- display project summary\n" +" -u -- display user summary\n" +" -b -- display number of blocks used\n" +" -i -- display number of inodes used\n" +" -r -- display number of realtime blocks used\n" +" -n -- suppress the initial header\n" +" -f -- send output to a file\n" +" The (optional) user/group/project can be specified either by name or by\n" +" number (i.e. uid/gid/projid).\n" +"\n")); +} + +static void +quot_bulkstat_add( + xfs_bstat_t *p, + uint flags) +{ + du_t *dp; + du_t **hp; + __uint64_t size; + __uint32_t i, id; + + if ((p->bs_mode & S_IFMT) == 0) + return; + size = howmany((p->bs_blocks * p->bs_blksize), 0x400ULL); + + if (flags & HISTOGRAM_FLAG) { + if (!(S_ISDIR(p->bs_mode) || S_ISREG(p->bs_mode))) + return; + if (size >= TSIZE) { + overflow += size; + size = TSIZE - 1; + } + sizes[(int)size]++; + return; + } + for (i = 0; i < 3; i++) { + id = (i == 0) ? p->bs_uid : ((i == 1) ? + p->bs_gid : p->bs_projid); + hp = &duhash[i][id % DUHASH]; + for (dp = *hp; dp; dp = dp->next) + if (dp->id == id) + break; + if (dp == 0) { + if (ndu[i] >= NDU) + return; + dp = &du[i][(ndu[i]++)]; + dp->next = *hp; + *hp = dp; + dp->id = id; + dp->nfiles = 0; + dp->blocks = 0; + dp->blocks30 = 0; + dp->blocks60 = 0; + dp->blocks90 = 0; + } + dp->blocks += size; + + if (now - p->bs_atime.tv_sec > 30 * (60*60*24)) + dp->blocks30 += size; + if (now - p->bs_atime.tv_sec > 60 * (60*60*24)) + dp->blocks60 += size; + if (now - p->bs_atime.tv_sec > 90 * (60*60*24)) + dp->blocks90 += size; + dp->nfiles++; + } +} + +static void +quot_bulkstat_mount( + char *fsdir, + uint flags) +{ + xfs_fsop_bulkreq_t bulkreq; + xfs_bstat_t *buf; + __int64_t last = 0; + __int32_t count; + int i, sts, fsfd; + du_t **dp; + + /* + * Initialize tables between checks; because of the qsort + * in report() the hash tables must be rebuilt each time. + */ + for (sts = 0; sts < TSIZE; sts++) + sizes[sts] = 0; + overflow = 0; + for (i = 0; i < 3; i++) + for (dp = duhash[i]; dp < &duhash[i][DUHASH]; dp++) + *dp = 0; + ndu[0] = ndu[1] = ndu[2] = 0; + + fsfd = open(fsdir, O_RDONLY); + if (fsfd < 0) { + perror(fsdir); + return; + } + + buf = (xfs_bstat_t *)calloc(NBSTAT, sizeof(xfs_bstat_t)); + if (!buf) { + perror("calloc"); + return; + } + + bulkreq.lastip = &last; + bulkreq.icount = NBSTAT; + bulkreq.ubuffer = buf; + bulkreq.ocount = &count; + + while ((sts = xfsctl(fsdir, fsfd, XFS_IOC_FSBULKSTAT, &bulkreq)) == 0) { + if (count == 0) + break; + for (i = 0; i < count; i++) + quot_bulkstat_add(&buf[i], flags); + } + if (sts < 0) + perror("XFS_IOC_FSBULKSTAT"), + free(buf); + close(fsfd); +} + +static int +qcompare( + du_t *p1, + du_t *p2) +{ + if (p1->blocks > p2->blocks) + return -1; + if (p1->blocks < p2->blocks) + return 1; + if (p1->id > p2->id) + return 1; + else if (p1->id < p2->id) + return -1; + return 0; +} + +typedef char *(*idtoname_t)(__uint32_t); + +static void +quot_report_mount_any_type( + FILE *fp, + du_t *dp, + int count, + idtoname_t names, + uint form, + uint type, + fs_path_t *mount, + uint flags) +{ + char *cp; + + fprintf(fp, _("%s (%s) %s:\n"), + mount->fs_name, mount->fs_dir, type_to_string(type)); + qsort(dp, count, sizeof(dp[0]), + (int (*)(const void *, const void *))qcompare); + for (; dp < &dp[count]; dp++) { + if (dp->blocks == 0) + return; + fprintf(fp, "%8llu ", (unsigned long long) dp->blocks); + if (form & XFS_INODE_QUOTA) + fprintf(fp, "%8llu ", + (unsigned long long) dp->nfiles); + if ((cp = (names)(dp->id)) != NULL) + fprintf(fp, "%-8.8s", cp); + else + fprintf(fp, "#%-7d", dp->id); + if (flags & VERBOSE_FLAG) + fprintf(fp, " %8llu %8llu %8llu", + (unsigned long long) dp->blocks30, + (unsigned long long) dp->blocks60, + (unsigned long long) dp->blocks90); + fputc('\n', fp); + } +} + +static void +quot_report_mount( + FILE *fp, + uint form, + uint type, + fs_path_t *mount, + uint flags) +{ + switch (type) { + case XFS_GROUP_QUOTA: + quot_report_mount_any_type(fp, du[1], ndu[1], gid_to_name, + form, type, mount, flags); + break; + case XFS_PROJ_QUOTA: + quot_report_mount_any_type(fp, du[2], ndu[2], prid_to_name, + form, type, mount, flags); + break; + case XFS_USER_QUOTA: + quot_report_mount_any_type(fp, du[0], ndu[0], uid_to_name, + form, type, mount, flags); + } +} + +static void +quot_report( + FILE *fp, + uint form, + uint type, + char *dir, + uint flags) +{ + fs_cursor_t cursor; + fs_path_t *mount; + + now = time(0); + fs_cursor_initialise(dir, FS_MOUNT_POINT, &cursor); + while ((mount = fs_cursor_next_entry(&cursor))) { + quot_bulkstat_mount(mount->fs_dir, flags); + quot_report_mount(fp, form, type, mount, flags); + } +} + +static void +quot_histogram_mount( + FILE *fp, + fs_path_t *mount, + uint flags) +{ + __uint64_t t = 0; + int i; + + fprintf(fp, _("%s (%s):\n"), mount->fs_name, mount->fs_dir); + + for (i = 0; i < TSIZE - 1; i++) + if (sizes[i] > 0) { + t += sizes[i] * i; + fprintf(fp, _("%d\t%llu\t%llu\n"), i, + (unsigned long long) sizes[i], + (unsigned long long) t); + } + fprintf(fp, _("%d\t%llu\t%llu\n"), TSIZE - 1, + (unsigned long long) sizes[TSIZE - 1], + (unsigned long long) (overflow + t)); +} + +static void +quot_histogram( + FILE *fp, + char *dir, + uint flags) +{ + fs_cursor_t cursor; + fs_path_t *mount; + + fs_cursor_initialise(dir, FS_MOUNT_POINT, &cursor); + while ((mount = fs_cursor_next_entry(&cursor))) { + quot_bulkstat_mount(mount->fs_dir, flags); + quot_histogram_mount(fp, mount, flags); + } +} + +static void +quot_any_type( + FILE *fp, + uint form, + uint type, + char *dir, + uint flags) +{ + if (flags & HISTOGRAM_FLAG) + quot_histogram(fp, dir, flags); + else + quot_report(fp, form, type, dir, flags); +} + +static int +quot_f( + int argc, + char **argv) +{ + FILE *fp = NULL; + char *fname = NULL; + int c, flags = 0, type = 0, form = 0; + + while ((c = getopt(argc, argv, "abcf:hgipruv")) != EOF) { + switch (c) { + case 'f': + fname = optarg; + break; + case 'b': + form |= XFS_BLOCK_QUOTA; + break; + case 'i': + form |= XFS_INODE_QUOTA; + break; + case 'r': + form |= XFS_RTBLOCK_QUOTA; + break; + case 'g': + type = XFS_GROUP_QUOTA; + break; + case 'p': + type = XFS_PROJ_QUOTA; + break; + case 'u': + type = XFS_USER_QUOTA; + break; + case 'a': + flags |= ALL_MOUNTS_FLAG; + break; + case 'c': + flags |= HISTOGRAM_FLAG; + break; + case 'v': + flags |= VERBOSE_FLAG; + break; + default: + return command_usage("_cmd); + } + } + + if (!form) + form = XFS_BLOCK_QUOTA; + + if (!type) + type = XFS_USER_QUOTA; + + if ((fp = fopen_write_secure(fname)) == NULL) + return 0; + + if (argc == optind) + quot_any_type(fp, form, type, (flags & ALL_MOUNTS_FLAG) ? + NULL : fs_path->fs_dir, flags); + else while (argc > optind) + quot_any_type(fp, form, type, argv[optind++], flags); + + if (fname) + fclose(fp); + return 0; +} + +void +quot_init(void) +{ + quot_cmd.name = _("quot"); + quot_cmd.cfunc = quot_f; + quot_cmd.argmin = 0; + quot_cmd.argmax = -1; + quot_cmd.args = _("[-bir] [-gpu] [-acv] [-f file]"); + quot_cmd.oneline = _("summarize filesystem ownership"); + quot_cmd.help = quot_help; + + if (expert) + add_command("_cmd); +} diff --git a/quota/quota.c b/quota/quota.c new file mode 100644 index 000000000..8af5facc9 --- /dev/null +++ b/quota/quota.c @@ -0,0 +1,461 @@ +/* + * Copyright (c) 2005 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + */ + +#include +#include +#include +#include +#include "init.h" +#include "quota.h" + +static cmdinfo_t quota_cmd; + +static void +quota_help(void) +{ + printf(_( +"\n" +" display usage and quota information\n" +"\n" +" -g -- display group quota information\n" +" -p -- display project quota information\n" +" -u -- display user quota information\n" +" -b -- display number of blocks used\n" +" -i -- display number of inodes used\n" +" -r -- display number of realtime blocks used\n" +" -h -- report in a human-readable format\n" +" -n -- suppress the initial header\n" +" -v -- increase verbosity in reporting (also dumps zero values)\n" +" -f -- send output to a file\n" +" The (optional) user/group/project can be specified either by name or by\n" +" number (i.e. uid/gid/projid).\n" +"\n")); +} + +static int +quota_mount( + FILE *fp, + __uint32_t id, + char *name, + uint form, + uint type, + fs_path_t *mount, + uint flags) +{ + fs_disk_quota_t d; + char *dev = mount->fs_name; + char c[8], h[8], s[8]; + uint qflags; + int count; + + if (xfsquotactl(XFS_GETQUOTA, dev, type, id, (void *)&d) < 0) + return 0; + + if (!(flags & VERBOSE_FLAG)) { + count = 0; + if ((form & XFS_BLOCK_QUOTA) && d.d_bcount) + count++; + if ((form & XFS_INODE_QUOTA) && d.d_icount) + count++; + if ((form & XFS_RTBLOCK_QUOTA) && d.d_rtbcount) + count++; + if (!count) + return 0; + } + + if (!(flags & NO_HEADER_FLAG)) { + fprintf(fp, + _("Disk quotas for %s %s (%u)\nFilesystem%s"), + type_to_string(type), name, id, + (flags & HUMAN_FLAG) ? " " : " "); + if (form & XFS_BLOCK_QUOTA) + fprintf(fp, (flags & HUMAN_FLAG) ? + _(" Blocks Quota Limit Warn/Time ") : + _(" Blocks Quota Limit Warn/Time ")); + if (form & XFS_INODE_QUOTA) + fprintf(fp, (flags & HUMAN_FLAG) ? + _(" Files Quota Limit Warn/Time ") : + _(" Files Quota Limit Warn/Time ")); + if (form & XFS_RTBLOCK_QUOTA) + fprintf(fp, (flags & HUMAN_FLAG) ? + _("Realtime Quota Limit Warn/Time ") : + _(" Realtime Quota Limit Warn/Time ")); + fputs("Mounted on\n", fp); + } + + if (flags & HUMAN_FLAG) { + count = fprintf(fp, "%-12s", dev); + if (count > 13) + fprintf(fp, "\n%12s", " "); + } else { + count = fprintf(fp, "%-19s", dev); + if (count > 20) + fprintf(fp, "\n%19s", " "); + } + + if (form & XFS_BLOCK_QUOTA) { + qflags = (flags & HUMAN_FLAG); + if (d.d_blk_hardlimit && d.d_bcount > d.d_blk_hardlimit) + qflags |= LIMIT_FLAG; + if (d.d_blk_softlimit && d.d_bcount > d.d_blk_softlimit) + qflags |= QUOTA_FLAG; + if (flags & HUMAN_FLAG) + fprintf(fp, " %6s %6s %6s %02d %8s ", + bbs_to_string(d.d_bcount, c, sizeof(c)), + bbs_to_string(d.d_blk_softlimit, s, sizeof(s)), + bbs_to_string(d.d_blk_hardlimit, h, sizeof(h)), + d.d_bwarns, + time_to_string(d.d_btimer, qflags)); + else + fprintf(fp, " %10llu %10llu %10llu %02d %9s ", + (unsigned long long)d.d_bcount >> 1, + (unsigned long long)d.d_blk_softlimit >> 1, + (unsigned long long)d.d_blk_hardlimit >> 1, + d.d_bwarns, + time_to_string(d.d_btimer, qflags)); + } + if (form & XFS_INODE_QUOTA) { + qflags = (flags & HUMAN_FLAG); + if (d.d_ino_hardlimit && d.d_icount > d.d_ino_hardlimit) + qflags |= LIMIT_FLAG; + if (d.d_ino_softlimit && d.d_icount > d.d_ino_softlimit) + qflags |= QUOTA_FLAG; + if (flags & HUMAN_FLAG) + fprintf(fp, " %6s %6s %6s %02d %8s ", + num_to_string(d.d_icount, c, sizeof(c)), + num_to_string(d.d_ino_softlimit, s, sizeof(s)), + num_to_string(d.d_ino_hardlimit, h, sizeof(h)), + d.d_iwarns, + time_to_string(d.d_itimer, qflags)); + else + fprintf(fp, " %10llu %10llu %10llu %02d %9s ", + (unsigned long long)d.d_icount, + (unsigned long long)d.d_ino_softlimit, + (unsigned long long)d.d_ino_hardlimit, + d.d_iwarns, + time_to_string(d.d_itimer, qflags)); + } + if (form & XFS_RTBLOCK_QUOTA) { + qflags = (flags & HUMAN_FLAG); + if (d.d_rtb_hardlimit && d.d_rtbcount > d.d_rtb_hardlimit) + qflags |= LIMIT_FLAG; + if (d.d_rtb_softlimit && d.d_rtbcount > d.d_rtb_softlimit) + qflags |= QUOTA_FLAG; + if (flags & HUMAN_FLAG) + fprintf(fp, " %6s %6s %6s %02d %8s ", + bbs_to_string(d.d_rtbcount, c, sizeof(c)), + bbs_to_string(d.d_rtb_softlimit, s, sizeof(s)), + bbs_to_string(d.d_rtb_hardlimit, h, sizeof(h)), + d.d_rtbwarns, + time_to_string(d.d_rtbtimer, qflags)); + else + fprintf(fp, " %10llu %10llu %10llu %02d %9s ", + (unsigned long long)d.d_rtbcount >> 1, + (unsigned long long)d.d_rtb_softlimit >> 1, + (unsigned long long)d.d_rtb_hardlimit >> 1, + d.d_rtbwarns, + time_to_string(d.d_rtbtimer, qflags)); + } + fprintf(fp, "%s\n", mount->fs_dir); + return 1; +} + +static void +quota( + FILE *fp, + __uint32_t id, + char *name, + uint form, + uint type, + uint flags) +{ + fs_cursor_t cursor; + fs_path_t *path; + + fs_cursor_initialise(NULL, FS_MOUNT_POINT, &cursor); + while ((path = fs_cursor_next_entry(&cursor))) { + if (quota_mount(fp, id, name, form, type, path, flags)) + flags |= NO_HEADER_FLAG; + } +} + +static char * +getusername( + uid_t uid) +{ + static char buffer[32]; + struct passwd *u; + + if ((u = getpwuid(uid))) + return u->pw_name; + snprintf(buffer, sizeof(buffer), "#%u", uid); + return &buffer[0]; +} + +static void +quota_user_type( + FILE *fp, + char *name, + uint form, + uint type, + uint flags) +{ + struct passwd *u; + uid_t id; + + if (name) { + if (isdigit(name[0])) { + id = atoi(name); + name = getusername(id); + } else if ((u = getpwnam(name))) { + id = u->pw_uid; + name = u->pw_name; + } else { + fprintf(stderr, _("%s: cannot find user %s\n"), + progname, name); + return; + } + } else { + id = getuid(); + name = getusername(id); + } + + quota(fp, id, name, form, type, flags); +} + +static char * +getgroupname( + gid_t gid) +{ + static char buffer[32]; + struct group *g; + + if ((g = getgrgid(gid))) + return g->gr_name; + snprintf(buffer, sizeof(buffer), "#%u", gid); + return &buffer[0]; +} + +static void +quota_group_type( + FILE *fp, + char *name, + uint form, + uint type, + uint flags) +{ + struct group *g; + gid_t gid, *gids = NULL; + int i, ngroups, dofree = 0; + + if (name) { + if (isdigit(name[0])) { + gid = atoi(name); + name = getgroupname(gid); + } else { + if ((g = getgrnam(name))) { + gid = g->gr_gid; + name = g->gr_name; + } else { + fprintf(stderr, _("%s: cannot find group %s\n"), + progname, name); + return; + } + } + gids = &gid; + ngroups = 1; + } else if ( ((ngroups = sysconf(_SC_NGROUPS_MAX)) < 0) || + ((gids = malloc(ngroups * sizeof(gid_t))) == NULL) || + ((ngroups = getgroups(ngroups, gids)) < 0)) { + dofree = (gids != NULL); + gid = getgid(); + gids = &gid; + ngroups = 1; + } else { + dofree = (gids != NULL); + } + + for (i = 0; i < ngroups; i++, name = NULL) { + if (!name) + name = getgroupname(gids[i]); + quota(fp, gids[i], name, form, type, flags); + } + + if (dofree) + free(gids); +} + +static char * +getprojectname( + prid_t prid) +{ + static char buffer[32]; + fs_project_t *p; + + if ((p = getprprid(prid))) + return p->pr_name; + snprintf(buffer, sizeof(buffer), "#%u", prid); + return &buffer[0]; +} + +static void +quota_proj_type( + FILE *fp, + char *name, + uint form, + uint type, + uint flags) +{ + fs_project_t *p; + prid_t id; + + if (!name) { + fprintf(stderr, _("%s: must specify a project name/ID\n"), + progname); + return; + } + + if (isdigit(name[0])) { + id = atoi(name); + name = getprojectname(id); + } else if ((p = getprnam(name))) { + id = p->pr_prid; + name = p->pr_name; + } else { + fprintf(stderr, _("%s: cannot find project %s\n"), + progname, name); + return; + } + + quota(fp, id, name, form, type, flags); +} + +static void +quota_any_type( + FILE *fp, + char *name, + uint form, + uint type, + uint flags) +{ + switch (type) { + case XFS_USER_QUOTA: + quota_user_type(fp, name, form, type, flags); + break; + case XFS_GROUP_QUOTA: + quota_group_type(fp, name, form, type, flags); + break; + case XFS_PROJ_QUOTA: + quota_proj_type(fp, name, form, type, flags); + break; + } +} + +static int +quota_f( + int argc, + char **argv) +{ + FILE *fp = NULL; + char *fname = NULL; + int c, flags = 0, type = 0, form = 0; + + while ((c = getopt(argc, argv, "bf:hgnipruv")) != EOF) { + switch (c) { + case 'f': + fname = optarg; + break; + case 'b': + form |= XFS_BLOCK_QUOTA; + break; + case 'i': + form |= XFS_INODE_QUOTA; + break; + case 'r': + form |= XFS_RTBLOCK_QUOTA; + break; + case 'g': + type = XFS_GROUP_QUOTA; + break; + case 'p': + type = XFS_PROJ_QUOTA; + break; + case 'u': + type = XFS_USER_QUOTA; + break; + case 'h': + flags |= HUMAN_FLAG; + break; + case 'n': + flags |= NO_HEADER_FLAG; + break; + case 'v': + flags |= VERBOSE_FLAG; + break; + default: + return command_usage("a_cmd); + } + } + + if (!form) + form = XFS_BLOCK_QUOTA; + + if (!type) + type = XFS_USER_QUOTA; + + if ((fp = fopen_write_secure(fname)) == NULL) + return 0; + + if (argc == optind) + quota_any_type(fp, NULL, form, type, flags); + else while (argc > optind) + quota_any_type(fp, argv[optind++], form, type, flags); + + if (fname) + fclose(fp); + return 0; +} + +void +quota_init(void) +{ + quota_cmd.name = _("quota"); + quota_cmd.altname = _("l"); + quota_cmd.cfunc = quota_f; + quota_cmd.argmin = 0; + quota_cmd.argmax = -1; + quota_cmd.args = _("[-bir] [-gpu] [-hnv] [-f file] [id|name]..."); + quota_cmd.oneline = _("show usage and limits"); + quota_cmd.help = quota_help; + + add_command("a_cmd); +} diff --git a/quota/quota.h b/quota/quota.h new file mode 100644 index 000000000..818427e35 --- /dev/null +++ b/quota/quota.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2005 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + */ + +#include +#include +#include + +/* + * Different forms of XFS quota + */ +enum { + XFS_BLOCK_QUOTA = 0x1, + XFS_INODE_QUOTA = 0x2, + XFS_RTBLOCK_QUOTA = 0x4, +}; + +/* + * System call definitions mapping to platform-specific quotactl + */ +extern int xfsquotactl(int __cmd, const char *__device, + uint __type, uint __id, void * __addr); +enum { + XFS_QUOTAON, /* enable accounting/enforcement */ + XFS_QUOTAOFF, /* disable accounting/enforcement */ + XFS_GETQUOTA, /* get disk limits and usage */ + XFS_SETQLIM, /* set disk limits */ + XFS_GETQSTAT, /* get quota subsystem status */ + XFS_QUOTARM, /* free disk space used by dquots */ +}; + +/* + * Utility routines + */ +extern char *type_to_string(uint __type); +extern char *form_to_string(uint __form); +extern char *time_to_string(__uint32_t __time, uint __flags); +extern char *bbs_to_string(__uint64_t __v, char *__c, uint __size); +extern char *num_to_string(__uint64_t __v, char *__c, uint __size); +extern char *pct_to_string(__uint64_t __v, __uint64_t __t, char *__c, uint __s); + +extern FILE *fopen_write_secure(char *__filename); + +/* + * Various utility routine flags + */ +enum { + NO_HEADER_FLAG = 0x0001, /* don't print header */ + VERBOSE_FLAG = 0x0002, /* increase verbosity */ + HUMAN_FLAG = 0x0004, /* human-readable values */ + QUOTA_FLAG = 0x0008, /* uid/gid/prid over-quota (soft) */ + LIMIT_FLAG = 0x0010, /* uid/gid/prid over-limit (hard) */ + ALL_MOUNTS_FLAG = 0x0020, /* iterate over every mounted xfs */ + TERSE_FLAG = 0x0040, /* decrease verbosity */ + HISTOGRAM_FLAG = 0x0080, /* histogram format output */ + DEFAULTS_FLAG = 0x0100, /* use value as a default */ + ABSOLUTE_FLAG = 0x0200, /* absolute time, not related to now */ +}; + +/* + * Identifier (uid/gid/prid) cache routines + */ +extern char *uid_to_name(__uint32_t __uid); +extern char *gid_to_name(__uint32_t __gid); +extern char *prid_to_name(__uint32_t __prid); + diff --git a/quota/report.c b/quota/report.c new file mode 100644 index 000000000..0d297e9ee --- /dev/null +++ b/quota/report.c @@ -0,0 +1,545 @@ +/* + * Copyright (c) 2005 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + */ + +#include +#include +#include +#include "init.h" +#include "quota.h" + +static cmdinfo_t dump_cmd; +static cmdinfo_t report_cmd; + +static void +dump_help(void) +{ + dump_cmd.args = _("[-gpu] [-f file]"); + dump_cmd.oneline = _("dump quota information for backup utilities"); + printf(_( +"\n" +" create a backup file which contains quota limits information\n" +" -g -- dump out group quota limits\n" +" -p -- dump out project quota limits\n" +" -u -- dump out user quota limits (default)\n" +" -f -- write the dump out to the specified file\n" +"\n")); +} + +static void +report_help(void) +{ + report_cmd.args = _("[-bir] [-gpu] [-ahnt] [-f file]"); + report_cmd.oneline = _("report filesystem quota information"); + printf(_( +"\n" +" report used space and inodes, and quota limits, for a filesystem\n" +" Example:\n" +" 'report -igh'\n" +" (reports inode usage for all groups, in an easy-to-read format)\n" +" This command is the equivalent of the traditional repquota command, which\n" +" prints a summary of the disk usage and quotas for the current filesystem,\n" +" or all filesystems.\n" +" -a -- report for all mounted filesystems with quota enabled\n" +" -h -- report in a human-readable format\n" +" -n -- suppress the header from the output\n" +" -t -- terse output format, hides rows which are all zero\n" +" -g -- report group usage and quota information\n" +" -p -- report project usage and quota information\n" +" -u -- report user usage and quota information\n" +" -b -- report blocks-used information only\n" +" -i -- report inodes-used information only\n" +" -r -- report realtime-blocks-used information only\n" +"\n")); +} + +static void +dump_file( + FILE *fp, + uint id, + uint type, + char *dev) +{ + fs_disk_quota_t d; + + if (xfsquotactl(XFS_GETQUOTA, dev, type, id, (void *)&d) < 0) + return; + fprintf(fp, "fs = %s\n", dev); + fprintf(fp, "%-10d %7llu %7llu %7llu %7llu %7llu %7llu\n", id, + (unsigned long long)d.d_blk_softlimit, + (unsigned long long)d.d_blk_hardlimit, + (unsigned long long)d.d_ino_softlimit, + (unsigned long long)d.d_ino_hardlimit, + (unsigned long long)d.d_rtb_softlimit, + (unsigned long long)d.d_rtb_hardlimit); +} + +static void +dump_limits_any_type( + FILE *fp, + uint type, + char *dir) +{ + fs_path_t *mount; + + if ((mount = fs_table_lookup(dir, FS_MOUNT_POINT)) == NULL) { + fprintf(stderr, "%s: cannot find mount point %s\n", + progname, dir); + return; + } + + switch (type) { + case XFS_GROUP_QUOTA: { + struct group *g; + setgrent(); + while ((g = getgrent()) != NULL) + dump_file(fp, g->gr_gid, type, mount->fs_name); + endgrent(); + break; + } + case XFS_PROJ_QUOTA: { + struct fs_project *p; + setprent(); + while ((p = getprent()) != NULL) + dump_file(fp, p->pr_prid, type, mount->fs_name); + endprent(); + break; + } + case XFS_USER_QUOTA: { + struct passwd *u; + setpwent(); + while ((u = getpwent()) != NULL) + dump_file(fp, u->pw_uid, type, mount->fs_name); + endpwent(); + break; + } + } +} + +static int +dump_f( + int argc, + char **argv) +{ + FILE *fp; + char *fname = NULL; + int c, type = XFS_USER_QUOTA; + + while ((c = getopt(argc, argv, "f:gpu")) != EOF) { + switch(c) { + case 'f': + fname = optarg; + break; + case 'g': + type = XFS_GROUP_QUOTA; + break; + case 'p': + type = XFS_PROJ_QUOTA; + break; + case 'u': + type = XFS_USER_QUOTA; + break; + default: + return command_usage(&dump_cmd); + } + } + + if (argc != optind) + return command_usage(&dump_cmd); + + if ((fp = fopen_write_secure(fname)) == NULL) + return 0; + + dump_limits_any_type(fp, type, fs_path->fs_dir); + + if (fname) + fclose(fp); + + return 0; +} + +static void +report_header( + FILE *fp, + uint form, + uint type, + fs_path_t *mount, + int flags) +{ + char *typename = type_to_string(type); + char scratch[64]; + uint i, count; + + if (flags & NO_HEADER_FLAG) + return; + + /* line 1 */ + fprintf(fp, _("%s quota on %s (%s)\n"), + typename, mount->fs_dir, mount->fs_name); + + /* line 2 */ + for (i = 0; i < 10; i++) + fputc(' ', fp); + if (form & XFS_BLOCK_QUOTA) + fprintf(fp, (flags & HUMAN_FLAG) ? + "%13c %s %13c" : "%20c %s %20c", + ' ', form_to_string(XFS_BLOCK_QUOTA), ' '); + if (form & XFS_INODE_QUOTA) + fprintf(fp, (flags & HUMAN_FLAG) ? + "%13c %s %13c" : "%20c %s %20c", + ' ', form_to_string(XFS_INODE_QUOTA), ' '); + if (form & XFS_RTBLOCK_QUOTA) + fprintf(fp, (flags & HUMAN_FLAG) ? + "%9c %s %9c" : "%15c %s %15c", + ' ', form_to_string(XFS_RTBLOCK_QUOTA), ' '); + fputc('\n', fp); + + /* line 3 */ + snprintf(scratch, sizeof(scratch), "%s ID", typename); + fprintf(fp, "%-10s ", scratch); + if (form & XFS_BLOCK_QUOTA) + fprintf(fp, (flags & HUMAN_FLAG) ? + _(" Used Soft Hard Warn/Grace ") : + _(" Used Soft Hard Warn/Grace ")); + if (form & XFS_INODE_QUOTA) + fprintf(fp, (flags & HUMAN_FLAG) ? + _(" Used Soft Hard Warn/Grace ") : + _(" Used Soft Hard Warn/ Grace ")); + if (form & XFS_RTBLOCK_QUOTA) + fprintf(fp, (flags & HUMAN_FLAG) ? + _(" Used Soft Hard Warn/Grace ") : + _(" Used Soft Hard Warn/Grace ")); + fputc('\n', fp); + + /* line 4 */ + for (i = 0; i < 10; i++) + fputc('-', fp); + fputc(' ', fp); + count = (flags & HUMAN_FLAG) ? 33 : 50; + if (form & XFS_BLOCK_QUOTA) { + for (i = 0; i < count; i++) + fputc('-', fp); + fputc(' ', fp); + } + if (form & XFS_INODE_QUOTA) { + for (i = 0; i < count; i++) + fputc('-', fp); + fputc(' ', fp); + } + if (form & XFS_RTBLOCK_QUOTA) { + for (i = 0; i < count; i++) + fputc('-', fp); + fputc(' ', fp); + } + fputc('\n', fp); +} + +static int +report_mount( + FILE *fp, + __uint32_t id, + char *name, + uint form, + uint type, + fs_path_t *mount, + uint flags) +{ + fs_disk_quota_t d; + char *dev = mount->fs_name; + char c[8], h[8], s[8]; + uint qflags; + int count; + + if (xfsquotactl(XFS_GETQUOTA, dev, type, id, (void *)&d) < 0) + return 0; + + if (flags & TERSE_FLAG) { + count = 0; + if ((form & XFS_BLOCK_QUOTA) && d.d_bcount) + count++; + if ((form & XFS_INODE_QUOTA) && d.d_icount) + count++; + if ((form & XFS_RTBLOCK_QUOTA) && d.d_rtbcount) + count++; + if (!count) + return 0; + } + + if (!(flags & NO_HEADER_FLAG)) + report_header(fp, form, type, mount, flags); + + fprintf(fp, "%-10s", name); + if (form & XFS_BLOCK_QUOTA) { + qflags = (flags & HUMAN_FLAG); + if (d.d_blk_hardlimit && d.d_bcount > d.d_blk_hardlimit) + qflags |= LIMIT_FLAG; + if (d.d_blk_softlimit && d.d_bcount > d.d_blk_softlimit) + qflags |= QUOTA_FLAG; + if (flags & HUMAN_FLAG) + fprintf(fp, " %6s %6s %6s %02d %8s", + bbs_to_string(d.d_bcount, c, sizeof(c)), + bbs_to_string(d.d_blk_softlimit, s, sizeof(s)), + bbs_to_string(d.d_blk_hardlimit, h, sizeof(h)), + d.d_bwarns, + time_to_string(d.d_btimer, qflags)); + else + fprintf(fp, " %10llu %10llu %10llu %02d %9s", + (unsigned long long)d.d_bcount >> 1, + (unsigned long long)d.d_blk_softlimit >> 1, + (unsigned long long)d.d_blk_hardlimit >> 1, + d.d_bwarns, + time_to_string(d.d_btimer, qflags)); + } + if (form & XFS_INODE_QUOTA) { + qflags = (flags & HUMAN_FLAG); + if (d.d_ino_hardlimit && d.d_icount > d.d_ino_hardlimit) + qflags |= LIMIT_FLAG; + if (d.d_ino_softlimit && d.d_icount > d.d_ino_softlimit) + qflags |= QUOTA_FLAG; + if (flags & HUMAN_FLAG) + fprintf(fp, " %6s %6s %6s %02d %8s", + num_to_string(d.d_icount, c, sizeof(c)), + num_to_string(d.d_ino_softlimit, s, sizeof(s)), + num_to_string(d.d_ino_hardlimit, h, sizeof(h)), + d.d_iwarns, + time_to_string(d.d_itimer, qflags)); + else + fprintf(fp, " %10llu %10llu %10llu %02d %9s", + (unsigned long long)d.d_icount, + (unsigned long long)d.d_ino_softlimit, + (unsigned long long)d.d_ino_hardlimit, + d.d_iwarns, + time_to_string(d.d_itimer, qflags)); + } + if (form & XFS_RTBLOCK_QUOTA) { + qflags = (flags & HUMAN_FLAG); + if (d.d_rtb_hardlimit && d.d_rtbcount > d.d_rtb_hardlimit) + qflags |= LIMIT_FLAG; + if (d.d_rtb_softlimit && d.d_rtbcount > d.d_rtb_softlimit) + qflags |= QUOTA_FLAG; + if (flags & HUMAN_FLAG) + fprintf(fp, " %6s %6s %6s %02d %8s", + bbs_to_string(d.d_rtbcount, c, sizeof(c)), + bbs_to_string(d.d_rtb_softlimit, s, sizeof(s)), + bbs_to_string(d.d_rtb_hardlimit, h, sizeof(h)), + d.d_rtbwarns, + time_to_string(d.d_rtbtimer, qflags)); + else + fprintf(fp, " %10llu %10llu %10llu %02d %9s", + (unsigned long long)d.d_rtbcount >> 1, + (unsigned long long)d.d_rtb_softlimit >> 1, + (unsigned long long)d.d_rtb_hardlimit >> 1, + d.d_rtbwarns, + time_to_string(d.d_rtbtimer, qflags)); + } + fputc('\n', fp); + return 1; +} + +static void +report_user_mount( + FILE *fp, + uint form, + fs_path_t *mount, + uint flags) +{ + struct passwd *u; + + setpwent(); + while ((u = getpwent()) != NULL) + if (report_mount(fp, u->pw_uid, u->pw_name, + form, XFS_USER_QUOTA, mount, flags)) + flags |= NO_HEADER_FLAG; + if (flags & NO_HEADER_FLAG) + fputc('\n', fp); + endpwent(); +} + +static void +report_group_mount( + FILE *fp, + uint form, + fs_path_t *mount, + uint flags) +{ + struct group *g; + + setgrent(); + while ((g = getgrent()) != NULL) + if (report_mount(fp, g->gr_gid, g->gr_name, + form, XFS_GROUP_QUOTA, mount, flags)) + flags |= NO_HEADER_FLAG; + if (flags & NO_HEADER_FLAG) + fputc('\n', fp); + endgrent(); +} + +static void +report_project_mount( + FILE *fp, + uint form, + fs_path_t *mount, + uint flags) +{ + fs_project_t *p; + + setprent(); + while ((p = getprent()) != NULL) + if (report_mount(fp, p->pr_prid, p->pr_name, + form, XFS_PROJ_QUOTA, mount, flags)) + flags |= NO_HEADER_FLAG; + if (flags & NO_HEADER_FLAG) + fputc('\n', fp); + endprent(); +} + +static void +report_any_type( + FILE *fp, + uint form, + uint type, + char *dir, + uint flags) +{ + fs_cursor_t cursor; + fs_path_t *mount; + + if (type & XFS_USER_QUOTA) { + fs_cursor_initialise(dir, FS_MOUNT_POINT, &cursor); + while ((mount = fs_cursor_next_entry(&cursor))) + report_user_mount(fp, form, mount, flags); + } + if (type & XFS_GROUP_QUOTA) { + fs_cursor_initialise(dir, FS_MOUNT_POINT, &cursor); + while ((mount = fs_cursor_next_entry(&cursor))) + report_group_mount(fp, form, mount, flags); + } + if (type & XFS_PROJ_QUOTA) { + fs_cursor_initialise(dir, FS_MOUNT_POINT, &cursor); + while ((mount = fs_cursor_next_entry(&cursor))) + report_project_mount(fp, form, mount, flags); + } +} + +static int +report_f( + int argc, + char **argv) +{ + FILE *fp = NULL; + char *fname = NULL; + int c, flags = 0, type = 0, form = 0; + + while ((c = getopt(argc, argv, "abf:hgniprtu")) != EOF) { + switch (c) { + case 'f': + fname = optarg; + break; + case 'b': + form |= XFS_BLOCK_QUOTA; + break; + case 'i': + form |= XFS_INODE_QUOTA; + break; + case 'r': + form |= XFS_RTBLOCK_QUOTA; + break; + case 'g': + type |= XFS_GROUP_QUOTA; + break; + case 'p': + type |= XFS_PROJ_QUOTA; + break; + case 'u': + type |= XFS_USER_QUOTA; + break; + case 'a': + flags |= ALL_MOUNTS_FLAG; + break; + case 'h': + flags |= HUMAN_FLAG; + break; + case 'n': + flags |= NO_HEADER_FLAG; + break; + case 't': + flags |= TERSE_FLAG; + break; + default: + return command_usage(&report_cmd); + } + } + + if (!form) + form = XFS_BLOCK_QUOTA; + + if (!type) + type = XFS_USER_QUOTA | XFS_GROUP_QUOTA | XFS_PROJ_QUOTA; + + if ((fp = fopen_write_secure(fname)) == NULL) + return 0; + + if (argc == optind) + report_any_type(fp, form, type, (flags & ALL_MOUNTS_FLAG) ? + NULL : fs_path->fs_dir, flags); + else while (argc > optind) + report_any_type(fp, form, type, argv[optind++], flags); + + if (fname) + fclose(fp); + return 0; +} + +void +report_init(void) +{ + dump_cmd.name = _("dump"); + dump_cmd.cfunc = dump_f; + dump_cmd.argmin = 0; + dump_cmd.argmax = -1; + dump_cmd.args = _("[-gpu] [-f file]"); + dump_cmd.oneline = _("dump quota information for backup utilities"); + dump_cmd.help = dump_help; + + report_cmd.name = _("report"); + report_cmd.altname = _("repquota"); + report_cmd.cfunc = report_f; + report_cmd.argmin = 0; + report_cmd.argmax = -1; + report_cmd.args = _("[-bir] [-gpu] [-ahnt] [-f file]"); + report_cmd.oneline = _("report filesystem quota information"); + report_cmd.help = report_help; + + if (expert) { + add_command(&dump_cmd); + add_command(&report_cmd); + } +} diff --git a/quota/state.c b/quota/state.c new file mode 100644 index 000000000..32a0533ff --- /dev/null +++ b/quota/state.c @@ -0,0 +1,563 @@ +/* + * Copyright (c) 2005 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + */ + +#include +#include "init.h" +#include "quota.h" + +static cmdinfo_t off_cmd; +static cmdinfo_t state_cmd; +static cmdinfo_t enable_cmd; +static cmdinfo_t disable_cmd; +static cmdinfo_t remove_cmd; + +static void +off_help(void) +{ + printf(_( +"\n" +" turn filesystem quota off, both accounting and enforcement\n" +"\n" +" Example:\n" +" 'off -uv' (switch off user quota on the current filesystem)\n" +" This command is the equivalent of the traditional quotaoff command,\n" +" which disables quota completely on a mounted filesystem.\n" +" Note that there is no 'on' command - for XFS filesystems (with the\n" +" exception of the root filesystem on IRIX) quota can only be enabled\n" +" at mount time, through the use of one of the quota mount options.\n" +"\n" +" The state command is useful for displaying the current state. Using\n" +" the -v (verbose) option with the 'off' command will display the quota\n" +" state for the affected filesystem once the operation is complete.\n" +" The affected quota type is -g (groups), -p (projects) or -u (users)\n" +" and defaults to user quota (multiple types can be specified).\n" +"\n")); +} + +static void +state_help(void) +{ + printf(_( +"\n" +" query the state of quota on the current filesystem\n" +"\n" +" This is a verbose status command, reporting whether or not accounting\n" +" and/or enforcement are enabled for a filesystem, which inodes are in\n" +" use as the quota state inodes, and how many extents and blocks are\n" +" presently being used to hold that information.\n" +" The quota type is specified via -g (groups), -p (projects) or -u (users)\n" +" and defaults to user quota (multiple types can be specified).\n" +"\n")); +} + +static void +enable_help(void) +{ + printf(_( +"\n" +" enable quota enforcement on a filesystem\n" +"\n" +" If a filesystem is mounted and has quota accounting enabled, but not\n" +" quota enforcement, enforcement can be enabled with this command.\n" +" With the -v (verbose) option, the status of the filesystem will be\n" +" reported after the operation is complete.\n" +" The affected quota type is -g (groups), -p (projects) or -u (users)\n" +" and defaults to user quota (multiple types can be specified).\n" +"\n")); +} + +static void +disable_help(void) +{ + printf(_( +"\n" +" disable quota enforcement on a filesystem\n" +"\n" +" If a filesystem is mounted and is currently enforcing quota, this\n" +" provides a mechanism to switch off the enforcement, but continue to\n" +" perform used space (and used inodes) accounting.\n" +" The affected quota type is -g (groups), -p (projects) or -u (users).\n" +"\n")); +} + +static void +remove_help(void) +{ + printf(_( +"\n" +" remove any space being used by the quota subsystem\n" +"\n" +" Once quota has been switched 'off' on a filesystem, the space that\n" +" was allocated to holding quota metadata can be freed via this command.\n" +" The affected quota type is -g (groups), -p (projects) or -u (users)\n" +" and defaults to user quota (multiple types can be specified).\n" +"\n")); +} + +static void +state_qfilestat( + FILE *fp, + fs_path_t *mount, + uint type, + fs_qfilestat_t *qfs, + int accounting, + int enforcing) +{ + fprintf(fp, _("%s quota state on %s (%s)\n"), type_to_string(type), + mount->fs_dir, mount->fs_name); + fprintf(fp, _(" Accounting: %s\n"), accounting ? _("ON") : _("OFF")); + fprintf(fp, _(" Enforcement: %s\n"), enforcing ? _("ON") : _("OFF")); + fprintf(fp, _(" Inode: #%llu (%llu blocks, %lu extents)\n"), + (unsigned long long)qfs->qfs_ino, + (unsigned long long)qfs->qfs_nblks, + (unsigned long)qfs->qfs_nextents); +} + +static void +state_timelimit( + FILE *fp, + uint form, + __uint32_t timelimit) +{ + fprintf(fp, _("%s grace time: %s\n"), + form_to_string(form), + time_to_string(timelimit, VERBOSE_FLAG | ABSOLUTE_FLAG)); +} + +static void +state_quotafile_mount( + FILE *fp, + uint type, + fs_path_t *mount, + uint flags) +{ + fs_quota_stat_t s; + char *dev = mount->fs_name; + + if (xfsquotactl(XFS_GETQSTAT, dev, type, 0, (void *)&s) < 0) { + if (flags & VERBOSE_FLAG) + fprintf(fp, _("%s quota are not enabled on %s\n"), + type_to_string(type), dev); + return; + } + + if (type & XFS_USER_QUOTA) + state_qfilestat(fp, mount, XFS_USER_QUOTA, &s.qs_uquota, + s.qs_flags & XFS_QUOTA_UDQ_ACCT, + s.qs_flags & XFS_QUOTA_UDQ_ENFD); + if (type & XFS_GROUP_QUOTA) + state_qfilestat(fp, mount, XFS_GROUP_QUOTA, &s.qs_gquota, + s.qs_flags & XFS_QUOTA_GDQ_ACCT, + s.qs_flags & XFS_QUOTA_GDQ_ENFD); + if (type & XFS_PROJ_QUOTA) + state_qfilestat(fp, mount, XFS_PROJ_QUOTA, &s.qs_gquota, + s.qs_flags & XFS_QUOTA_PDQ_ACCT, + s.qs_flags & XFS_QUOTA_PDQ_ENFD); + + state_timelimit(fp, XFS_BLOCK_QUOTA, s.qs_btimelimit); + state_timelimit(fp, XFS_INODE_QUOTA, s.qs_itimelimit); + state_timelimit(fp, XFS_RTBLOCK_QUOTA, s.qs_rtbtimelimit); +} + +static void +state_quotafile( + FILE *fp, + uint type, + char *dir, + uint flags) +{ + fs_cursor_t cursor; + fs_path_t *mount; + + fs_cursor_initialise(dir, FS_MOUNT_POINT, &cursor); + while ((mount = fs_cursor_next_entry(&cursor))) + state_quotafile_mount(fp, type, mount, flags); +} + +static int +state_f( + int argc, + char **argv) +{ + FILE *fp = NULL; + char *fname = NULL; + int c, flags = 0, type = 0; + + while ((c = getopt(argc, argv, "af:gpuv")) != EOF) { + switch (c) { + case 'a': + flags |= ALL_MOUNTS_FLAG; + break; + case 'f': + fname = optarg; + break; + case 'g': + type |= XFS_GROUP_QUOTA; + break; + case 'p': + type |= XFS_PROJ_QUOTA; + break; + case 'u': + type |= XFS_USER_QUOTA; + break; + case 'v': + flags |= VERBOSE_FLAG; + break; + default: + return command_usage(&state_cmd); + } + } + + if (argc != optind) + return command_usage(&state_cmd); + + if ((fp = fopen_write_secure(fname)) == NULL) + return 0; + + if (!type) + type = XFS_USER_QUOTA | XFS_GROUP_QUOTA | XFS_PROJ_QUOTA; + + state_quotafile(fp, type, (flags & ALL_MOUNTS_FLAG) ? + NULL : fs_path->fs_dir, flags); + + if (fname) + fclose(fp); + return 0; +} + +static void +enable_enforcement( + char *dir, + uint type, + uint qflags, + uint flags) +{ + fs_path_t *mount; + fs_quota_stat_t qstat = + { .qs_version = FS_QSTAT_VERSION, .qs_flags = qflags }; + + mount = fs_table_lookup(dir, FS_MOUNT_POINT); + if (!mount) { + fprintf(stderr, "%s: unknown mount point %s\n", progname, dir); + return; + } + dir = mount->fs_name; + if (xfsquotactl(XFS_QUOTAON, dir, type, 0, (void *)&qstat) < 0) + perror("XFS_QUOTAON"); + else if (flags & VERBOSE_FLAG) + state_quotafile_mount(stdout, type, mount, flags); +} + +static void +disable_enforcement( + char *dir, + uint type, + uint qflags, + uint flags) +{ + fs_path_t *mount; + fs_quota_stat_t qstat = + { .qs_version = FS_QSTAT_VERSION, .qs_flags = qflags }; + + mount = fs_table_lookup(dir, FS_MOUNT_POINT); + if (!mount) { + fprintf(stderr, "%s: unknown mount point %s\n", progname, dir); + return; + } + dir = mount->fs_name; + if (xfsquotactl(XFS_QUOTAOFF, dir, type, 0, (void *)&qstat) < 0) + perror("XFS_QUOTAOFF"); + else if (flags & VERBOSE_FLAG) + state_quotafile_mount(stdout, type, mount, flags); +} + +static void +quotaoff( + char *dir, + uint type, + uint qflags, + uint flags) +{ + fs_path_t *mount; + fs_quota_stat_t qstat = + { .qs_version = FS_QSTAT_VERSION, .qs_flags = qflags }; + + mount = fs_table_lookup(dir, FS_MOUNT_POINT); + if (!mount) { + fprintf(stderr, "%s: unknown mount point %s\n", progname, dir); + return; + } + dir = mount->fs_name; + if (xfsquotactl(XFS_QUOTAOFF, dir, type, 0, (void *)&qstat) < 0) + perror("XFS_QUOTAOFF"); + else if (flags & VERBOSE_FLAG) + state_quotafile_mount(stdout, type, mount, flags); +} + +static void +remove_extents( + char *dir, + uint type, + uint qflags, + uint flags) +{ + fs_path_t *mount; + fs_quota_stat_t qstat = + { .qs_version = FS_QSTAT_VERSION, .qs_flags = qflags }; + + mount = fs_table_lookup(dir, FS_MOUNT_POINT); + if (!mount) { + fprintf(stderr, "%s: unknown mount point %s\n", progname, dir); + return; + } + dir = mount->fs_name; + if (xfsquotactl(XFS_QUOTARM, dir, type, 0, (void *)&qstat) < 0) + perror("XFS_QUOTARM"); + else if (flags & VERBOSE_FLAG) + state_quotafile_mount(stdout, type, mount, flags); +} + +static int +enable_f( + int argc, + char **argv) +{ + int c, flags = 0, qflags = 0, type = 0; + + while ((c = getopt(argc, argv, "gpuv")) != EOF) { + switch (c) { + case 'g': + type |= XFS_GROUP_QUOTA; + qflags |= XFS_QUOTA_GDQ_ACCT | XFS_QUOTA_GDQ_ENFD; + break; + case 'p': + type |= XFS_PROJ_QUOTA; + qflags |= XFS_QUOTA_PDQ_ACCT | XFS_QUOTA_PDQ_ENFD; + break; + case 'u': + type |= XFS_USER_QUOTA; + qflags |= XFS_QUOTA_UDQ_ACCT | XFS_QUOTA_UDQ_ENFD; + break; + case 'v': + flags |= VERBOSE_FLAG; + break; + default: + return command_usage(&enable_cmd); + } + } + + if (argc != optind) + return command_usage(&enable_cmd); + + if (!flags) { + type |= XFS_USER_QUOTA; + qflags |= XFS_QUOTA_UDQ_ACCT | XFS_QUOTA_UDQ_ENFD; + } + + enable_enforcement(fs_path->fs_dir, type, qflags, flags); + return 0; +} + +static int +disable_f( + int argc, + char **argv) +{ + int c, flags = 0, qflags = 0, type = 0; + + while ((c = getopt(argc, argv, "gpuv")) != EOF) { + switch (c) { + case 'g': + type |= XFS_GROUP_QUOTA; + qflags |= XFS_QUOTA_GDQ_ACCT; + break; + case 'p': + type |= XFS_PROJ_QUOTA; + qflags |= XFS_QUOTA_PDQ_ACCT; + break; + case 'u': + type |= XFS_USER_QUOTA; + qflags |= XFS_QUOTA_UDQ_ACCT; + break; + case 'v': + flags |= VERBOSE_FLAG; + break; + default: + return command_usage(&disable_cmd); + } + } + + if (argc != optind) + return command_usage(&disable_cmd); + + if (!flags) { + type |= XFS_USER_QUOTA; + qflags |= XFS_QUOTA_UDQ_ACCT; + } + + disable_enforcement(fs_path->fs_dir, type, qflags, flags); + return 0; +} + +static int +off_f( + int argc, + char **argv) +{ + int c, flags = 0, qflags = 0, type = 0; + + while ((c = getopt(argc, argv, "gpuv")) != EOF) { + switch (c) { + case 'g': + type |= XFS_GROUP_QUOTA; + qflags |= XFS_QUOTA_GDQ_ACCT | XFS_QUOTA_GDQ_ENFD; + break; + case 'p': + type |= XFS_PROJ_QUOTA; + qflags |= XFS_QUOTA_PDQ_ACCT | XFS_QUOTA_PDQ_ENFD; + break; + case 'u': + type |= XFS_USER_QUOTA; + qflags |= XFS_QUOTA_UDQ_ACCT | XFS_QUOTA_UDQ_ENFD; + break; + case 'v': + flags |= VERBOSE_FLAG; + break; + default: + return command_usage(&off_cmd); + } + } + + if (argc != optind) + return command_usage(&off_cmd); + + if (!flags) { + type |= XFS_USER_QUOTA; + qflags |= XFS_QUOTA_UDQ_ACCT | XFS_QUOTA_UDQ_ENFD; + } + + quotaoff(fs_path->fs_dir, type, qflags, flags); + return 0; +} + +static int +remove_f( + int argc, + char **argv) +{ + int c, flags = 0, qflags = 0, type = 0; + + while ((c = getopt(argc, argv, "gpuv")) != EOF) { + switch (c) { + case 'g': + type |= XFS_GROUP_QUOTA; + qflags |= XFS_QUOTA_GDQ_ACCT | XFS_QUOTA_GDQ_ENFD; + break; + case 'p': + type |= XFS_PROJ_QUOTA; + qflags |= XFS_QUOTA_PDQ_ACCT | XFS_QUOTA_PDQ_ENFD; + break; + case 'u': + type |= XFS_USER_QUOTA; + qflags |= XFS_QUOTA_UDQ_ACCT | XFS_QUOTA_UDQ_ENFD; + break; + case 'v': + flags |= VERBOSE_FLAG; + break; + default: + return command_usage(&remove_cmd); + } + } + + if (argc != optind) + return command_usage(&remove_cmd); + + if (!flags) { + type |= XFS_USER_QUOTA; + qflags |= XFS_QUOTA_UDQ_ACCT | XFS_QUOTA_UDQ_ENFD; + } + + remove_extents(fs_path->fs_dir, type, qflags, flags); + return 0; +} + +void +state_init(void) +{ + off_cmd.name = _("off"); + off_cmd.cfunc = off_f; + off_cmd.argmin = 0; + off_cmd.argmax = -1; + off_cmd.args = _("[-gpu] [-v]"); + off_cmd.oneline = _("permanently switch quota off for a path"); + off_cmd.help = off_help; + + state_cmd.name = _("state"); + state_cmd.cfunc = state_f; + state_cmd.argmin = 0; + state_cmd.argmax = -1; + state_cmd.args = _("[-gpu] [-f file]"); + state_cmd.oneline = _("get overall quota state information"); + state_cmd.help = state_help; + + enable_cmd.name = _("enable"); + enable_cmd.cfunc = enable_f; + enable_cmd.argmin = 0; + enable_cmd.argmax = -1; + enable_cmd.args = _("[-gpu] [-v]"); + enable_cmd.oneline = _("enable quota enforcement"); + enable_cmd.help = enable_help; + + disable_cmd.name = _("disable"); + disable_cmd.cfunc = disable_f; + disable_cmd.argmin = 0; + disable_cmd.argmax = -1; + disable_cmd.args = _("[-gpu] [-v]"); + disable_cmd.oneline = _("disable quota enforcement"); + disable_cmd.help = disable_help; + + remove_cmd.name = _("remove"); + remove_cmd.cfunc = remove_f; + remove_cmd.argmin = 0; + remove_cmd.argmax = -1; + remove_cmd.args = _("[-gpu] [-v]"); + remove_cmd.oneline = _("remove quota extents from a filesystem"); + remove_cmd.help = remove_help; + + if (expert) { + add_command(&off_cmd); + add_command(&state_cmd); + add_command(&enable_cmd); + add_command(&disable_cmd); + add_command(&remove_cmd); + } +} diff --git a/quota/util.c b/quota/util.c new file mode 100644 index 000000000..7b9c1f9e2 --- /dev/null +++ b/quota/util.c @@ -0,0 +1,436 @@ +/* + * Copyright (c) 2005 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + */ + +#include +#include +#include +#include "quota.h" + +#define SECONDS_IN_A_DAY (24 * 60 * 60) +#define SECONDS_IN_A_HOUR (60 * 60) +#define SECONDS_IN_A_MINUTE (60) + +char * +time_to_string( + __uint32_t origin, + uint flags) +{ + static char timestamp[32]; + time_t now, timer; + uint days, hours, minutes, seconds; + + if (flags & ABSOLUTE_FLAG) { + timer = origin; + } else { + time(&now); + timer = MAX(origin - now, 0); + } + days = timer / SECONDS_IN_A_DAY; + if (days) + timer %= SECONDS_IN_A_DAY; + hours = timer / SECONDS_IN_A_HOUR; + if (hours) + timer %= SECONDS_IN_A_HOUR; + minutes = timer / SECONDS_IN_A_MINUTE; + seconds = timer % SECONDS_IN_A_MINUTE; + + if (flags & LIMIT_FLAG) { + snprintf(timestamp, sizeof(timestamp), (flags & HUMAN_FLAG) ? + _("[-none-]") : _("[--none--]")); + } else if (origin == 0) { + snprintf(timestamp, sizeof(timestamp), (flags & HUMAN_FLAG) ? + _("[------]") : _("[--------]")); + } else if ((hours == 0 && minutes == 0 && seconds == 0) || + (!(flags & VERBOSE_FLAG) && days > 0)) { + snprintf(timestamp, sizeof(timestamp), "[%u %s]", + days, days == 1 ? _("day") : _("days")); + } else if (flags & VERBOSE_FLAG) { + snprintf(timestamp, sizeof(timestamp), "[%u %s %02u:%02u:%02u]", + days, days == 1 ? _("day") : _("days"), + hours, minutes, seconds); + } else { /* non-verbose, less than a day remaining */ + snprintf(timestamp, sizeof(timestamp), + (flags & HUMAN_FLAG) ? + "%02u:%02u:%02u" : "[%02u:%02u:%02u]", + hours, minutes, seconds); + } + return timestamp; +} + +static int +round_snprintf( + char *sp, + size_t size, + const char *fmt_round, + const char *fmt_not_round, + __uint64_t value, + __uint64_t divisor) +{ + double v = (double)value / divisor; + + value /= divisor; + if (v == (double)value) + return snprintf(sp, size, fmt_round, (uint)value); + else + return snprintf(sp, size, fmt_not_round, v); +} + +/* Basic blocks (512) bytes are returned from quotactl */ +#define BBS_TO_EXABYTES(bbs) ((__uint64_t)(bbs)>>51) +#define BBS_TO_PETABYTES(bbs) ((__uint64_t)(bbs)>>41) +#define BBS_TO_TERABYTES(bbs) ((__uint64_t)(bbs)>>31) +#define BBS_TO_GIGABYTES(bbs) ((__uint64_t)(bbs)>>21) +#define BBS_TO_MEGABYTES(bbs) ((__uint64_t)(bbs)>>11) +#define BBS_TO_KILOBYTES(bbs) ((__uint64_t)(bbs)>>1) + +#define BBEXABYTE ((__uint64_t)1<<51) +#define BBPETABYTE ((__uint64_t)1<<41) +#define BBTERABYTE ((__uint64_t)1<<31) +#define BBGIGABYTE ((__uint64_t)1<<21) +#define BBMEGABYTE ((__uint64_t)1<<11) +#define BBKILOBYTE ((__uint64_t)1<< 1) + +char * +bbs_to_string( + __uint64_t v, + char *sp, + uint size) +{ + if (v == 0) + snprintf(sp, size, "%4u", (uint)v); + else if (BBS_TO_EXABYTES(v)) + round_snprintf(sp, size, "%3uE", "%3.1fE", v, BBEXABYTE); + else if (BBS_TO_PETABYTES(v)) + round_snprintf(sp, size, "%3uP", "%3.1fP", v, BBPETABYTE); + else if (BBS_TO_TERABYTES(v)) + round_snprintf(sp, size, "%3uT", "%3.1fT", v, BBTERABYTE); + else if (BBS_TO_GIGABYTES(v)) + round_snprintf(sp, size, "%3uG", "%3.1fG", v, BBGIGABYTE); + else if (BBS_TO_MEGABYTES(v)) + round_snprintf(sp, size, "%3uM", "%3.1fM", v, BBMEGABYTE); + else if (BBS_TO_KILOBYTES(v)) + round_snprintf(sp, size, "%3uK", "%3.1fK", v, BBKILOBYTE); + else + snprintf(sp, size, "%4u", (uint)v << BBSHIFT); /* bytes */ + return sp; +} + +#define THOUSAND ((__uint64_t)1000) +#define MILLION ((__uint64_t)1000*1000) +#define BILLION ((__uint64_t)1000*1000*1000) +#define TRILLION ((__uint64_t)1000*1000*1000*1000) +#define GAZILLION ((__uint64_t)1000*1000*1000*1000*1000) +#define RIDICULOUS ((__uint64_t)1000*1000*1000*1000*1000*1000) +#define STOPALREADY ((__uint64_t)1000*1000*1000*1000*1000*1000*1000) + +char * +num_to_string( + __uint64_t v, + char *sp, + uint size) +{ + if (v == 0) + snprintf(sp, size, "%4u", (uint)v); + else if (v > STOPALREADY) + round_snprintf(sp, size, "%3us", "%3.1fs", v, STOPALREADY); + else if (v > RIDICULOUS) + round_snprintf(sp, size, "%3ur", "%3.1fr", v, RIDICULOUS); + else if (v > GAZILLION) + round_snprintf(sp, size, "%3ug", "%3.1fg", v, GAZILLION); + else if (v > TRILLION) + round_snprintf(sp, size, "%3ut", "%3.1ft", v, TRILLION); + else if (v > BILLION) + round_snprintf(sp, size, "%3ub", "%3.1fb", v, BILLION); + else if (v > MILLION) + round_snprintf(sp, size, "%3um", "%3.1fm", v, MILLION); + else if (v > THOUSAND) + round_snprintf(sp, size, "%3uk", "%3.1fk", v, THOUSAND); + else + snprintf(sp, size, "%4u", (uint)v); + return sp; +} + +char * +pct_to_string( + __uint64_t v, + __uint64_t t, + char *sp, + uint size) +{ + if (t == 0 || v == 0) + snprintf(sp, size, "%3u", (uint)0); + else if (t == v) + snprintf(sp, size, "%3u", (uint)100); + else + snprintf(sp, size, "%3u", (uint)(((double)v / t) * 100 + 1)); + return sp; +} + +char * +form_to_string( + uint form) +{ + static char *forms[] = { + _("Blocks"), _("Inodes"), _("Realtime Blocks") }; + + if (form & XFS_BLOCK_QUOTA) + return forms[0]; + if (form & XFS_INODE_QUOTA) + return forms[1]; + if (form & XFS_RTBLOCK_QUOTA) + return forms[2]; + return NULL; +} + +char * +type_to_string( + uint type) +{ + static char *types[] = { _("User"), _("Group"), _("Project") }; + + if (type & XFS_USER_QUOTA) + return types[0]; + if (type & XFS_GROUP_QUOTA) + return types[1]; + if (type & XFS_PROJ_QUOTA) + return types[2]; + return NULL; +} + + +/* + * Identifier caches - user/group/project names/IDs + */ + +#define NID 4096 +#define IDMASK (NID-1) + +typedef struct { + __uint32_t id; + char name[UT_NAMESIZE + 1]; +} idcache_t; + +static idcache_t uidnc[NID]; +static idcache_t gidnc[NID]; +static idcache_t pidnc[NID]; +static int uentriesleft = NID; +static int gentriesleft = NID; +static int pentriesleft = NID; + +static idcache_t * +getnextpwent( + __uint32_t id, + int byid) +{ + struct passwd *pw; + static idcache_t idc; + + /* /etc/passwd */ + if ((pw = byid? getpwuid(id) : getpwent()) == NULL) + return NULL; + idc.id = pw->pw_uid; + strncpy(idc.name, pw->pw_name, UT_NAMESIZE); + return &idc; +} + +static idcache_t * +getnextgrent( + __uint32_t id, + int byid) +{ + struct group *gr; + static idcache_t idc; + + if ((gr = byid? getgrgid(id) : getgrent()) == NULL) + return NULL; + idc.id = gr->gr_gid; + strncpy(idc.name, gr->gr_name, UT_NAMESIZE); + return &idc; +} + +static idcache_t * +getnextprent( + __uint32_t id, + int byid) +{ + fs_project_t *pr; + static idcache_t idc; + + if ((pr = byid? getprprid(id) : getprent()) == NULL) + return NULL; + idc.id = pr->pr_prid; + strncpy(idc.name, pr->pr_name, UT_NAMESIZE); + return &idc; +} + +char * +uid_to_name( + __uint32_t id) +{ + idcache_t *ncp, *idp; + + /* Check cache for name first */ + ncp = &uidnc[id & IDMASK]; + if (ncp->id == id && ncp->name[0]) + return ncp->name; + if (uentriesleft) { + /* + * Fill this cache while seaching for a name. + * This lets us run through the file serially. + */ + if (uentriesleft == NID) + setpwent(); + while (((idp = getnextpwent(id, 0)) != NULL) && uentriesleft) { + uentriesleft--; + ncp = &uidnc[idp->id & IDMASK]; + if (ncp->name[0] == '\0' || idp->id == id) + memcpy(ncp, idp, sizeof(idcache_t)); + if (idp->id == id) + return ncp->name; + } + endpwent(); + uentriesleft = 0; + ncp = &uidnc[id & IDMASK]; + } + + /* Not cached - do it the slow way & insert into cache */ + if ((idp = getnextpwent(id, 1)) == NULL) + return NULL; + memcpy(ncp, idp, sizeof(idcache_t)); + return ncp->name; +} + +char * +gid_to_name( + __uint32_t id) +{ + idcache_t *ncp, *idp; + + /* Check cache for name first */ + ncp = &gidnc[id & IDMASK]; + if (ncp->id == id && ncp->name[0]) + return ncp->name; + if (gentriesleft) { + /* + * Fill this cache while seaching for a name. + * This lets us run through the file serially. + */ + if (gentriesleft == NID) + setgrent(); + while (((idp = getnextgrent(id, 0)) != NULL) && gentriesleft) { + gentriesleft--; + ncp = &gidnc[idp->id & IDMASK]; + if (ncp->name[0] == '\0' || idp->id == id) + memcpy(ncp, idp, sizeof(idcache_t)); + if (idp->id == id) + return ncp->name; + } + endgrent(); + gentriesleft = 0; + ncp = &gidnc[id & IDMASK]; + } + + /* Not cached - do it the slow way & insert into cache */ + if ((idp = getnextgrent(id, 1)) == NULL) + return NULL; + memcpy(ncp, idp, sizeof(idcache_t)); + return ncp->name; +} + +char * +prid_to_name( + __uint32_t id) +{ + idcache_t *ncp, *idp; + + /* Check cache for name first */ + ncp = &pidnc[id & IDMASK]; + if (ncp->id == id && ncp->name[0]) + return ncp->name; + if (pentriesleft) { + /* + * Fill this cache while seaching for a name. + * This lets us run through the file serially. + */ + if (pentriesleft == NID) + setprent(); + while (((idp = getnextprent(id, 0)) != NULL) && pentriesleft) { + pentriesleft--; + ncp = &pidnc[idp->id & IDMASK]; + if (ncp->name[0] == '\0' || idp->id == id) + memcpy(ncp, idp, sizeof(idcache_t)); + if (idp->id == id) + return ncp->name; + } + endprent(); + pentriesleft = 0; + ncp = &pidnc[id & IDMASK]; + } + + /* Not cached - do it the slow way & insert into cache */ + if ((idp = getnextprent(id, 1)) == NULL) + return NULL; + memcpy(ncp, idp, sizeof(idcache_t)); + return ncp->name; +} + + +/* + * Utility routine for opening an output file so that it can + * be "securely" written to (i.e. without vulnerability to a + * symlink attack). + * + * Returns NULL on failure, stdout on NULL input. + */ +FILE * +fopen_write_secure( + char *fname) +{ + FILE *fp; + int fd; + + if (!fname) + return stdout; + + if ((fd = open(fname, O_CREAT|O_WRONLY|O_EXCL, 0600)) < 0) { + fprintf(stderr, _("%s: open on %s failed: %s\n"), + progname, fname, strerror(errno)); + return NULL; + } + if ((fp = fdopen(fd, "w")) == NULL) { + fprintf(stderr, _("%s: fdopen on %s failed: %s\n"), + progname, fname, strerror(errno)); + close(fd); + return NULL; + } + return fp; +}