"Fossies" - the Fresh Open Source Software Archive

Member "quotactl-1.00/mkquota.c" (29 Sep 2005, 8063 Bytes) of package /linux/privat/old/quotactl-1.00.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "mkquota.c" see the Fossies "Dox" file reference documentation.

    1 /*============================================================================
    2                                 mkquota
    3 ==============================================================================
    4   Generate a Linux disk quota file.
    5 ============================================================================*/
    6 
    7 /* 
    8    Format of the disk quota file:
    9 
   10    File is composed of 1K blocks.  Some of them contain data,
   11    some are free.
   12 
   13    In the first block:
   14 
   15      Header: 8 bytes
   16        4 bytes: magic number:
   17        4 bytes: file format version:
   18      Info structure: 24 bytes
   19        4 bytes: default grace time for block count overage
   20        4 bytes: default grace time for file count overage
   21        4 bytes: flags
   22        4 bytes: number of blocks in file
   23        4 bytes: number of first free block; zero means none.
   24        4 bytes: number of first block with at least one free entry;
   25                 zero means none.
   26 
   27    Subsequent blocks: the tree of individual quotas.  Each block is
   28    either a pointer block or a quota block.  A pointer block contains
   29    256 pointers of a trie as explained below.
   30 
   31 
   32    A quota block contains 48 byte entries, one entry per individual
   33    quota, plus a 16 byte block header.  At least one of the slots in
   34    the block is occupied.  An unoccupied slot is all zeroes.  Note that
   35    it is impossible to have a quota entry with all zero values, because
   36    the situation that would represent is really represented by the
   37    absence of a quota entry.
   38 
   39    There is an entry for each ID.  An ID is a uid if it is a user quota
   40    file; a gid if it is a group quota file.
   41 
   42    These entries are arranged in a trie of depth 4 (V2_DQTREEDEPTH).
   43    That's a tree that is 4 ranks deep.  The 1st rank is a block of 256
   44    pointers.  Each of those points to a block in the 2nd rank.  Each
   45    of those blocks is 256 pointers into the 3rd rank, and so on.  The
   46    pointers in the 4th rank point to the quota block.
   47 
   48    The root block of the trie (the single block that composes the
   49    1st rank) is the 2nd block of the file.
   50 
   51    The "number of blocks in file" is consistent with the file's filesize.
   52    There are always at least two blocks in the file; i.e. the root
   53    block of the trie is always present.  If there is at least one quota
   54    in the file, then there are at least 6 blocks, because there is
   55    a header block, a trie block for each of the 4 ranks, and a block
   56    containing the quota entry.
   57    
   58    A pointer is a block number in the file (relative to the beginning of
   59    the file, not the beginning of the trie block region).
   60 
   61    To get the indices into the pointer block at each rank, we just divide
   62    the ID into 4 bytes.  The first byte is the index for the 1st rank;
   63    the 2nd byte is the index for the 2nd rank, etc.
   64 
   65    Integers are all same endian as the machine (which is, of course, a 
   66    problem if you use the same filesystem from multiple machines).  */
   67 
   68 #define _GNU_SOURCE
   69 
   70 #include <stdbool.h>
   71 #include <inttypes.h>
   72 #include <string.h>
   73 #include <unistd.h>
   74 #include <stdlib.h>
   75 #include <stdio.h>
   76 #include <errno.h>
   77 
   78 #include <linux/quota.h>
   79 
   80 #include <giraffe/girstring.h>
   81 #include <giraffe/cmdline_parser.h>
   82 
   83 #define ATTR_UNUSED __attribute__((__unused__))
   84 
   85 static __u32 const magics[MAXQUOTAS] = INITQMAGICS;
   86 static __u32 const versions[MAXQUOTAS] = INITQVERSIONS;
   87 
   88 enum userGroup {USER, GROUP};
   89 
   90 struct cmdlineInfo {
   91     enum userGroup userGroup;
   92 };
   93 
   94 
   95 
   96 static void
   97 parseCommandLine(int                  const argc,
   98                  char **              const argv,
   99                  struct cmdlineInfo * const cmdlineP,
  100                  const char **        const errorP) {
  101 
  102     cmdlineParser const cp = cmd_createOptionParser();
  103 
  104     const char * error;
  105 
  106     cmd_defineOption(cp, "user",        OPTTYPE_FLAG);
  107     cmd_defineOption(cp, "group",       OPTTYPE_FLAG);
  108 
  109     cmd_processOptions(cp, argc, argv, &error);
  110 
  111     if (error) {
  112         casprintf(errorP, "Command syntax error.  %s", error);
  113         strfree(error);
  114     } else {
  115         *errorP = NULL;  // initial value
  116 
  117         if (cmd_optionIsPresent(cp, "user") &&
  118             cmd_optionIsPresent(cp, "group"))
  119             casprintf(errorP, "You may not specify both -user and -group");
  120         else {
  121             if (cmd_optionIsPresent(cp, "group"))
  122                 cmdlineP->userGroup = GROUP;
  123             else if (cmd_optionIsPresent(cp, "user"))
  124                 cmdlineP->userGroup = USER;
  125             else
  126                 casprintf(errorP, "You must specify either -user or -group");
  127         }
  128     }
  129     cmd_destroyOptionParser(cp);
  130 }
  131 
  132 
  133 
  134 static void
  135 freeCmdline(struct cmdlineInfo const cmdline ATTR_UNUSED) {
  136 
  137 }
  138 
  139 
  140 
  141 static void
  142 setFilesize(const char ** const errorP) {
  143 /*----------------------------------------------------------------------------
  144    Set the filesize to encompass the header blocks and one block of trie.
  145 
  146    Note that any blocks we don't explicitly write contain implicit zeroes,
  147    which is exactly the value we want for most of the file.
  148 -----------------------------------------------------------------------------*/
  149     int rc;
  150 
  151     /* This is actually kind of sleazy -- This program accesses stdout as
  152        a stream, so it shouldn't be accessing the underlying file descriptor
  153        here.
  154     */
  155     rc = ftruncate(fileno(stdout), (DQTREEOFF + 1) * QUOTABLOCK_SIZE);
  156 
  157     if (rc < 0)
  158         casprintf(errorP, "Unable to set file size.  ftruncate() "
  159                   "fails with errno %d (%s)", errno, strerror(errno));
  160     else
  161         *errorP = NULL;
  162 }
  163 
  164 
  165 
  166 static void
  167 writeHeader(enum userGroup const userGroup,
  168             const char **  const errorP) {
  169 
  170     struct disk_dqheader header;
  171     size_t bytesWritten;
  172     
  173     header.dqh_magic   = magics[userGroup];
  174     header.dqh_version = versions[userGroup];
  175     
  176     bytesWritten = fwrite(&header, 1, sizeof(header), stdout);
  177     
  178     if (bytesWritten != sizeof(header))
  179         casprintf(errorP, "write() failed.\n");
  180     else
  181         *errorP = NULL;
  182 }
  183 
  184 
  185 
  186 static void
  187 writeInfo(const char **  const errorP) {
  188 
  189     time_t const oneWeek = 7 * 86400;
  190 
  191     struct disk_dqinfo info;
  192     size_t bytesWritten;
  193     
  194     info.dqi_bgrace = oneWeek;
  195     info.dqi_igrace = oneWeek;
  196     info.dqi_flags  = 0;
  197     info.dqi_blocks = DQTREEOFF + 1;
  198         /* One block of tree.  Note that by default, if we write nothing
  199            else, this block contains all zeroes.
  200         */
  201     info.dqi_free_blk = 0;
  202     info.dqi_free_entry = 0;
  203     
  204     bytesWritten = fwrite(&info, 1, sizeof(info), stdout);
  205     
  206     if (bytesWritten != sizeof(info))
  207         casprintf(errorP, "write() failed.\n");
  208     else
  209         *errorP = NULL;
  210 }
  211 
  212 
  213 
  214 int
  215 main(int argc, char **argv) {
  216 
  217     struct cmdlineInfo cmdline;
  218 
  219     int retval;
  220     const char * mainError;
  221     const char * error;
  222 
  223     parseCommandLine(argc, argv, &cmdline, &error);
  224 
  225     if (error) {
  226         casprintf(&mainError, "Error parsing command line:  %s", error);
  227         strfree(error);
  228     } else {
  229         const char * error;
  230         setFilesize(&error);
  231         if (error) {
  232             casprintf(&mainError, "Unable to set the filesize of Stdout.  %s",
  233                       error);
  234             strfree(error);
  235         } else {
  236             const char * error;
  237             writeHeader(cmdline.userGroup, &error);
  238             if (error) {
  239                 casprintf(&mainError,
  240                           "Unable to write header to quota file.  %s",
  241                           error);
  242                 strfree(error);
  243             } else {
  244                 const char * error;
  245                 writeInfo(&error);
  246                 if (error) {
  247                     casprintf(&mainError, "Unable to write info structure to "
  248                               "quota file.  %s", error);
  249                     strfree(error);
  250                 } else
  251                     mainError = NULL;
  252             }
  253         }
  254         freeCmdline(cmdline);
  255     }
  256         
  257     if (mainError) {
  258         fprintf(stderr, "Error parsing command line:  %s\n", mainError);
  259         strfree(mainError);
  260         retval = 1;
  261     } else
  262         retval = 0;
  263 
  264     return retval;
  265 }