]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
Initial version of xfs quota utility. Knows how to do user/group/project quota,...
authorNathan Scott <nathans@sgi.com>
Tue, 19 Apr 2005 15:00:30 +0000 (15:00 +0000)
committerNathan Scott <nathans@sgi.com>
Tue, 19 Apr 2005 15:00:30 +0000 (15:00 +0000)
Merge of master-melb:xfs-cmds:22274a by kenmcd.

17 files changed:
quota/Makefile [new file with mode: 0644]
quota/darwin.c [new file with mode: 0644]
quota/edit.c [new file with mode: 0644]
quota/free.c [new file with mode: 0644]
quota/freebsd.c [new file with mode: 0644]
quota/init.c [new file with mode: 0644]
quota/init.h [new file with mode: 0644]
quota/irix.c [new file with mode: 0644]
quota/linux.c [new file with mode: 0644]
quota/path.c [new file with mode: 0644]
quota/project.c [new file with mode: 0644]
quota/quot.c [new file with mode: 0644]
quota/quota.c [new file with mode: 0644]
quota/quota.h [new file with mode: 0644]
quota/report.c [new file with mode: 0644]
quota/state.c [new file with mode: 0644]
quota/util.c [new file with mode: 0644]

diff --git a/quota/Makefile b/quota/Makefile
new file mode 100644 (file)
index 0000000..894fe8a
--- /dev/null
@@ -0,0 +1,66 @@
+#
+# Copyright (c) 2005 Silicon Graphics, Inc.  All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like.  Any license provided herein, whether implied or
+# otherwise, applies only to this software file.  Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write the Free Software Foundation, Inc., 59
+# Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+# Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+# Mountain View, CA  94043, or:
+#
+# http://www.sgi.com
+#
+# For further information regarding this notice, see:
+#
+# http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+#
+
+TOPDIR = ..
+include $(TOPDIR)/include/builddefs
+
+LTCOMMAND = xfs_quota
+HFILES = init.h quota.h
+CFILES = init.c util.c \
+       edit.c free.c path.c project.c quot.c quota.c report.c state.c
+
+CFILES += $(PKG_PLATFORM).c
+PCFILES = darwin.c freebsd.c irix.c linux.c
+LSRCFILES = $(shell echo $(PCFILES) | sed -e "s/$(PKG_PLATFORM).c//g")
+
+LLDLIBS = $(LIBXCMD)
+LTDEPENDENCIES = $(LIBXCMD)
+LLDFLAGS = -static
+
+ifeq ($(ENABLE_READLINE),yes)
+LLDLIBS += $(LIBREADLINE) $(LIBTERMCAP)
+CFLAGS += -DENABLE_READLINE
+endif
+
+ifeq ($(ENABLE_EDITLINE),yes)
+LLDLIBS += $(LIBEDITLINE) $(LIBTERMCAP)
+CFLAGS += -DENABLE_EDITLINE
+endif
+
+default: $(LTCOMMAND)
+
+include $(BUILDRULES)
+
+install: default
+       $(INSTALL) -m 755 -d $(PKG_BIN_DIR)
+       $(LTINSTALL) -m 755 $(LTCOMMAND) $(PKG_BIN_DIR)
+install-dev:
diff --git a/quota/darwin.c b/quota/darwin.c
new file mode 100644 (file)
index 0000000..b1d6fe5
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2005 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.         Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "quota.h"
+#include <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;
+}
diff --git a/quota/edit.c b/quota/edit.c
new file mode 100644 (file)
index 0000000..5c52e35
--- /dev/null
@@ -0,0 +1,765 @@
+/*
+ * Copyright (c) 2005 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include <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);
+       }
+}
diff --git a/quota/free.c b/quota/free.c
new file mode 100644 (file)
index 0000000..df42954
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ * Copyright (c) 2005 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include <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);
+}
diff --git a/quota/freebsd.c b/quota/freebsd.c
new file mode 100644 (file)
index 0000000..e8c10dc
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2005 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.         Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "quota.h"
+#include <sys/quota.h>
+
+int
+xfsquotactl(
+       int             command,
+       const char      *device,
+       uint            type,
+       uint            id,
+       void            *addr)
+{
+       errno = -ENOSYS;
+       return -1;
+}
diff --git a/quota/init.c b/quota/init.c
new file mode 100644 (file)
index 0000000..b3ddead
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2003-2005 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include <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;
+}
diff --git a/quota/init.h b/quota/init.h
new file mode 100644 (file)
index 0000000..476f34b
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2003-2005 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+extern char    *progname;
+extern int     exitcode;
+extern int     expert;
+
+extern void    edit_init(void);
+extern void    free_init(void);
+extern void    path_init(void);
+extern void    project_init(void);
+extern void    quot_init(void);
+extern void    quota_init(void);
+extern void    report_init(void);
+extern void    state_init(void);
+
+extern void init_cvtnum(int *, int *);
diff --git a/quota/irix.c b/quota/irix.c
new file mode 100644 (file)
index 0000000..e568f30
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2005 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.         Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "quota.h"
+#include <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);
+}
diff --git a/quota/linux.c b/quota/linux.c
new file mode 100644 (file)
index 0000000..9cbc3fe
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2005 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.         Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "quota.h"
+#include <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);
+}
diff --git a/quota/path.c b/quota/path.c
new file mode 100644 (file)
index 0000000..7b985fb
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2005 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include <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);
+}
diff --git a/quota/project.c b/quota/project.c
new file mode 100644 (file)
index 0000000..d998c80
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2005 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include <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);
+}
diff --git a/quota/quot.c b/quota/quot.c
new file mode 100644 (file)
index 0000000..068c409
--- /dev/null
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 2005 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include <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(&quot_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(&quot_cmd);
+}
diff --git a/quota/quota.c b/quota/quota.c
new file mode 100644 (file)
index 0000000..8af5fac
--- /dev/null
@@ -0,0 +1,461 @@
+/*
+ * Copyright (c) 2005 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include <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(&quota_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(&quota_cmd);
+}
diff --git a/quota/quota.h b/quota/quota.h
new file mode 100644 (file)
index 0000000..818427e
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2005 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include <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);
+
diff --git a/quota/report.c b/quota/report.c
new file mode 100644 (file)
index 0000000..0d297e9
--- /dev/null
@@ -0,0 +1,545 @@
+/*
+ * Copyright (c) 2005 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include <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);
+       }
+}
diff --git a/quota/state.c b/quota/state.c
new file mode 100644 (file)
index 0000000..32a0533
--- /dev/null
@@ -0,0 +1,563 @@
+/*
+ * Copyright (c) 2005 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include <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);
+       }
+}
diff --git a/quota/util.c b/quota/util.c
new file mode 100644 (file)
index 0000000..7b9c1f9
--- /dev/null
@@ -0,0 +1,436 @@
+/*
+ * Copyright (c) 2005 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include <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;
+}