From 9217ac4c9b563951b94b020660644c9579f4ed27 Mon Sep 17 00:00:00 2001
From: Arkadiusz Miskiewicz <arekm@maven.pl>
Date: Tue, 16 Dec 2008 17:12:55 +0100
Subject: find: "XFS project quota" support

XFS filesystem provides three different quota types. UID-based,
GID-based and "project quota" path name based.

Add support for test parameter "-xfsprojquota N" which allows
looking for files with specified project quota ID.

Add support for "%q" format which allows project quota ID
to be displayed.

Example:
$ ./find /home/users/arekm/xtest -printf "name=%f projquotaid=%q\n"
name=xtest projquotaid=0
name=proj50 projquotaid=50
name=aaa projquotaid=50
name=aaadsfdsf projquotaid=50
name=something projquotaid=50
name=34knjf projquotaid=50
name=proj60 projquotaid=60
name=34knjf projquotaid=60
name=34knjfwenhwjqe projquotaid=60
name=dsfdsfknjfwenhwjqe projquotaid=60
name=proj70 projquotaid=70
name=7sfdsfdsf projquotaid=70
name=7sf4344 projquotaid=70
name=7sf4344ad projquotaid=70

$ ./find /home/users/arekm/xtest -xfsprojquota 50
/home/users/arekm/xtest/proj50
/home/users/arekm/xtest/proj50/aaa
/home/users/arekm/xtest/proj50/aaadsfdsf
/home/users/arekm/xtest/proj50/something
/home/users/arekm/xtest/proj50/34knjf
---
 find/defs.h   |   17 +++++++++++++-
 find/find.1   |    2 +
 find/parser.c |   20 ++++++++++++++-
 find/pred.c   |   71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 find/tree.c   |    1 +
 5 files changed, 108 insertions(+), 3 deletions(-)

diff --git a/find/defs.h b/find/defs.h
index 1708d83..9a36433 100644
--- a/find/defs.h
+++ b/find/defs.h
@@ -39,6 +39,7 @@ Please stop compiling the program now
 
 /* XXX: some of these includes probably don't belong in a common header file */
 #include <sys/stat.h>
+#include <sys/ioctl.h>
 #include <stdio.h>		/* for FILE* */
 #include <string.h>
 #include <stdlib.h>
@@ -65,6 +66,19 @@ typedef bool boolean;
 #include "buildcmd.h"
 #include "quotearg.h"
 
+#ifndef XFS_IOC_FSGETXATTR
+struct fsxattr
+{
+  uint32_t      fsx_xflags;     /* xflags field value (get/set) */
+  uint32_t      fsx_extsize;    /* extsize field value (get/set)*/
+  uint32_t      fsx_nextents;   /* nextents field value (get)   */
+  uint32_t      fsx_projid;     /* project identifier (get/set) */
+  unsigned char fsx_pad[12];
+};
+
+#define XFS_IOC_FSGETXATTR      _IOR ('X', 31, struct fsxattr)
+#endif
+
 /* These days we will assume ANSI/ISO C protootypes work on our compiler. */
 #define PARAMS(Args) Args
 
@@ -306,7 +320,7 @@ struct predicate
     const char *str;		/* fstype [i]lname [i]name [i]path */
     struct re_pattern_buffer *regex; /* regex */
     struct exec_val exec_vec;	/* exec ok */
-    struct long_val numinfo;	/* gid inum links  uid */
+    struct long_val numinfo;	/* gid inum links uid xfsprojquota */
     struct size_val size;	/* size */
     uid_t uid;			/* user */
     gid_t gid;			/* group */
@@ -458,6 +472,7 @@ PREDICATEFUNCTION pred_uid;
 PREDICATEFUNCTION pred_used;
 PREDICATEFUNCTION pred_user;
 PREDICATEFUNCTION pred_writable;
+PREDICATEFUNCTION pred_xfsprojquota;
 PREDICATEFUNCTION pred_xtype;
 
 
