Merge of master-melb:xfs-cmds:22274a by kenmcd.
--- /dev/null
+#
+# 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:
--- /dev/null
+/*
+ * 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 <sys/quota.h>
+
+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;
+}
--- /dev/null
+/*
+ * 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 <pwd.h>
+#include <grp.h>
+#include <ctype.h>
+#include <xfs/input.h>
+#include <xfs/command.h>
+#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 = <device>
+ * <numeric id> 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);
+ }
+}
--- /dev/null
+/*
+ * 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 <xfs/command.h>
+#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);
+}
--- /dev/null
+/*
+ * 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 <sys/quota.h>
+
+int
+xfsquotactl(
+ int command,
+ const char *device,
+ uint type,
+ uint id,
+ void *addr)
+{
+ errno = -ENOSYS;
+ return -1;
+}
--- /dev/null
+/*
+ * 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 <xfs/path.h>
+#include <xfs/command.h>
+#include <xfs/input.h>
+#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;
+}
--- /dev/null
+/*
+ * 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 *);
--- /dev/null
+/*
+ * 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 <sys/quota.h>
+
+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);
+}
--- /dev/null
+/*
+ * 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 <sys/quota.h>
+
+#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);
+}
--- /dev/null
+/*
+ * 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 <xfs/command.h>
+#include <xfs/input.h>
+#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);
+}
--- /dev/null
+/*
+ * 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 <xfs/command.h>
+#include <xfs/input.h>
+#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);
+}
--- /dev/null
+/*
+ * 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 <xfs/command.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <grp.h>
+#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);
+}
--- /dev/null
+/*
+ * 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 <xfs/command.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <grp.h>
+#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);
+}
--- /dev/null
+/*
+ * 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 <xfs/xqm.h>
+#include <xfs/path.h>
+#include <xfs/project.h>
+
+/*
+ * 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);
+
--- /dev/null
+/*
+ * 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 <xfs/command.h>
+#include <pwd.h>
+#include <grp.h>
+#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);
+ }
+}
--- /dev/null
+/*
+ * 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 <xfs/command.h>
+#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);
+ }
+}
--- /dev/null
+/*
+ * 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 <pwd.h>
+#include <grp.h>
+#include <utmp.h>
+#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;
+}