coreutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Du feature request - group reporting


From: Daniel Gall
Subject: Re: Du feature request - group reporting
Date: Fri, 2 Mar 2018 00:22:24 -0500

Have I done something incorrectly?  How or does this feature request
proceed from here?

Thank you for considering it.

Dan.

On Wednesday, February 28, 2018, Daniel Gall <address@hidden>
wrote:

> uggh and i'm not current with master.  one more time.
>
> From bbbbd12345c4585de20f2fef304c6b5c7185d2a2 Mon Sep 17 00:00:00 2001
> From: Daniel Gall <address@hidden>
> Date: Tue, 27 Feb 2018 20:05:01 -0500
> Subject: [PATCH] du: Added group reporting feature
>
> ---
>  NEWS               |   2 +
>  doc/coreutils.texi |   5 ++
>  src/du.c           | 176 ++++++++++++++++++++++++++++++
> ++++++++++++++++++++---
>  3 files changed, 174 insertions(+), 9 deletions(-)
>
> diff --git a/NEWS b/NEWS
> index 5fa6928..a136cc9 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -61,6 +61,8 @@ GNU coreutils NEWS
>  -*- outline -*-
>
>    timeout now supports the --verbose option to diagnose forced
> termination.
>
> +  du now supports the -g option for group reporting
> +
>  ** Improvements
>
>    dd now supports iflag=direct with arbitrary sized files on all file
> systems.
> diff --git a/doc/coreutils.texi b/doc/coreutils.texi
> index cdde136..d220012 100644
> --- a/doc/coreutils.texi
> +++ b/doc/coreutils.texi
> @@ -11910,6 +11910,11 @@ is at level 0, so @code{du --max-depth=0} is
> equivalent to @code{du -s}.
>  @c --files0-from=FILE
>  @filesZeroFromOption{du,, with the @option{--total} (@option{-c}) option}
>
> +@item -g
> +@opindex -g
> +@cindex group reporting
> +Show group subtotals for each item reported on.
> +
>  @item -H
>  @opindex -H
>  Equivalent to @option{--dereference-args} (@option{-D}).
> diff --git a/src/du.c b/src/du.c
> index ac4489f..97edb5d 100644
> --- a/src/du.c
> +++ b/src/du.c
> @@ -35,6 +35,7 @@
>  #include "error.h"
>  #include "exclude.h"
>  #include "fprintftime.h"
> +#include "grp.h"
>  #include "human.h"
>  #include "mountlist.h"
>  #include "quote.h"
> @@ -61,6 +62,9 @@ extern bool fts_debug;
>  # define FTS_CROSS_CHECK(Fts)
>  #endif
>
> +/* If true, display group size info. */
> +bool opt_group_sizes = false;
> +
>  /* A set of dev/ino pairs to help identify files and directories
>     whose sizes have already been counted.  */
>  static struct di_set *di_files;
> @@ -80,7 +84,7 @@ struct duinfo
>
>    /* Number of inodes in directory.  */
>    uintmax_t inodes;
> -
> +  uintmax_t *group_size;
>    /* Latest timestamp found.  If tmax.tv_sec == TYPE_MINIMUM (time_t)
>       && tmax.tv_nsec < 0, no timestamp has been found.  */
>    struct timespec tmax;
> @@ -90,28 +94,62 @@ struct duinfo
>  static inline void
>  duinfo_init (struct duinfo *a)
>  {
> +  uintmax_t i = 0;
>    a->size = 0;
>    a->inodes = 0;
>    a->tmax.tv_sec = TYPE_MINIMUM (time_t);
>    a->tmax.tv_nsec = -1;
> +  if (opt_group_sizes)
> +    {
> +      for (i=0; i<65535; i++)
> +        {
> +      a->group_size[i] = 0;
> +    }
> +    }
>  }
>
>  /* Set directory data.  */
>  static inline void
> -duinfo_set (struct duinfo *a, uintmax_t size, struct timespec tmax)
> +duinfo_set (struct duinfo *a, uintmax_t size, struct timespec tmax,
> uintmax_t gid)
>  {
> +  uintmax_t gid_u = (uintmax_t)gid;
> +  uintmax_t gid_s;
>    a->size = size;
>    a->inodes = 1;
>    a->tmax = tmax;
> +  if (opt_group_sizes)
> +    {
> +      if (gid_u > 65534)
> +        {
> +      gid_s = 65534;
> +    }
> +      else
> +        {
> +      gid_s = gid_u;
> +    }
> +      if (gid_s >=0 && gid_s <=65534)
> +        {
> +      a->group_size[gid_s] = size;
> +    }
> +    }
>  }
>
>  /* Accumulate directory data.  */
>  static inline void
>  duinfo_add (struct duinfo *a, struct duinfo const *b)
>  {
> +  uintmax_t i = 0;
>    uintmax_t sum = a->size + b->size;
>    a->size = a->size <= sum ? sum : UINTMAX_MAX;
>    a->inodes = a->inodes + b->inodes;
> +  if (opt_group_sizes)
> +    {
> +      for (i=0; i<65535; i++)
> +        {
> +          sum = a->group_size[i] + b->group_size[i];
> +          a->group_size[i] = a->group_size[i] <= sum ? sum : UINTMAX_MAX;
> +        }
> +    }
>    if (timespec_cmp (a->tmax, b->tmax) < 0)
>      a->tmax = b->tmax;
>  }
> @@ -226,6 +264,7 @@ static struct option const long_options[] =
>    {"exclude", required_argument, NULL, EXCLUDE_OPTION},
>    {"exclude-from", required_argument, NULL, 'X'},
>    {"files0-from", required_argument, NULL, FILES0_FROM_OPTION},
> +  {"group-reporting", no_argument, NULL, 'g'},
>    {"human-readable", no_argument, NULL, 'h'},
>    {"inodes", no_argument, NULL, INODES_OPTION},
>    {"si", no_argument, NULL, HUMAN_SI_OPTION},
> @@ -317,6 +356,7 @@ Summarize disk usage of the set of FILEs,
> recursively for directories.\n\
>        --files0-from=F   summarize disk usage of the\n\
>                            NUL-terminated file names specified in file
> F;\n\
>                            if F is -, then read names from standard
> input\n\
> +  -g, --group-reporting also print group subtotals\n\
>    -H                    equivalent to --dereference-args (-D)\n\
>    -h, --human-readable  print sizes in human readable format (e.g.,
> 1K 234M 2G)\
>  \n\
> @@ -411,7 +451,25 @@ print_size (const struct duinfo *pdui, const char
> *string)
>    print_only_size (opt_inodes
>                     ? pdui->inodes
>                     : pdui->size);
> -
> +  if (opt_group_sizes)
> +    {
> +      uintmax_t i=0;
> +      struct group *g;
> +      printf(" Groups");
> +      for (i=0; i<65535; i++){
> +        if (pdui->group_size[i] > 0)
> +          {
> +            g = getgrgid(i);
> +            printf (",");
> +            if (g == NULL){
> +              printf (" %Ld:", (long long unsigned int)i);
> +            }else{
> +              printf(" %s:", g->gr_name);
> +            }
> +            print_only_size(pdui->group_size[i]);
> +          }
> +      }
> +    }
>    if (opt_time)
>      {
>        putchar ('\t');
> @@ -506,6 +564,12 @@ process_file (FTS *fts, FTSENT *ent)
>    const struct stat *sb = ent->fts_statp;
>    int info = ent->fts_info;
>
> +  if(opt_group_sizes)
> +    {
> +      dui.group_size = xcalloc (65536, sizeof (uintmax_t));
> +      dui_to_print.group_size = xcalloc (65536, sizeof (uintmax_t));
> +    }
> +
>    if (info == FTS_DNR)
>      {
>        /* An error occurred, but the size is known, so count it.  */
> @@ -530,7 +594,18 @@ process_file (FTS *fts, FTSENT *ent)
>            if (info == FTS_NS || info == FTS_SLNONE)
>              {
>                error (0, ent->fts_errno, _("cannot access %s"), quoteaf
> (file));
> -              return false;
> +              if(opt_group_sizes)
> +                {
> +                  if (dui.group_size != NULL)
> +                    {
> +                      free(dui.group_size);
> +                    }
> +                  if (dui_to_print.group_size != NULL)
> +                    {
> +                      free(dui_to_print.group_size);
> +                    }
> +                }
> +          return false;
>              }
>
>            /* The --one-file-system (-x) option cannot exclude anything
> @@ -558,13 +633,34 @@ process_file (FTS *fts, FTSENT *ent)
>                FTSENT const *e = fts_read (fts);
>                assert (e == ent);
>              }
> -
> +          if(opt_group_sizes)
> +            {
> +              if (dui.group_size != NULL)
> +                {
> +                  free(dui.group_size);
> +                }
> +              if (dui_to_print.group_size != NULL)
> +                {
> +                  free(dui_to_print.group_size);
> +                }
> +            }
>            return true;
>          }
>
>        switch (info)
>          {
>          case FTS_D:
> +      if(opt_group_sizes)
> +            {
> +              if (dui.group_size != NULL)
> +                {
> +                  free(dui.group_size);
> +                }
> +              if (dui_to_print.group_size != NULL)
> +                {
> +                  free(dui_to_print.group_size);
> +                }
> +            }
>            return true;
>
>          case FTS_ERR:
> @@ -574,6 +670,17 @@ process_file (FTS *fts, FTSENT *ent)
>            break;
>
>          case FTS_DC:
> +      if(opt_group_sizes)
> +            {
> +              if (dui.group_size != NULL)
> +                {
> +                  free(dui.group_size);
> +                }
> +              if (dui_to_print.group_size != NULL)
> +                {
> +                  free(dui_to_print.group_size);
> +                }
> +            }
>            /* If not following symlinks and not a (bind) mount point.  */
>            if (cycle_warning_required (fts, ent)
>                && ! mount_point_in_fts_cycle (ent))
> @@ -591,15 +698,40 @@ process_file (FTS *fts, FTSENT *ent)
>                 : (uintmax_t) ST_NBLOCKS (*sb) * ST_NBLOCKSIZE),
>                (time_type == time_mtime ? get_stat_mtime (sb)
>                 : time_type == time_atime ? get_stat_atime (sb)
> -               : get_stat_ctime (sb)));
> +               : get_stat_ctime (sb)),
> +           sb->st_gid);
>
>    level = ent->fts_level;
> -  dui_to_print = dui;
> +
> +  if (opt_group_sizes)
> +    {
> +      duinfo_set (&dui_to_print,
> +              (apparent_size
> +               ? MAX (0, sb->st_size)
> +               : (uintmax_t) ST_NBLOCKS (*sb) * ST_NBLOCKSIZE),
> +              (time_type == time_mtime ? get_stat_mtime (sb)
> +               : time_type == time_atime ? get_stat_atime (sb)
> +               : get_stat_ctime (sb)),
> +               sb->st_gid);
> +    }
> +  else
> +    {
> +      dui_to_print = dui;
> +    }
>
>    if (n_alloc == 0)
>      {
> +      size_t i;
>        n_alloc = level + 10;
>        dulvl = xcalloc (n_alloc, sizeof *dulvl);
> +      if(opt_group_sizes)
> +        {
> +          for (i=0; i<n_alloc; i++)
> +            {
> +              dulvl[i].ent.group_size = xcalloc (65536, sizeof
> (uintmax_t));
> +              dulvl[i].subdir.group_size = xcalloc (65536, sizeof
> (uintmax_t));
> +            }
> +        }
>      }
>    else
>      {
> @@ -613,14 +745,23 @@ process_file (FTS *fts, FTSENT *ent)
>               Clear the accumulators for *all* levels between prev_level
>               and the current one.  The depth may change dramatically,
>               e.g., from 1 to 10.  */
> +          size_t i;
>
>            if (n_alloc <= level)
>              {
>                dulvl = xnrealloc (dulvl, level, 2 * sizeof *dulvl);
> +              if(opt_group_sizes)
> +                {
> +                  for (i=n_alloc; i<level*2; i++)
> +                    {
> +                      dulvl[i].ent.group_size = xcalloc (65536,
> sizeof (uintmax_t));
> +                      dulvl[i].subdir.group_size = xcalloc (65536,
> sizeof (uintmax_t));
> +                    }
> +                }
>                n_alloc = level * 2;
>              }
>
> -          for (size_t i = prev_level + 1; i <= level; i++)
> +          for (i = prev_level + 1; i <= level; i++)
>              {
>                duinfo_init (&dulvl[i].ent);
>                duinfo_init (&dulvl[i].subdir);
> @@ -666,6 +807,18 @@ process_file (FTS *fts, FTSENT *ent)
>          print_size (&dui_to_print, file);
>      }
>
> +  if(opt_group_sizes)
> +    {
> +      if (dui.group_size != NULL)
> +        {
> +          free(dui.group_size);
> +        }
> +      if (dui_to_print.group_size != NULL)
> +        {
> +          free(dui_to_print.group_size);
> +        }
> +    }
> +
>    return ok;
>  }
>
> @@ -755,7 +908,7 @@ main (int argc, char **argv)
>    while (true)
>      {
>        int oi = -1;
> -      int c = getopt_long (argc, argv, "0abd:chHklmst:xB:DLPSX:",
> +      int c = getopt_long (argc, argv, "0abgd:chHklmst:xB:DLPSX:",
>                             long_options, &oi);
>        if (c == -1)
>          break;
> @@ -800,6 +953,11 @@ main (int argc, char **argv)
>            output_block_size = 1;
>            break;
>
> +        case 'g':
> +          tot_dui.group_size = xcalloc (65536, sizeof (uintmax_t));
> +          opt_group_sizes = true;
> +          break;
> +
>          case 'k':
>            human_output_opts = 0;
>            output_block_size = 1024;
> --
> 2.10.2
>
>
> On Wed, Feb 28, 2018 at 12:59 AM, Daniel Gall <address@hidden>
> wrote:
> > Ok, here it all is in one commit.
> >
> > From f170b214684f870e81ab79c83a0dd87206fee4af Mon Sep 17 00:00:00 2001
> > From: Daniel Gall <address@hidden>
> > Date: Tue, 27 Feb 2018 19:51:34 -0500
> > Subject: [PATCH] du: Added group reporting feature
> >
> > ---
> >  NEWS               |   7 +--
> >  doc/coreutils.texi |   5 ++
> >  src/du.c           | 176 ++++++++++++++++++++++++++++++
> ++++++++++++++++++++---
> >  3 files changed, 174 insertions(+), 14 deletions(-)
> >
> > diff --git a/NEWS b/NEWS
> > index 5fa6928..97ccc2c 100644
> > --- a/NEWS
> > +++ b/NEWS
> > @@ -16,11 +16,6 @@ GNU coreutils NEWS
> >   -*- outline -*-
> >    that caused -u to sometimes override -n.
> >    [bug introduced with coreutils-7.1]
> >
> > -  'cp -a --no-preserve=mode' now sets appropriate default permissions
> > -  for non regular files like fifos and character device nodes etc.
> > -  Previously it would have set executable bits on created special files.
> > -  [bug introduced with coreutils-8.20]
> > -
> >
> >  * Noteworthy changes in release 8.29 (2017-12-27) [stable]
> >
> > @@ -61,6 +56,8 @@ GNU coreutils NEWS
> >  -*- outline -*-
> >
> >    timeout now supports the --verbose option to diagnose forced
> termination.
> >
> > +  du now supports the -g option for group reporting
> > +
> >  ** Improvements
> >
> >    dd now supports iflag=direct with arbitrary sized files on all file
> systems.
> > diff --git a/doc/coreutils.texi b/doc/coreutils.texi
> > index cdde136..d220012 100644
> > --- a/doc/coreutils.texi
> > +++ b/doc/coreutils.texi
> > @@ -11910,6 +11910,11 @@ is at level 0, so @code{du --max-depth=0} is
> > equivalent to @code{du -s}.
> >  @c --files0-from=FILE
> >  @filesZeroFromOption{du,, with the @option{--total} (@option{-c})
> option}
> >
> > +@item -g
> > +@opindex -g
> > +@cindex group reporting
> > +Show group subtotals for each item reported on.
> > +
> >  @item -H
> >  @opindex -H
> >  Equivalent to @option{--dereference-args} (@option{-D}).
> > diff --git a/src/du.c b/src/du.c
> > index ac4489f..97edb5d 100644
> > --- a/src/du.c
> > +++ b/src/du.c
> > @@ -35,6 +35,7 @@
> >  #include "error.h"
> >  #include "exclude.h"
> >  #include "fprintftime.h"
> > +#include "grp.h"
> >  #include "human.h"
> >  #include "mountlist.h"
> >  #include "quote.h"
> > @@ -61,6 +62,9 @@ extern bool fts_debug;
> >  # define FTS_CROSS_CHECK(Fts)
> >  #endif
> >
> > +/* If true, display group size info. */
> > +bool opt_group_sizes = false;
> > +
> >  /* A set of dev/ino pairs to help identify files and directories
> >     whose sizes have already been counted.  */
> >  static struct di_set *di_files;
> > @@ -80,7 +84,7 @@ struct duinfo
> >
> >    /* Number of inodes in directory.  */
> >    uintmax_t inodes;
> > -
> > +  uintmax_t *group_size;
> >    /* Latest timestamp found.  If tmax.tv_sec == TYPE_MINIMUM (time_t)
> >       && tmax.tv_nsec < 0, no timestamp has been found.  */
> >    struct timespec tmax;
> > @@ -90,28 +94,62 @@ struct duinfo
> >  static inline void
> >  duinfo_init (struct duinfo *a)
> >  {
> > +  uintmax_t i = 0;
> >    a->size = 0;
> >    a->inodes = 0;
> >    a->tmax.tv_sec = TYPE_MINIMUM (time_t);
> >    a->tmax.tv_nsec = -1;
> > +  if (opt_group_sizes)
> > +    {
> > +      for (i=0; i<65535; i++)
> > +        {
> > +      a->group_size[i] = 0;
> > +    }
> > +    }
> >  }
> >
> >  /* Set directory data.  */
> >  static inline void
> > -duinfo_set (struct duinfo *a, uintmax_t size, struct timespec tmax)
> > +duinfo_set (struct duinfo *a, uintmax_t size, struct timespec tmax,
> > uintmax_t gid)
> >  {
> > +  uintmax_t gid_u = (uintmax_t)gid;
> > +  uintmax_t gid_s;
> >    a->size = size;
> >    a->inodes = 1;
> >    a->tmax = tmax;
> > +  if (opt_group_sizes)
> > +    {
> > +      if (gid_u > 65534)
> > +        {
> > +      gid_s = 65534;
> > +    }
> > +      else
> > +        {
> > +      gid_s = gid_u;
> > +    }
> > +      if (gid_s >=0 && gid_s <=65534)
> > +        {
> > +      a->group_size[gid_s] = size;
> > +    }
> > +    }
> >  }
> >
> >  /* Accumulate directory data.  */
> >  static inline void
> >  duinfo_add (struct duinfo *a, struct duinfo const *b)
> >  {
> > +  uintmax_t i = 0;
> >    uintmax_t sum = a->size + b->size;
> >    a->size = a->size <= sum ? sum : UINTMAX_MAX;
> >    a->inodes = a->inodes + b->inodes;
> > +  if (opt_group_sizes)
> > +    {
> > +      for (i=0; i<65535; i++)
> > +        {
> > +          sum = a->group_size[i] + b->group_size[i];
> > +          a->group_size[i] = a->group_size[i] <= sum ? sum :
> UINTMAX_MAX;
> > +        }
> > +    }
> >    if (timespec_cmp (a->tmax, b->tmax) < 0)
> >      a->tmax = b->tmax;
> >  }
> > @@ -226,6 +264,7 @@ static struct option const long_options[] =
> >    {"exclude", required_argument, NULL, EXCLUDE_OPTION},
> >    {"exclude-from", required_argument, NULL, 'X'},
> >    {"files0-from", required_argument, NULL, FILES0_FROM_OPTION},
> > +  {"group-reporting", no_argument, NULL, 'g'},
> >    {"human-readable", no_argument, NULL, 'h'},
> >    {"inodes", no_argument, NULL, INODES_OPTION},
> >    {"si", no_argument, NULL, HUMAN_SI_OPTION},
> > @@ -317,6 +356,7 @@ Summarize disk usage of the set of FILEs,
> > recursively for directories.\n\
> >        --files0-from=F   summarize disk usage of the\n\
> >                            NUL-terminated file names specified in file
> F;\n\
> >                            if F is -, then read names from standard
> input\n\
> > +  -g, --group-reporting also print group subtotals\n\
> >    -H                    equivalent to --dereference-args (-D)\n\
> >    -h, --human-readable  print sizes in human readable format (e.g.,
> > 1K 234M 2G)\
> >  \n\
> > @@ -411,7 +451,25 @@ print_size (const struct duinfo *pdui, const char
> *string)
> >    print_only_size (opt_inodes
> >                     ? pdui->inodes
> >                     : pdui->size);
> > -
> > +  if (opt_group_sizes)
> > +    {
> > +      uintmax_t i=0;
> > +      struct group *g;
> > +      printf(" Groups");
> > +      for (i=0; i<65535; i++){
> > +        if (pdui->group_size[i] > 0)
> > +          {
> > +            g = getgrgid(i);
> > +            printf (",");
> > +            if (g == NULL){
> > +              printf (" %Ld:", (long long unsigned int)i);
> > +            }else{
> > +              printf(" %s:", g->gr_name);
> > +            }
> > +            print_only_size(pdui->group_size[i]);
> > +          }
> > +      }
> > +    }
> >    if (opt_time)
> >      {
> >        putchar ('\t');
> > @@ -506,6 +564,12 @@ process_file (FTS *fts, FTSENT *ent)
> >    const struct stat *sb = ent->fts_statp;
> >    int info = ent->fts_info;
> >
> > +  if(opt_group_sizes)
> > +    {
> > +      dui.group_size = xcalloc (65536, sizeof (uintmax_t));
> > +      dui_to_print.group_size = xcalloc (65536, sizeof (uintmax_t));
> > +    }
> > +
> >    if (info == FTS_DNR)
> >      {
> >        /* An error occurred, but the size is known, so count it.  */
> > @@ -530,7 +594,18 @@ process_file (FTS *fts, FTSENT *ent)
> >            if (info == FTS_NS || info == FTS_SLNONE)
> >              {
> >                error (0, ent->fts_errno, _("cannot access %s"), quoteaf
> (file));
> > -              return false;
> > +              if(opt_group_sizes)
> > +                {
> > +                  if (dui.group_size != NULL)
> > +                    {
> > +                      free(dui.group_size);
> > +                    }
> > +                  if (dui_to_print.group_size != NULL)
> > +                    {
> > +                      free(dui_to_print.group_size);
> > +                    }
> > +                }
> > +          return false;
> >              }
> >
> >            /* The --one-file-system (-x) option cannot exclude anything
> > @@ -558,13 +633,34 @@ process_file (FTS *fts, FTSENT *ent)
> >                FTSENT const *e = fts_read (fts);
> >                assert (e == ent);
> >              }
> > -
> > +          if(opt_group_sizes)
> > +            {
> > +              if (dui.group_size != NULL)
> > +                {
> > +                  free(dui.group_size);
> > +                }
> > +              if (dui_to_print.group_size != NULL)
> > +                {
> > +                  free(dui_to_print.group_size);
> > +                }
> > +            }
> >            return true;
> >          }
> >
> >        switch (info)
> >          {
> >          case FTS_D:
> > +      if(opt_group_sizes)
> > +            {
> > +              if (dui.group_size != NULL)
> > +                {
> > +                  free(dui.group_size);
> > +                }
> > +              if (dui_to_print.group_size != NULL)
> > +                {
> > +                  free(dui_to_print.group_size);
> > +                }
> > +            }
> >            return true;
> >
> >          case FTS_ERR:
> > @@ -574,6 +670,17 @@ process_file (FTS *fts, FTSENT *ent)
> >            break;
> >
> >          case FTS_DC:
> > +      if(opt_group_sizes)
> > +            {
> > +              if (dui.group_size != NULL)
> > +                {
> > +                  free(dui.group_size);
> > +                }
> > +              if (dui_to_print.group_size != NULL)
> > +                {
> > +                  free(dui_to_print.group_size);
> > +                }
> > +            }
> >            /* If not following symlinks and not a (bind) mount point.  */
> >            if (cycle_warning_required (fts, ent)
> >                && ! mount_point_in_fts_cycle (ent))
> > @@ -591,15 +698,40 @@ process_file (FTS *fts, FTSENT *ent)
> >                 : (uintmax_t) ST_NBLOCKS (*sb) * ST_NBLOCKSIZE),
> >                (time_type == time_mtime ? get_stat_mtime (sb)
> >                 : time_type == time_atime ? get_stat_atime (sb)
> > -               : get_stat_ctime (sb)));
> > +               : get_stat_ctime (sb)),
> > +           sb->st_gid);
> >
> >    level = ent->fts_level;
> > -  dui_to_print = dui;
> > +
> > +  if (opt_group_sizes)
> > +    {
> > +      duinfo_set (&dui_to_print,
> > +              (apparent_size
> > +               ? MAX (0, sb->st_size)
> > +               : (uintmax_t) ST_NBLOCKS (*sb) * ST_NBLOCKSIZE),
> > +              (time_type == time_mtime ? get_stat_mtime (sb)
> > +               : time_type == time_atime ? get_stat_atime (sb)
> > +               : get_stat_ctime (sb)),
> > +               sb->st_gid);
> > +    }
> > +  else
> > +    {
> > +      dui_to_print = dui;
> > +    }
> >
> >    if (n_alloc == 0)
> >      {
> > +      size_t i;
> >        n_alloc = level + 10;
> >        dulvl = xcalloc (n_alloc, sizeof *dulvl);
> > +      if(opt_group_sizes)
> > +        {
> > +          for (i=0; i<n_alloc; i++)
> > +            {
> > +              dulvl[i].ent.group_size = xcalloc (65536, sizeof
> (uintmax_t));
> > +              dulvl[i].subdir.group_size = xcalloc (65536, sizeof
> (uintmax_t));
> > +            }
> > +        }
> >      }
> >    else
> >      {
> > @@ -613,14 +745,23 @@ process_file (FTS *fts, FTSENT *ent)
> >               Clear the accumulators for *all* levels between prev_level
> >               and the current one.  The depth may change dramatically,
> >               e.g., from 1 to 10.  */
> > +          size_t i;
> >
> >            if (n_alloc <= level)
> >              {
> >                dulvl = xnrealloc (dulvl, level, 2 * sizeof *dulvl);
> > +              if(opt_group_sizes)
> > +                {
> > +                  for (i=n_alloc; i<level*2; i++)
> > +                    {
> > +                      dulvl[i].ent.group_size = xcalloc (65536,
> > sizeof (uintmax_t));
> > +                      dulvl[i].subdir.group_size = xcalloc (65536,
> > sizeof (uintmax_t));
> > +                    }
> > +                }
> >                n_alloc = level * 2;
> >              }
> >
> > -          for (size_t i = prev_level + 1; i <= level; i++)
> > +          for (i = prev_level + 1; i <= level; i++)
> >              {
> >                duinfo_init (&dulvl[i].ent);
> >                duinfo_init (&dulvl[i].subdir);
> > @@ -666,6 +807,18 @@ process_file (FTS *fts, FTSENT *ent)
> >          print_size (&dui_to_print, file);
> >      }
> >
> > +  if(opt_group_sizes)
> > +    {
> > +      if (dui.group_size != NULL)
> > +        {
> > +          free(dui.group_size);
> > +        }
> > +      if (dui_to_print.group_size != NULL)
> > +        {
> > +          free(dui_to_print.group_size);
> > +        }
> > +    }
> > +
> >    return ok;
> >  }
> >
> > @@ -755,7 +908,7 @@ main (int argc, char **argv)
> >    while (true)
> >      {
> >        int oi = -1;
> > -      int c = getopt_long (argc, argv, "0abd:chHklmst:xB:DLPSX:",
> > +      int c = getopt_long (argc, argv, "0abgd:chHklmst:xB:DLPSX:",
> >                             long_options, &oi);
> >        if (c == -1)
> >          break;
> > @@ -800,6 +953,11 @@ main (int argc, char **argv)
> >            output_block_size = 1;
> >            break;
> >
> > +        case 'g':
> > +          tot_dui.group_size = xcalloc (65536, sizeof (uintmax_t));
> > +          opt_group_sizes = true;
> > +          break;
> > +
> >          case 'k':
> >            human_output_opts = 0;
> >            output_block_size = 1024;
> > --
> > 2.10.2
> >
> >
> > On Wed, Feb 7, 2018 at 8:01 AM, Daniel Gall <address@hidden>
> wrote:
> >> From 9fa842c9153dc0f68cfcf4ba25fa7f6798b4e2e4 Mon Sep 17 00:00:00 2001
> >> From: Daniel Gall <address@hidden>
> >> Date: Wed, 7 Feb 2018 02:58:52 -0500
> >> Subject: [PATCH 2/2] du: added "Groups" intro to group reporting
> >>
> >> In the event that both the group subtotal reporting feature
> >> and the users subtotal reporting feature are accepted,
> >> invokers may wish to use both features at the same time.
> >> This necessitates some delineation of user vs group reporting.
> >> ---
> >>  src/du.c | 1 +
> >>  1 file changed, 1 insertion(+)
> >>
> >> diff --git a/src/du.c b/src/du.c
> >> index a8318ca..97edb5d 100644
> >> --- a/src/du.c
> >> +++ b/src/du.c
> >> @@ -455,6 +455,7 @@ print_size (const struct duinfo *pdui, const char
> *string)
> >>      {
> >>        uintmax_t i=0;
> >>        struct group *g;
> >> +      printf(" Groups");
> >>        for (i=0; i<65535; i++){
> >>          if (pdui->group_size[i] > 0)
> >>            {
> >> --
> >> 2.10.2
> >>
> >> On Tue, Feb 6, 2018 at 10:45 PM, Daniel Gall <address@hidden>
> wrote:
> >>> I realized as I was implementing the user subtotal reporting feature
> >>> that if both the user and group reporting features are accepted then
> >>> users will be able to invoke both options and thus the reporting would
> >>> be confusing without some delineation of user vs group reporting
> >>> output.  I intend to add a line to the group feature request to try to
> >>> address that.
> >>>
> >>> Dan.
> >>>
> >>> On Mon, Feb 5, 2018 at 7:32 PM, Daniel Gall <address@hidden>
> wrote:
> >>>> From c5b3dc83a762fd96c60d7560249a1815386a5efb Mon Sep 17 00:00:00
> 2001
> >>>> From: Daniel Gall <address@hidden>
> >>>> Date: Mon, 5 Feb 2018 14:30:39 -0500
> >>>> Subject: [PATCH] :Added group reporting to du
> >>>>
> >>>> ---
> >>>>  NEWS               |   2 +
> >>>>  doc/coreutils.texi |   5 ++
> >>>>  src/du.c           | 175 ++++++++++++++++++++++++++++++
> ++++++++++++++++++++---
> >>>>  3 files changed, 173 insertions(+), 9 deletions(-)
> >>>>
> >>>> diff --git a/NEWS b/NEWS
> >>>> index 8a9e09e..97ccc2c 100644
> >>>> --- a/NEWS
> >>>> +++ b/NEWS
> >>>> @@ -56,6 +56,8 @@ GNU coreutils NEWS
> >>>>  -*- outline -*-
> >>>>
> >>>>    timeout now supports the --verbose option to diagnose forced
> termination.
> >>>>
> >>>> +  du now supports the -g option for group reporting
> >>>> +
> >>>>  ** Improvements
> >>>>
> >>>>    dd now supports iflag=direct with arbitrary sized files on all
> file systems.
> >>>> diff --git a/doc/coreutils.texi b/doc/coreutils.texi
> >>>> index cdde136..d220012 100644
> >>>> --- a/doc/coreutils.texi
> >>>> +++ b/doc/coreutils.texi
> >>>> @@ -11910,6 +11910,11 @@ is at level 0, so @code{du --max-depth=0} is
> >>>> equivalent to @code{du -s}.
> >>>>  @c --files0-from=FILE
> >>>>  @filesZeroFromOption{du,, with the @option{--total} (@option{-c})
> option}
> >>>>
> >>>> +@item -g
> >>>> +@opindex -g
> >>>> +@cindex group reporting
> >>>> +Show group subtotals for each item reported on.
> >>>> +
> >>>>  @item -H
> >>>>  @opindex -H
> >>>>  Equivalent to @option{--dereference-args} (@option{-D}).
> >>>> diff --git a/src/du.c b/src/du.c
> >>>> index ac4489f..a8318ca 100644
> >>>> --- a/src/du.c
> >>>> +++ b/src/du.c
> >>>> @@ -35,6 +35,7 @@
> >>>>  #include "error.h"
> >>>>  #include "exclude.h"
> >>>>  #include "fprintftime.h"
> >>>> +#include "grp.h"
> >>>>  #include "human.h"
> >>>>  #include "mountlist.h"
> >>>>  #include "quote.h"
> >>>> @@ -61,6 +62,9 @@ extern bool fts_debug;
> >>>>  # define FTS_CROSS_CHECK(Fts)
> >>>>  #endif
> >>>>
> >>>> +/* If true, display group size info. */
> >>>> +bool opt_group_sizes = false;
> >>>> +
> >>>>  /* A set of dev/ino pairs to help identify files and directories
> >>>>     whose sizes have already been counted.  */
> >>>>  static struct di_set *di_files;
> >>>> @@ -80,7 +84,7 @@ struct duinfo
> >>>>
> >>>>    /* Number of inodes in directory.  */
> >>>>    uintmax_t inodes;
> >>>> -
> >>>> +  uintmax_t *group_size;
> >>>>    /* Latest timestamp found.  If tmax.tv_sec == TYPE_MINIMUM (time_t)
> >>>>       && tmax.tv_nsec < 0, no timestamp has been found.  */
> >>>>    struct timespec tmax;
> >>>> @@ -90,28 +94,62 @@ struct duinfo
> >>>>  static inline void
> >>>>  duinfo_init (struct duinfo *a)
> >>>>  {
> >>>> +  uintmax_t i = 0;
> >>>>    a->size = 0;
> >>>>    a->inodes = 0;
> >>>>    a->tmax.tv_sec = TYPE_MINIMUM (time_t);
> >>>>    a->tmax.tv_nsec = -1;
> >>>> +  if (opt_group_sizes)
> >>>> +    {
> >>>> +      for (i=0; i<65535; i++)
> >>>> +        {
> >>>> +      a->group_size[i] = 0;
> >>>> +    }
> >>>> +    }
> >>>>  }
> >>>>
> >>>>  /* Set directory data.  */
> >>>>  static inline void
> >>>> -duinfo_set (struct duinfo *a, uintmax_t size, struct timespec tmax)
> >>>> +duinfo_set (struct duinfo *a, uintmax_t size, struct timespec tmax,
> >>>> uintmax_t gid)
> >>>>  {
> >>>> +  uintmax_t gid_u = (uintmax_t)gid;
> >>>> +  uintmax_t gid_s;
> >>>>    a->size = size;
> >>>>    a->inodes = 1;
> >>>>    a->tmax = tmax;
> >>>> +  if (opt_group_sizes)
> >>>> +    {
> >>>> +      if (gid_u > 65534)
> >>>> +        {
> >>>> +      gid_s = 65534;
> >>>> +    }
> >>>> +      else
> >>>> +        {
> >>>> +      gid_s = gid_u;
> >>>> +    }
> >>>> +      if (gid_s >=0 && gid_s <=65534)
> >>>> +        {
> >>>> +      a->group_size[gid_s] = size;
> >>>> +    }
> >>>> +    }
> >>>>  }
> >>>>
> >>>>  /* Accumulate directory data.  */
> >>>>  static inline void
> >>>>  duinfo_add (struct duinfo *a, struct duinfo const *b)
> >>>>  {
> >>>> +  uintmax_t i = 0;
> >>>>    uintmax_t sum = a->size + b->size;
> >>>>    a->size = a->size <= sum ? sum : UINTMAX_MAX;
> >>>>    a->inodes = a->inodes + b->inodes;
> >>>> +  if (opt_group_sizes)
> >>>> +    {
> >>>> +      for (i=0; i<65535; i++)
> >>>> +        {
> >>>> +          sum = a->group_size[i] + b->group_size[i];
> >>>> +          a->group_size[i] = a->group_size[i] <= sum ? sum :
> UINTMAX_MAX;
> >>>> +        }
> >>>> +    }
> >>>>    if (timespec_cmp (a->tmax, b->tmax) < 0)
> >>>>      a->tmax = b->tmax;
> >>>>  }
> >>>> @@ -226,6 +264,7 @@ static struct option const long_options[] =
> >>>>    {"exclude", required_argument, NULL, EXCLUDE_OPTION},
> >>>>    {"exclude-from", required_argument, NULL, 'X'},
> >>>>    {"files0-from", required_argument, NULL, FILES0_FROM_OPTION},
> >>>> +  {"group-reporting", no_argument, NULL, 'g'},
> >>>>    {"human-readable", no_argument, NULL, 'h'},
> >>>>    {"inodes", no_argument, NULL, INODES_OPTION},
> >>>>    {"si", no_argument, NULL, HUMAN_SI_OPTION},
> >>>> @@ -317,6 +356,7 @@ Summarize disk usage of the set of FILEs,
> >>>> recursively for directories.\n\
> >>>>        --files0-from=F   summarize disk usage of the\n\
> >>>>                            NUL-terminated file names specified in
> file F;\n\
> >>>>                            if F is -, then read names from standard
> input\n\
> >>>> +  -g, --group-reporting also print group subtotals\n\
> >>>>    -H                    equivalent to --dereference-args (-D)\n\
> >>>>    -h, --human-readable  print sizes in human readable format (e.g.,
> >>>> 1K 234M 2G)\
> >>>>  \n\
> >>>> @@ -411,7 +451,24 @@ print_size (const struct duinfo *pdui, const
> char *string)
> >>>>    print_only_size (opt_inodes
> >>>>                     ? pdui->inodes
> >>>>                     : pdui->size);
> >>>> -
> >>>> +  if (opt_group_sizes)
> >>>> +    {
> >>>> +      uintmax_t i=0;
> >>>> +      struct group *g;
> >>>> +      for (i=0; i<65535; i++){
> >>>> +        if (pdui->group_size[i] > 0)
> >>>> +          {
> >>>> +            g = getgrgid(i);
> >>>> +            printf (",");
> >>>> +            if (g == NULL){
> >>>> +              printf (" %Ld:", (long long unsigned int)i);
> >>>> +            }else{
> >>>> +              printf(" %s:", g->gr_name);
> >>>> +            }
> >>>> +            print_only_size(pdui->group_size[i]);
> >>>> +          }
> >>>> +      }
> >>>> +    }
> >>>>    if (opt_time)
> >>>>      {
> >>>>        putchar ('\t');
> >>>> @@ -506,6 +563,12 @@ process_file (FTS *fts, FTSENT *ent)
> >>>>    const struct stat *sb = ent->fts_statp;
> >>>>    int info = ent->fts_info;
> >>>>
> >>>> +  if(opt_group_sizes)
> >>>> +    {
> >>>> +      dui.group_size = xcalloc (65536, sizeof (uintmax_t));
> >>>> +      dui_to_print.group_size = xcalloc (65536, sizeof (uintmax_t));
> >>>> +    }
> >>>> +
> >>>>    if (info == FTS_DNR)
> >>>>      {
> >>>>        /* An error occurred, but the size is known, so count it.  */
> >>>> @@ -530,7 +593,18 @@ process_file (FTS *fts, FTSENT *ent)
> >>>>            if (info == FTS_NS || info == FTS_SLNONE)
> >>>>              {
> >>>>                error (0, ent->fts_errno, _("cannot access %s"),
> quoteaf (file));
> >>>> -              return false;
> >>>> +              if(opt_group_sizes)
> >>>> +                {
> >>>> +                  if (dui.group_size != NULL)
> >>>> +                    {
> >>>> +                      free(dui.group_size);
> >>>> +                    }
> >>>> +                  if (dui_to_print.group_size != NULL)
> >>>> +                    {
> >>>> +                      free(dui_to_print.group_size);
> >>>> +                    }
> >>>> +                }
> >>>> +          return false;
> >>>>              }
> >>>>
> >>>>            /* The --one-file-system (-x) option cannot exclude
> anything
> >>>> @@ -558,13 +632,34 @@ process_file (FTS *fts, FTSENT *ent)
> >>>>                FTSENT const *e = fts_read (fts);
> >>>>                assert (e == ent);
> >>>>              }
> >>>> -
> >>>> +          if(opt_group_sizes)
> >>>> +            {
> >>>> +              if (dui.group_size != NULL)
> >>>> +                {
> >>>> +                  free(dui.group_size);
> >>>> +                }
> >>>> +              if (dui_to_print.group_size != NULL)
> >>>> +                {
> >>>> +                  free(dui_to_print.group_size);
> >>>> +                }
> >>>> +            }
> >>>>            return true;
> >>>>          }
> >>>>
> >>>>        switch (info)
> >>>>          {
> >>>>          case FTS_D:
> >>>> +      if(opt_group_sizes)
> >>>> +            {
> >>>> +              if (dui.group_size != NULL)
> >>>> +                {
> >>>> +                  free(dui.group_size);
> >>>> +                }
> >>>> +              if (dui_to_print.group_size != NULL)
> >>>> +                {
> >>>> +                  free(dui_to_print.group_size);
> >>>> +                }
> >>>> +            }
> >>>>            return true;
> >>>>
> >>>>          case FTS_ERR:
> >>>> @@ -574,6 +669,17 @@ process_file (FTS *fts, FTSENT *ent)
> >>>>            break;
> >>>>
> >>>>          case FTS_DC:
> >>>> +      if(opt_group_sizes)
> >>>> +            {
> >>>> +              if (dui.group_size != NULL)
> >>>> +                {
> >>>> +                  free(dui.group_size);
> >>>> +                }
> >>>> +              if (dui_to_print.group_size != NULL)
> >>>> +                {
> >>>> +                  free(dui_to_print.group_size);
> >>>> +                }
> >>>> +            }
> >>>>            /* If not following symlinks and not a (bind) mount
> point.  */
> >>>>            if (cycle_warning_required (fts, ent)
> >>>>                && ! mount_point_in_fts_cycle (ent))
> >>>> @@ -591,15 +697,40 @@ process_file (FTS *fts, FTSENT *ent)
> >>>>                 : (uintmax_t) ST_NBLOCKS (*sb) * ST_NBLOCKSIZE),
> >>>>                (time_type == time_mtime ? get_stat_mtime (sb)
> >>>>                 : time_type == time_atime ? get_stat_atime (sb)
> >>>> -               : get_stat_ctime (sb)));
> >>>> +               : get_stat_ctime (sb)),
> >>>> +           sb->st_gid);
> >>>>
> >>>>    level = ent->fts_level;
> >>>> -  dui_to_print = dui;
> >>>> +
> >>>> +  if (opt_group_sizes)
> >>>> +    {
> >>>> +      duinfo_set (&dui_to_print,
> >>>> +              (apparent_size
> >>>> +               ? MAX (0, sb->st_size)
> >>>> +               : (uintmax_t) ST_NBLOCKS (*sb) * ST_NBLOCKSIZE),
> >>>> +              (time_type == time_mtime ? get_stat_mtime (sb)
> >>>> +               : time_type == time_atime ? get_stat_atime (sb)
> >>>> +               : get_stat_ctime (sb)),
> >>>> +               sb->st_gid);
> >>>> +    }
> >>>> +  else
> >>>> +    {
> >>>> +      dui_to_print = dui;
> >>>> +    }
> >>>>
> >>>>    if (n_alloc == 0)
> >>>>      {
> >>>> +      size_t i;
> >>>>        n_alloc = level + 10;
> >>>>        dulvl = xcalloc (n_alloc, sizeof *dulvl);
> >>>> +      if(opt_group_sizes)
> >>>> +        {
> >>>> +          for (i=0; i<n_alloc; i++)
> >>>> +            {
> >>>> +              dulvl[i].ent.group_size = xcalloc (65536, sizeof
> (uintmax_t));
> >>>> +              dulvl[i].subdir.group_size = xcalloc (65536, sizeof
> (uintmax_t));
> >>>> +            }
> >>>> +        }
> >>>>      }
> >>>>    else
> >>>>      {
> >>>> @@ -613,14 +744,23 @@ process_file (FTS *fts, FTSENT *ent)
> >>>>               Clear the accumulators for *all* levels between
> prev_level
> >>>>               and the current one.  The depth may change dramatically,
> >>>>               e.g., from 1 to 10.  */
> >>>> +          size_t i;
> >>>>
> >>>>            if (n_alloc <= level)
> >>>>              {
> >>>>                dulvl = xnrealloc (dulvl, level, 2 * sizeof *dulvl);
> >>>> +              if(opt_group_sizes)
> >>>> +                {
> >>>> +                  for (i=n_alloc; i<level*2; i++)
> >>>> +                    {
> >>>> +                      dulvl[i].ent.group_size = xcalloc (65536,
> >>>> sizeof (uintmax_t));
> >>>> +                      dulvl[i].subdir.group_size = xcalloc (65536,
> >>>> sizeof (uintmax_t));
> >>>> +                    }
> >>>> +                }
> >>>>                n_alloc = level * 2;
> >>>>              }
> >>>>
> >>>> -          for (size_t i = prev_level + 1; i <= level; i++)
> >>>> +          for (i = prev_level + 1; i <= level; i++)
> >>>>              {
> >>>>                duinfo_init (&dulvl[i].ent);
> >>>>                duinfo_init (&dulvl[i].subdir);
> >>>> @@ -666,6 +806,18 @@ process_file (FTS *fts, FTSENT *ent)
> >>>>          print_size (&dui_to_print, file);
> >>>>      }
> >>>>
> >>>> +  if(opt_group_sizes)
> >>>> +    {
> >>>> +      if (dui.group_size != NULL)
> >>>> +        {
> >>>> +          free(dui.group_size);
> >>>> +        }
> >>>> +      if (dui_to_print.group_size != NULL)
> >>>> +        {
> >>>> +          free(dui_to_print.group_size);
> >>>> +        }
> >>>> +    }
> >>>> +
> >>>>    return ok;
> >>>>  }
> >>>>
> >>>> @@ -755,7 +907,7 @@ main (int argc, char **argv)
> >>>>    while (true)
> >>>>      {
> >>>>        int oi = -1;
> >>>> -      int c = getopt_long (argc, argv, "0abd:chHklmst:xB:DLPSX:",
> >>>> +      int c = getopt_long (argc, argv, "0abgd:chHklmst:xB:DLPSX:",
> >>>>                             long_options, &oi);
> >>>>        if (c == -1)
> >>>>          break;
> >>>> @@ -800,6 +952,11 @@ main (int argc, char **argv)
> >>>>            output_block_size = 1;
> >>>>            break;
> >>>>
> >>>> +        case 'g':
> >>>> +          tot_dui.group_size = xcalloc (65536, sizeof (uintmax_t));
> >>>> +          opt_group_sizes = true;
> >>>> +          break;
> >>>> +
> >>>>          case 'k':
> >>>>            human_output_opts = 0;
> >>>>            output_block_size = 1024;
> >>>> --
> >>>> 2.10.2
> >>>>
> >>>> On Mon, Feb 5, 2018 at 10:14 AM, Daniel Gall <address@hidden>
> wrote:
> >>>>> Thanks. Will resubmit without it.
> >>>>>
> >>>>> Sent from my iPhone
> >>>>>
> >>>>>> On Feb 5, 2018, at 9:20 AM, Eric Blake <address@hidden> wrote:
> >>>>>>
> >>>>>>> On 02/02/2018 09:27 PM, Daniel Gall wrote:
> >>>>>>> Sorry for the delay; life intervened.  In addition to the feature
> add,
> >>>>>>> I found a place where du was calling xcalloc and did not check that
> >>>>>>> the returned pointer was not NULL.  I added a check.
> >>>>>>
> >>>>>> Wrong; the contract of xcalloc() is that it CAN'T return NULL (it
> will
> >>>>>> have abort()ed instead, if you are low on memory).
> >>>>>>
> >>>>>>>> From 544c581654cd0dcfb363215801245a7c2dd3fcd3 Mon Sep 17
> 00:00:00 2001
> >>>>>>> From: Daniel Gall <address@hidden>
> >>>>>>> Date: Fri, 2 Feb 2018 17:18:44 -0500
> >>>>>>> Subject: [PATCH] added du group reporting feature and fixed a bug
> where du
> >>>>>>> allocated memory and did not check that the target pointer was not
> NULL after
> >>>>>>> the allocation call.
> >>>>>>
> >>>>>> Too long of a subject line; the best commits include a one-line
> summary
> >>>>>> (~60 characters or less), then a blank line, then more details.
> >>>>>>
> >>>>>>> +++ b/NEWS
> >>>>>>> @@ -32,6 +32,8 @@ GNU coreutils NEWS
> >>>>>>> -*- outline -*-
> >>>>>>>   df no longer hangs when given a fifo argument.
> >>>>>>>   [bug introduced in coreutils-7.3]
> >>>>>>>
> >>>>>>> +  du no longer allocates memory without checking whether the
> >>>>>>> allocation call succeeded.
> >>>>>>
> >>>>>> This change is not needed, as it was not a bug in the first place.
> >>>>>>
> >>>>>> --
> >>>>>> Eric Blake, Principal Software Engineer
> >>>>>> Red Hat, Inc.           +1-919-301-3266
> >>>>>> Virtualization:  qemu.org | libvirt.org
> >>>>>>
>


reply via email to

[Prev in Thread] Current Thread [Next in Thread]