diff --git a/find/find.1 b/find/find.1
index 25953fc..a4a783f 100644
--- a/find/find.1
+++ b/find/find.1
@@ -1327,6 +1327,8 @@ File's name.
 .IP %P
 File's name with the name of the command line argument under which
 it was found removed.
+.IP %q
+XFS filesystem project quota ID.
 .IP %s
 File's size in bytes.
 .IP %S 
diff --git a/find/parser.c b/find/parser.c
index 102eb7d..9702f87 100644
--- a/find/parser.c
+++ b/find/parser.c
@@ -145,6 +145,7 @@ static boolean parse_time          PARAMS((const struct parser_table*, char *arg
 static boolean parse_true          PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
 static boolean parse_type          PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
 static boolean parse_uid           PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
+static boolean parse_xfsprojquota  PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
 static boolean parse_used          PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
 static boolean parse_user          PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
 static boolean parse_version       PARAMS((const struct parser_table*, char *argv[], int *arg_ptr));
@@ -320,6 +321,7 @@ static struct parser_table const parse_table[] =
   PARSE_TEST_NP    ("wholename",             wholename), /* GNU, replaced -path, but anyway -path will soon be in POSIX */
   {ARG_TEST,       "writable",               parse_accesscheck, pred_writable}, /* GNU, 4.3.0+ */
   PARSE_OPTION     ("xdev",                  xdev), /* POSIX */
+  PARSE_TEST       ("xfsprojquota",          xfsprojquota), /* XFS Project Quota ID */
   PARSE_TEST       ("xtype",                 xtype),	     /* GNU */
 #ifdef UNIMPLEMENTED_UNIX
   /* It's pretty ugly for find to know about archive formats.
@@ -1127,7 +1129,7 @@ tests (N can be +N or -N or N): -amin N -anewer FILE -atime N -cmin N\n\
       -nouser -nogroup -path PATTERN -perm [+-]MODE -regex PATTERN\n\
       -readable -writable -executable\n\
       -wholename PATTERN -size N[bcwkMG] -true -type [bcdpflsD] -uid N\n\
-      -used N -user NAME -xtype [bcdpfls]\n"));
+      -used N -user NAME -xfsprojquota N -xtype [bcdpfls]\n"));
   puts (_("\
 actions: -delete -print0 -printf FORMAT -fprintf FILE FORMAT -print \n\
       -fprint0 FILE -fprint FILE -ls -fls FILE -prune -quit\n\
@@ -2562,6 +2564,19 @@ parse_warn (const struct parser_table* entry, char **argv, int *arg_ptr)
 }
 
 static boolean
+parse_xfsprojquota (const struct parser_table* entry, char **argv, int *arg_ptr)
+{
+  struct predicate *p = insert_num (argv, arg_ptr, entry);
+  if (p)
+  {
+    /* XXX: how to do if (parent dir has xfsprojid) { est_success_rate = 99 } else { 0.2 } ? */
+    p->est_success_rate = 0.2;
+    return true;
+  }
+  return false;
+}
+
+static boolean
 parse_xtype (const struct parser_table* entry, char **argv, int *arg_ptr)
 {
   return insert_type (argv, arg_ptr, entry, pred_xtype);
@@ -2784,7 +2799,7 @@ insert_fprintf (struct format_val *vec,
 	  if (*scan2 == '.')
 	    for (scan2++; ISDIGIT (*scan2); scan2++)
 	      /* Do nothing. */ ;
-	  if (strchr ("abcdDfFgGhHiklmMnpPsStuUyY", *scan2))
+	  if (strchr ("abcdDfFgGhHiklmMnpPqsStuUyY", *scan2))
 	    {
 	      segmentp = make_segment (segmentp, format, scan2 - format,
 				       KIND_FORMAT, *scan2, 0,
@@ -2915,6 +2930,7 @@ make_segment (struct segment **segment,
       break;
 
     case 'H':			/* ARGV element file was found under */
+    case 'q':			/* XFS Project Quota ID */
       *fmt++ = 's';
       break;
       
diff --git a/find/pred.c b/find/pred.c
index a458803..12d0a2f 100644
--- a/find/pred.c
+++ b/find/pred.c
@@ -26,6 +26,7 @@
 #include <grp.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/ioctl.h>
 #include <errno.h>
 #include <assert.h>
 #include <stdarg.h>
@@ -229,6 +230,7 @@ struct pred_assoc pred_table[] =
   {pred_used, "used    "},
   {pred_user, "user    "},
   {pred_writable, "writable "},
+  {pred_xfsprojquota, "xfsprojquota "},
   {pred_xtype, "xtype   "},
   {0, "none    "}
 };
@@ -954,6 +956,29 @@ do_fprintf(struct format_val *dest,
 	  checked_print_quoted (dest, segment->text, cp);
 	  break;
 	  
+	case 'q':
+	  {
+	    int fd;
+	    struct fsxattr fsx;
+
+	    if ((fd = openat(state.cwd_dir_fd, state.rel_pathname, O_RDONLY|O_NOCTTY
+#if defined O_LARGEFILE
+			    |O_LARGEFILE
+#endif
+	              )) >= 0) {
+	      if ((ioctl(fd, XFS_IOC_FSGETXATTR, &fsx)) >= 0) {
+	        checked_fprintf (dest, segment->text,
+				human_readable ((uintmax_t) fsx.fsx_projid, hbuf,
+					human_ceiling, 1, 1));
+	      }
+	      close(fd);
+	    }
+
+	    /* Do nothing. */
+	    /*FALLTHROUGH*/
+	    break;
+	  }
+
 	case 's':		/* size in bytes */
 	  /* UNTRUSTED, probably unexploitable */
 	  checked_fprintf (dest, segment->text,
@@ -1804,6 +1829,52 @@ pred_user (const char *pathname, struct stat *stat_buf, struct predicate *pred_p
   else
     return (false);
 }
+
+boolean
+pred_xfsprojquota(const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
+{
+  int fd;
+  struct fsxattr fsx;
+
+  if ((fd = openat(state.cwd_dir_fd, state.rel_pathname, O_RDONLY|O_NOCTTY
+#if defined O_LARGEFILE
+		  |O_LARGEFILE
+#endif
+		  )) < 0)
+  {
+	  error (0, errno, "%s", safely_quote_err_filename(0, pathname));
+	  state.exit_status = 1;
+	  return false;
+  }
+
+  if ((ioctl(fd, XFS_IOC_FSGETXATTR, &fsx)) < 0) {
+	  error (0, errno, "%s", safely_quote_err_filename(0, pathname));
+	  state.exit_status = 1;
+	  close(fd);
+	  return false;
+  }
+
+  close(fd);
+
+  switch (pred_ptr->args.numinfo.kind)
+    {
+    case COMP_GT:
+      if (fsx.fsx_projid > pred_ptr->args.numinfo.l_val)
+	return (true);
+      break;
+    case COMP_LT:
+      if (fsx.fsx_projid < pred_ptr->args.numinfo.l_val)
+	return (true);
+      break;
+    case COMP_EQ:
+      if (fsx.fsx_projid == pred_ptr->args.numinfo.l_val)
+	return (true);
+      break;
+    }
+  return (false);
+
+}
+
 
 boolean
 pred_xtype (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
diff --git a/find/tree.c b/find/tree.c
index 7420c60..e5c0683 100644
--- a/find/tree.c
+++ b/find/tree.c
@@ -950,6 +950,7 @@ static struct pred_cost_lookup costlookup[] =
     { pred_true	     ,  NeedsNothing         },
     { pred_type      ,  NeedsType            },
     { pred_uid       ,  NeedsStatInfo        },
+    { pred_xfsprojquota, NeedsNothing        },
     { pred_used      ,  NeedsStatInfo        },
     { pred_user      ,  NeedsStatInfo        },
     { pred_writable  ,  NeedsAccessInfo      },
-- 
1.6.0.5

