"Fossies" - the Fresh Open Source Software Archive

Member "quotactl-1.00/quotause/quotaio_v2.c" (9 Oct 2005, 33330 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 "quotaio_v2.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2  *  Implementation of new quotafile format
    3  *
    4  *  Jan Kara <jack@suse.cz> - sponsored by SuSE CR
    5  */
    6 
    7 /* See comments at the top of the 'mkquota' program source code for the
    8    format of the quota file.
    9 */
   10 
   11 #define _FILE_OFFSET_BITS 64
   12 
   13 #include <stdbool.h>
   14 #include <assert.h>
   15 #include <sys/types.h>
   16 #include <errno.h>
   17 #include <stdio.h>
   18 #include <stdlib.h>
   19 #include <string.h>
   20 #include <unistd.h>
   21 #include <asm/byteorder.h>
   22 
   23 #include <giraffe/girtypes.h>
   24 #include <giraffe/fatal.h>
   25 #include <giraffe/mallocvar.h>
   26 
   27 #include "quotaio_v2.h"
   28 #include "dqblk_v2.h"
   29 #include "quotaio.h"
   30 #include "misc.h"
   31 
   32 
   33 static unsigned int const file_magics[] = {
   34     [USRQUOTA] = 0xd9c01f11,
   35     [GRPQUOTA] = 0xd9c01927,
   36 };
   37 
   38 /* Latest known versions */
   39 #define INITKNOWNVERSIONS {\
   40     0,\
   41     0\
   42     }
   43 
   44 static unsigned int const known_versions[MAXQUOTAS] = INITKNOWNVERSIONS;
   45     /* Versions we accept */
   46 
   47 
   48 /*
   49  *  Copy dquot from disk to memory
   50  */
   51 static inline void
   52 v2_disk2memdqblk(struct util_dqblk *    const m,
   53                  struct v2_disk_dqblk * const d) {
   54     m->dqb_ihardlimit = __le32_to_cpu(d->dqb_ihardlimit);
   55     m->dqb_isoftlimit = __le32_to_cpu(d->dqb_isoftlimit);
   56     m->dqb_bhardlimit = __le32_to_cpu(d->dqb_bhardlimit);
   57     m->dqb_bsoftlimit = __le32_to_cpu(d->dqb_bsoftlimit);
   58     m->dqb_curinodes = __le32_to_cpu(d->dqb_curinodes);
   59     m->dqb_curspace = __le64_to_cpu(d->dqb_curspace);
   60     m->dqb_itime = __le64_to_cpu(d->dqb_itime);
   61     m->dqb_btime = __le64_to_cpu(d->dqb_btime);
   62 }
   63 
   64 
   65 
   66 /*
   67  *  Copy dquot from memory to disk
   68  */
   69 static inline void
   70 v2_mem2diskdqblk(struct v2_disk_dqblk * const d,
   71                  struct util_dqblk *    const m) {
   72 
   73     d->dqb_ihardlimit = __cpu_to_le32(m->dqb_ihardlimit);
   74     d->dqb_isoftlimit = __cpu_to_le32(m->dqb_isoftlimit);
   75     d->dqb_bhardlimit = __cpu_to_le32(m->dqb_bhardlimit);
   76     d->dqb_bsoftlimit = __cpu_to_le32(m->dqb_bsoftlimit);
   77     d->dqb_curinodes = __cpu_to_le32(m->dqb_curinodes);
   78     d->dqb_curspace = __cpu_to_le64(m->dqb_curspace);
   79     d->dqb_itime = __cpu_to_le64(m->dqb_itime);
   80     d->dqb_btime = __cpu_to_le64(m->dqb_btime);
   81 }
   82 
   83 
   84 
   85 /*
   86  *  Copy dqinfo from disk to memory
   87  */
   88 static inline void
   89 v2_disk2memdqinfo(struct util_dqinfo *    const m,
   90                   struct v2_disk_dqinfo * const d) {
   91     m->dqi_bgrace = __le32_to_cpu(d->dqi_bgrace);
   92     m->dqi_igrace = __le32_to_cpu(d->dqi_igrace);
   93     m->u.v2_mdqi.dqi_flags = __le32_to_cpu(d->dqi_flags) & V2_DQF_MASK;
   94     m->u.v2_mdqi.dqi_blocks = __le32_to_cpu(d->dqi_blocks);
   95     m->u.v2_mdqi.dqi_free_blk = __le32_to_cpu(d->dqi_free_blk);
   96     m->u.v2_mdqi.dqi_free_entry = __le32_to_cpu(d->dqi_free_entry);
   97 }
   98 
   99 
  100 
  101 /*
  102  *  Copy dqinfo from memory to disk
  103  */
  104 static inline void
  105 v2_mem2diskdqinfo(struct v2_disk_dqinfo * const d,
  106                   struct util_dqinfo *    const m) {
  107 
  108     d->dqi_bgrace = __cpu_to_le32(m->dqi_bgrace);
  109     d->dqi_igrace = __cpu_to_le32(m->dqi_igrace);
  110     d->dqi_flags = __cpu_to_le32(m->u.v2_mdqi.dqi_flags & V2_DQF_MASK);
  111     d->dqi_blocks = __cpu_to_le32(m->u.v2_mdqi.dqi_blocks);
  112     d->dqi_free_blk = __cpu_to_le32(m->u.v2_mdqi.dqi_free_blk);
  113     d->dqi_free_entry = __cpu_to_le32(m->u.v2_mdqi.dqi_free_entry);
  114 }
  115 
  116 
  117 
  118 /* Convert kernel quotablock format to utility one */
  119 static inline void
  120 v2_kern2utildqblk(struct util_dqblk *    const u,
  121                   struct v2_kern_dqblk * const k) {
  122 
  123     u->dqb_ihardlimit = k->dqb_ihardlimit;
  124     u->dqb_isoftlimit = k->dqb_isoftlimit;
  125     u->dqb_bhardlimit = k->dqb_bhardlimit;
  126     u->dqb_bsoftlimit = k->dqb_bsoftlimit;
  127     u->dqb_curinodes = k->dqb_curinodes;
  128     u->dqb_curspace = k->dqb_curspace;
  129     u->dqb_itime = k->dqb_itime;
  130     u->dqb_btime = k->dqb_btime;
  131 }
  132 
  133 
  134 
  135 /* Convert utility quotablock format to kernel one */
  136 static inline void 
  137 v2_util2kerndqblk(struct v2_kern_dqblk * const k,
  138                   struct util_dqblk *    const u) {
  139     k->dqb_ihardlimit = u->dqb_ihardlimit;
  140     k->dqb_isoftlimit = u->dqb_isoftlimit;
  141     k->dqb_bhardlimit = u->dqb_bhardlimit;
  142     k->dqb_bsoftlimit = u->dqb_bsoftlimit;
  143     k->dqb_curinodes = u->dqb_curinodes;
  144     k->dqb_curspace = u->dqb_curspace;
  145     k->dqb_itime = u->dqb_itime;
  146     k->dqb_btime = u->dqb_btime;
  147 }
  148 
  149 
  150 
  151 /* Is given dquot empty? */
  152 static bool
  153 dquotIsEmpty(const struct v2_disk_dqblk * const d) {
  154     static struct v2_disk_dqblk emptydquot;
  155         /* Loader initializes this to all zeroes */
  156 
  157     return memcmp(d, &emptydquot, sizeof(emptydquot)) == 0;
  158 }
  159 
  160 
  161 
  162 /*
  163  *  Check whether given quota file is in our format
  164  */
  165 static bool
  166 v2_check_file(int const fd,
  167               int const type) {
  168 /*----------------------------------------------------------------------------
  169    Return true iff the file on file descriptor 'fd' appears to be in our
  170    format.
  171 -----------------------------------------------------------------------------*/
  172     struct v2_disk_dqheader h;
  173     unsigned int known_versions[] = INIT_V2_VERSIONS;
  174 
  175     bool retval;
  176 
  177     lseek(fd, 0, SEEK_SET);
  178     if (read(fd, &h, sizeof(h)) != sizeof(h))
  179         retval = false;
  180     else {
  181         if (__le32_to_cpu(h.dqh_magic) != file_magics[type]) {
  182             if (__be32_to_cpu(h.dqh_magic) == file_magics[type])
  183                 fatal("Your quota file is stored in wrong endianity.  "
  184                       "Please use 'convertquota' to convert it.");
  185             retval = false;
  186         } else if (__le32_to_cpu(h.dqh_version) > known_versions[type])
  187             retval = false;
  188         else
  189             retval = true;
  190     }
  191     return retval;
  192 }
  193 
  194 
  195 
  196 /*
  197  *  Initialize new quotafile
  198  */
  199 static int
  200 v2_create_file(struct quota_handle * const quotaFileP) {
  201 
  202     int known_versions[] = INIT_V2_VERSIONS;
  203     struct v2_disk_dqheader ddqheader;
  204     struct v2_disk_dqinfo ddqinfo;
  205     int retval;
  206     ssize_t rc;
  207 
  208     /* Write basic quota header */
  209     ddqheader.dqh_magic = __cpu_to_le32(file_magics[quotaFileP->qh_type]);
  210     ddqheader.dqh_version = __cpu_to_le32(known_versions[quotaFileP->qh_type]);
  211     lseek(quotaFileP->qh_fd, 0, SEEK_SET);
  212     rc = write(quotaFileP->qh_fd, &ddqheader, sizeof(ddqheader));
  213     if (rc != sizeof(ddqheader))
  214         retval = -1;
  215     else {
  216         /* Write information about quotafile */
  217         ssize_t rc;
  218 
  219         quotaFileP->qh_info.dqi_bgrace = MAX_DQ_TIME;
  220         quotaFileP->qh_info.dqi_igrace = MAX_IQ_TIME;
  221         quotaFileP->qh_info.u.v2_mdqi.dqi_flags = 0;
  222         quotaFileP->qh_info.u.v2_mdqi.dqi_blocks = V2_DQTREEOFF + 1;
  223         quotaFileP->qh_info.u.v2_mdqi.dqi_free_blk = 0;
  224         quotaFileP->qh_info.u.v2_mdqi.dqi_free_entry = 0;
  225         v2_mem2diskdqinfo(&ddqinfo, &quotaFileP->qh_info);
  226         lseek(quotaFileP->qh_fd, V2_DQINFOOFF, SEEK_SET);
  227         rc = write(quotaFileP->qh_fd, &ddqinfo, sizeof(ddqinfo));
  228         if (rc != sizeof(ddqinfo))
  229             retval = -1;
  230         else
  231             retval = 0;
  232     }
  233     return retval;
  234 }
  235 
  236 
  237 
  238 static int
  239 v2_init_read(struct quota_handle * const quotaFileP) {
  240 /*----------------------------------------------------------------------------
  241    Open existing file.
  242 -----------------------------------------------------------------------------*/
  243     int retval;
  244     struct v2_disk_dqinfo ddqinfo;
  245     int rc;
  246     
  247     lseek(quotaFileP->qh_fd, V2_DQINFOOFF, SEEK_SET);
  248     rc = read(quotaFileP->qh_fd, &ddqinfo, sizeof(ddqinfo));
  249     if (rc != sizeof(ddqinfo))
  250         retval = -1;
  251     else {
  252         retval = 0;
  253 
  254         v2_disk2memdqinfo(&quotaFileP->qh_info, &ddqinfo);
  255     }
  256     return retval;
  257 }
  258 
  259 
  260 
  261 /*
  262  *  Write information (grace times to file)
  263  */
  264 static int
  265 v2_write_info(struct quota_handle * const quotaFileP) {
  266 
  267     struct v2_disk_dqinfo ddqinfo;
  268 
  269     v2_mem2diskdqinfo(&ddqinfo, &quotaFileP->qh_info);
  270     lseek(quotaFileP->qh_fd, V2_DQINFOOFF, SEEK_SET);
  271     if (write(quotaFileP->qh_fd, &ddqinfo, sizeof(ddqinfo)) != sizeof(ddqinfo))
  272         return -1;
  273     return 0;
  274 }
  275 
  276 
  277 
  278 /* Read given block */
  279 static void
  280 read_blk(struct quota_handle * const quotaFileP,
  281          uint                  const blk,
  282          dqbuf *               const bufP) {
  283 
  284     ssize_t rc;
  285 
  286     lseek(quotaFileP->qh_fd, blk * sizeof(*bufP), SEEK_SET);
  287     rc = read(quotaFileP->qh_fd, bufP, sizeof(*bufP));
  288     if (rc < 0)
  289         fatal("Can't read block %u.  errno=%d (%s)",
  290               blk, errno, strerror(errno));
  291     else {
  292         size_t const bytesRead = rc;
  293 
  294         if (bytesRead != sizeof(*bufP))
  295             memset(&bufP->bytes[0] + bytesRead, 0, sizeof(*bufP) - bytesRead);
  296     }
  297 }
  298 
  299 
  300 
  301 static int
  302 write_blk(struct quota_handle * const quotaFileP,
  303           uint                  const blk,
  304           const dqbuf *         const bufP) {
  305 /*----------------------------------------------------------------------------
  306    Write a block to the quota file.  'blk' is the block number (0 is the
  307    first block of the file).  'buf' is the raw contents of the block.
  308    'quotaFileP' is the image of the quota file through which we write it.
  309 -----------------------------------------------------------------------------*/
  310     int retval;
  311     ssize_t rc;
  312 
  313     lseek(quotaFileP->qh_fd, blk * V2_DQBLKSIZE, SEEK_SET);
  314 debug("writing %u bytes at offset %u", sizeof(*bufP), blk*V2_DQBLKSIZE);
  315     rc = write(quotaFileP->qh_fd, bufP, sizeof(*bufP));
  316     if (rc < 0)
  317         retval = -1;
  318     else {
  319         size_t const bytesWritten = rc;
  320  
  321         if (bytesWritten != sizeof(*bufP)) {
  322             errno = ENOSPC;
  323             retval = -1;
  324         } else
  325             retval = 0;
  326     }
  327     return retval;
  328 }
  329 
  330 
  331 
  332 static int
  333 getFreeDqblk(struct quota_handle * const quotaFileP) {
  334 /*----------------------------------------------------------------------------
  335    Allocate a free dquot block (one containing no dquots) in the file.
  336 -----------------------------------------------------------------------------*/
  337     struct v2_mem_dqinfo * const infoP = &quotaFileP->qh_info.u.v2_mdqi;
  338 
  339     int retval;
  340     int blk;
  341     int error;
  342 
  343     if (infoP->dqi_free_blk) {
  344         /* There's an interior block free.  Return that. */
  345         dqbuf buf;
  346         const struct v2_disk_dqdbheader * const dh =
  347             (struct v2_disk_dqdbheader *)&buf.bytes[0];
  348 
  349         blk = infoP->dqi_free_blk;
  350         read_blk(quotaFileP, blk, &buf);
  351         infoP->dqi_free_blk = __le32_to_cpu(dh->dqdh_next_free);
  352         error = 0;
  353     } else {
  354         /* There's no interior block free, so we extend the file by one
  355            block and return the new block.
  356         */
  357         dqbuf buf;
  358         int rc;
  359 
  360         memset(&buf, 0, sizeof(buf));
  361         blk = infoP->dqi_blocks++;
  362         /* Assure block allocation... */
  363         rc = write_blk(quotaFileP, blk, &buf);
  364         if (rc < 0) {
  365             msg("Can't allocate new quota block (out of disk space).");
  366             error = -ENOSPC;
  367         } else
  368             error = 0;
  369     }
  370     mark_quotafile_info_dirty(quotaFileP);
  371 
  372     if (error)
  373         retval = error;
  374     else
  375         retval = blk;
  376 
  377     return retval;
  378 }
  379 
  380 
  381 
  382 /* Put given block to free list */
  383 static void
  384 put_free_dqblk(struct quota_handle * const quotaFileP,
  385                dqbuf *               const bufP,
  386                uint                  const blk) {
  387 
  388     struct v2_disk_dqdbheader * const dh =
  389         (struct v2_disk_dqdbheader *)bufP;
  390     struct v2_mem_dqinfo * const infoP = &quotaFileP->qh_info.u.v2_mdqi;
  391 
  392     dh->dqdh_next_free = __cpu_to_le32(infoP->dqi_free_blk);
  393     dh->dqdh_prev_free = __cpu_to_le32(0);
  394     dh->dqdh_entries = __cpu_to_le16(0);
  395     infoP->dqi_free_blk = blk;
  396     mark_quotafile_info_dirty(quotaFileP);
  397     write_blk(quotaFileP, blk, bufP);
  398 }
  399 
  400 
  401 
  402 /* Remove given block from the list of blocks with free entries */
  403 static void
  404 remove_free_dqentry(struct quota_handle * const quotaFileP,
  405                     const dqbuf *         const bufP,
  406                     uint                  const blk) {
  407 
  408     dqbuf tmpbuf;
  409     struct v2_disk_dqdbheader * const dh =
  410         (struct v2_disk_dqdbheader *)&bufP->bytes[0];
  411     struct v2_disk_dqdbheader * const tmpdh =
  412         (struct v2_disk_dqdbheader *)&tmpbuf.bytes[0];
  413     uint const nextblk = __le32_to_cpu(dh->dqdh_next_free);
  414     uint const prevblk = __le32_to_cpu(dh->dqdh_prev_free);
  415 
  416     if (nextblk) {
  417         read_blk(quotaFileP, nextblk, &tmpbuf);
  418         tmpdh->dqdh_prev_free = dh->dqdh_prev_free;
  419         write_blk(quotaFileP, nextblk, &tmpbuf);
  420     }
  421     if (prevblk) {
  422         read_blk(quotaFileP, prevblk, &tmpbuf);
  423         tmpdh->dqdh_next_free = dh->dqdh_next_free;
  424         write_blk(quotaFileP, prevblk, &tmpbuf);
  425     }
  426     else {
  427         quotaFileP->qh_info.u.v2_mdqi.dqi_free_entry = nextblk;
  428         mark_quotafile_info_dirty(quotaFileP);
  429     }
  430     dh->dqdh_next_free = dh->dqdh_prev_free = __cpu_to_le32(0);
  431     write_blk(quotaFileP, blk, bufP);
  432         /* No matter whether write succeeds block is out of list */
  433 }
  434 
  435 
  436 
  437 /* Insert given block to the beginning of list with free entries */
  438 static void
  439 insert_free_dqentry(struct quota_handle * const quotaFileP,
  440                     dqbuf *               const bufP,
  441                     uint                  const blk) {
  442 
  443     dqbuf tmpbuf;
  444     struct v2_disk_dqdbheader * const dh =
  445         (struct v2_disk_dqdbheader *)&bufP->bytes[0];
  446     struct v2_disk_dqdbheader * const tmpdh =
  447         (struct v2_disk_dqdbheader *)&tmpbuf.bytes[0];
  448     struct v2_mem_dqinfo * const infoP = &quotaFileP->qh_info.u.v2_mdqi;
  449 
  450     dh->dqdh_next_free = __cpu_to_le32(infoP->dqi_free_entry);
  451     dh->dqdh_prev_free = __cpu_to_le32(0);
  452     write_blk(quotaFileP, blk, bufP);
  453     if (infoP->dqi_free_entry) {
  454         read_blk(quotaFileP, infoP->dqi_free_entry, &tmpbuf);
  455         tmpdh->dqdh_prev_free = __cpu_to_le32(blk);
  456         write_blk(quotaFileP, infoP->dqi_free_entry, &tmpbuf);
  457     }
  458     infoP->dqi_free_entry = blk;
  459     mark_quotafile_info_dirty(quotaFileP);
  460 }
  461 
  462 
  463 
  464 static void
  465 getFreeDqEntryInBlock(struct quota_handle * const quotaFileP,
  466                       uint                  const blk,
  467                       dqbuf *               const bufP,
  468                       loff_t *              const offsetP) {
  469 /*----------------------------------------------------------------------------
  470    Assuming there is at least one free entry frame in block number
  471    'blk' of quota file 'quotaFileP', allocate it and return its offset
  472    in the file as *offsetP.
  473 
  474    *bufP is the cached contents of block number 'blk'.  We update it as
  475    part of performing the allocation.
  476 -----------------------------------------------------------------------------*/
  477     struct v2_disk_dqdbheader * const dhP = 
  478         (struct v2_disk_dqdbheader *)&bufP->bytes[0];
  479     struct v2_disk_dqblk * const ddquot =  /* array */
  480         V2_GETENTRIES(bufP);
  481 
  482     unsigned int i;
  483 
  484     if ((unsigned)__le16_to_cpu(dhP->dqdh_entries) + 1 >= V2_DQSTRINBLK)
  485         /* Adding the entry makes the block full */
  486         remove_free_dqentry(quotaFileP, bufP, blk);
  487 
  488     dhP->dqdh_entries = __cpu_to_le16(__le16_to_cpu(dhP->dqdh_entries) + 1);
  489     
  490     /* Find an empty frame in the block */
  491     for (i = 0; i < V2_DQSTRINBLK && !dquotIsEmpty(&ddquot[i]); ++i);
  492     
  493     if (i == V2_DQSTRINBLK)
  494         fatal("INTERNAL ERROR: Quota block %u full but shouldn't be", blk);
  495 
  496     /* Fill the entry frame with all 1's, just to show that it is not
  497        free anymore.  Note that all zeroes has the special meaning that
  498        the frame is empty.
  499     */
  500     memset(&ddquot[i], 0xff, sizeof(ddquot[i]));
  501     
  502     *offsetP = blk * V2_DQBLKSIZE +
  503         sizeof(struct v2_disk_dqdbheader) +
  504         i * sizeof(struct v2_disk_dqblk);
  505 }    
  506 
  507 
  508 
  509 static void
  510 getFreeDqentry(struct quota_handle * const quotaFileP,
  511                uint *                const blockNumP,
  512                loff_t *              const offsetP,
  513                int *                 const errorP) {
  514 /*----------------------------------------------------------------------------
  515    Allocate a frame for a dquot.
  516 
  517    Return the offset in the file of the frame as *offsetP, and the
  518    block number that contains it as *blockNumP.
  519 -----------------------------------------------------------------------------*/
  520     struct v2_mem_dqinfo * const infoP = &quotaFileP->qh_info.u.v2_mdqi;
  521 
  522     dqbuf buf;
  523     uint blk;
  524 
  525     *errorP = 0;
  526     if (infoP->dqi_free_entry) {
  527         blk = infoP->dqi_free_entry;
  528         read_blk(quotaFileP, blk, &buf);
  529     } else {
  530         /* There is no partially used block in the file, so we get a new
  531            totally empty block
  532         */
  533         int rc;
  534         rc = getFreeDqblk(quotaFileP);
  535         if (rc < 0) {
  536             *errorP = rc;
  537         } else {
  538             blk = rc;
  539             debug("Allocated new empty dquot block %u", blk);
  540             memset(&buf, 0, sizeof(buf));
  541             infoP->dqi_free_entry = blk;
  542             mark_quotafile_info_dirty(quotaFileP);
  543         }
  544     }
  545     if (!*errorP) {
  546         getFreeDqEntryInBlock(quotaFileP, blk, &buf, offsetP);
  547         
  548         write_blk(quotaFileP, blk, &buf);
  549 
  550         *blockNumP = blk;
  551         *errorP = 0;
  552     }
  553 }
  554 
  555 
  556 
  557 /* Insert reference to structure into the trie */
  558 static int
  559 do_insert_tree(struct quota_handle * const quotaFileP,
  560                qid_t                 const id,
  561                uint *                const treeblk,
  562                loff_t *              const offsetP,
  563                uint                  const depth) {
  564 /*----------------------------------------------------------------------------
  565    Insert a frame for a dquot for id 'id' in the trie, under block
  566    *treeblk, assuming that is the appropriate block for the dquot at
  567    rank 'depth'.
  568 
  569    We don't put a dquot in the file; we just allocate a frame for it
  570    and hook it into the trie.  We return the offset in the file of the
  571    allocated frame as *offsetP.  We fill the frame with arbitrary data,
  572    so if Caller doesn't put the real dquot in it, the file will be
  573    corrupted.
  574 
  575    Special case: if *treeblk is zero, that means there is no block yet
  576    for this dquot in rank 'depth'.  In that case, we create one and
  577    return its block number as *treeblk, and then insert normally under
  578    that block.  This is messy, and we should rewrite this so Caller
  579    creates that block before calling us.
  580 -----------------------------------------------------------------------------*/
  581     dqbuf buf;
  582     bool newson;
  583     bool weGotDqblk;
  584     uint newblk;
  585     int retval;
  586 
  587     weGotDqblk = false;  /* initial assumption */
  588     newson = false;  /* initial assumption */
  589 
  590     if (!*treeblk) {
  591         int rc;
  592 
  593         debug("Making new block in trie for depth %u", depth);
  594         rc = getFreeDqblk(quotaFileP);
  595         if (rc < 0)
  596             return rc;
  597         else {
  598             *treeblk = rc;
  599             memset(&buf, 0, sizeof(buf));
  600             weGotDqblk = true;
  601         }
  602     } else
  603         read_blk(quotaFileP, *treeblk, &buf);
  604 
  605     debug("inserting frame for dquot with id %u under block %u at depth %u",
  606           id, *treeblk, depth);
  607 
  608     newblk = __le32_to_cpu(buf.words[V2_GETIDINDEX(id, depth)]);
  609     if (!newblk)
  610         newson = true;
  611     if (depth == V2_DQTREEDEPTH - 1) {
  612         /* We're at the bottom rank of the tree.  If 'newblk' is nonzero,
  613            that means we've found this exact ID ('id') in the trie.
  614         */
  615         if (newblk)
  616             fatal("Error! Attempt to insert already present quota entry "
  617                   "for ID %u.  It exists already in Block %u.",
  618                   id, buf.words[V2_GETIDINDEX(id, depth)]);
  619         getFreeDqentry(quotaFileP, &newblk, offsetP, &retval);
  620     } else
  621         retval = do_insert_tree(quotaFileP, id, &newblk, offsetP, 
  622                                 depth + 1);
  623 
  624     if (newson && retval >= 0) {
  625         buf.words[V2_GETIDINDEX(id, depth)] = __cpu_to_le32(newblk);
  626         write_blk(quotaFileP, *treeblk, &buf);
  627     }
  628 
  629     if (weGotDqblk && retval < 0)
  630         put_free_dqblk(quotaFileP, &buf, *treeblk);
  631 
  632     return retval;
  633 }
  634 
  635 
  636 
  637 /* Wrapper for inserting quota structure into tree */
  638 static inline void
  639 dq_insert_tree(struct quota_handle * const quotaFileP,
  640                qid_t                 const id,
  641                loff_t *              const offsetP) {
  642 
  643     uint rootBlockNum;
  644 
  645     /* We start the search at the fixed, permanent Rank 0 (depth 0)
  646        block:
  647     */
  648     rootBlockNum = V2_DQTREEOFF;
  649 
  650     /* Note that do_insert_tree() does not modify 'rootBlockNum'.  We
  651        pass it by reference only because we have the recursion screwed
  652        up.  See comments in do_insert_tree() for details.
  653     */
  654 
  655     if (do_insert_tree(quotaFileP, id, &rootBlockNum, offsetP, 0) < 0)
  656         fatal("Can't write quota (id %u). errno=%d (%s)",
  657               (uint) id, errno, strerror(errno));
  658 }
  659 
  660 
  661 
  662 /* Write dquot to file */
  663 static int
  664 v2_write_dquot(struct quota_handle * const quotaFileP,
  665                struct dquot *        const dquotP) {
  666 /*----------------------------------------------------------------------------
  667   Add the dquot *dquotP to the quota file *quotaFileP.
  668 
  669   Assume this will not conflict with a dquot already in the file -- i.e.
  670   there is no dquot for the same ID in the file.
  671 
  672   (There is no interface for updating an exsting dquot).
  673 -----------------------------------------------------------------------------*/
  674     int retval;
  675     struct v2_disk_dqblk ddquot;
  676     loff_t offset;
  677         /* The file offset at which this dquot belongs */
  678     off_t rc;
  679 
  680     if (dquotP->dq_dqb.dqb_ihardlimit == 0 &&
  681         dquotP->dq_dqb.dqb_isoftlimit == 0 &&
  682         dquotP->dq_dqb.dqb_curinodes  == 0 &&
  683         dquotP->dq_dqb.dqb_bhardlimit == 0 &&
  684         dquotP->dq_dqb.dqb_bsoftlimit == 0 &&
  685         dquotP->dq_dqb.dqb_curspace   == 0) {
  686 
  687         debug("Skipping writing of all-zero dquot for id %u", dquotP->dq_id);
  688 
  689         /* All zero limits and usages is represented by no entry at all,
  690            so we needn't do anything.  (In fact, there's no way to represent
  691            an all zero dquot in the file because all zeroes in the file
  692            indicates an empty frame).
  693         */
  694         retval = 0;
  695     } else {
  696         /* Place a frame in the proper place in the tree */
  697         dq_insert_tree(quotaFileP, dquotP->dq_id, &offset);
  698 
  699         /* Fill that frame with the dquot */
  700         v2_mem2diskdqblk(&ddquot, &dquotP->dq_dqb);
  701         ddquot.dqb_id = __cpu_to_le32(dquotP->dq_id);
  702 
  703         assert(!dquotIsEmpty(&ddquot));
  704 
  705         debug("Writing dquot for id %u at offset %llu.",
  706               dquotP->dq_id, offset);
  707         rc = lseek(quotaFileP->qh_fd, offset, SEEK_SET);
  708         if (rc < 0)
  709             retval = -1;
  710         else {
  711             ssize_t rc;
  712 
  713             rc = write(quotaFileP->qh_fd, &ddquot, sizeof(ddquot));
  714             if (rc < 0)
  715                 retval = -1;
  716             else if (rc != sizeof(struct v2_disk_dqblk)) {
  717                 errno = ENOSPC;
  718                 retval = -1;
  719             }
  720         }
  721     }
  722     return retval;
  723 }
  724 
  725 
  726 
  727 /* Free dquot entry in data block */
  728 static void
  729 free_dqentry(struct quota_handle * const quotaFileP,
  730              loff_t                const offset) {
  731 
  732     struct v2_disk_dqdbheader * dh;
  733     dqbuf buf;
  734     uint blk;
  735 
  736     blk = offset / sizeof(buf);
  737 
  738     read_blk(quotaFileP, blk, &buf);
  739     dh = (struct v2_disk_dqdbheader *)&buf.bytes[0];
  740     dh->dqdh_entries = __cpu_to_le16(__le16_to_cpu(dh->dqdh_entries) - 1);
  741     if (!__le16_to_cpu(dh->dqdh_entries)) { /* Block got free? */
  742         remove_free_dqentry(quotaFileP, &buf, blk);
  743         put_free_dqblk(quotaFileP, &buf, blk);
  744     } else {
  745         memset(&buf.bytes[0] + (offset & ((1 << V2_DQBLKSIZE_BITS) - 1)),
  746                0, sizeof(struct v2_disk_dqblk));
  747 
  748         if (__le16_to_cpu(dh->dqdh_entries) == V2_DQSTRINBLK - 1)
  749             /* First free entry */
  750             insert_free_dqentry(quotaFileP, &buf, blk);
  751                 /* This will also write data block */
  752         else
  753             write_blk(quotaFileP, blk, &buf);
  754     }
  755 }
  756 
  757 
  758 
  759 /* Find entry in block */
  760 static loff_t
  761 find_block_dqentry(struct quota_handle * const quotaFileP,
  762                    struct dquot *        const dquot,
  763                    uint                  const blk) {
  764 
  765     dqbuf buf;
  766     unsigned int i;
  767     struct v2_disk_dqblk * const ddquot = V2_GETENTRIES(&buf);
  768 
  769     read_blk(quotaFileP, blk, &buf);
  770     if (dquot->dq_id)
  771         for (i = 0;
  772              i < V2_DQSTRINBLK && __le32_to_cpu(ddquot[i].dqb_id) !=
  773                  dquot->dq_id;
  774              ++i);
  775     else {          /* ID 0 as a bit more complicated searching... */
  776         for (i = 0; i < V2_DQSTRINBLK; ++i)
  777             if (!__le32_to_cpu(ddquot[i].dqb_id) && !dquotIsEmpty(ddquot + i))
  778                 break;
  779     }
  780     if (i == V2_DQSTRINBLK)
  781         fatal("Quota for id %u referenced but not present.", dquot->dq_id);
  782 
  783     return
  784         blk * sizeof(buf) +
  785         sizeof(struct v2_disk_dqdbheader) +
  786         i * sizeof(struct v2_disk_dqblk);
  787 }
  788 
  789 
  790 
  791 /* Find entry for given id in the tree */
  792 static loff_t
  793 find_tree_dqentry(struct quota_handle * const quotaFileP,
  794                   struct dquot *        const dquot,
  795                   uint                  const blk,
  796                   int                   const depth) {
  797 
  798     dqbuf buf;
  799 
  800     loff_t ret;
  801     uint blk2;
  802 
  803     read_blk(quotaFileP, blk, &buf);
  804 
  805     blk2 = __le32_to_cpu(buf.words[V2_GETIDINDEX(dquot->dq_id, depth)]);
  806     if (!blk2)       /* No reference? */
  807         ret = 0;
  808     else if (depth < V2_DQTREEDEPTH - 1)
  809         ret = find_tree_dqentry(quotaFileP, dquot, blk2, depth + 1);
  810     else
  811         ret = find_block_dqentry(quotaFileP, dquot, blk2);
  812 
  813     return ret;
  814 }
  815 
  816 
  817 
  818 /* Find entry for given id in the tree - wrapper function */
  819 static inline loff_t
  820 find_dqentry(struct quota_handle * const quotaFileP,
  821              struct dquot *        const dquot) {
  822 
  823     return find_tree_dqentry(quotaFileP, dquot, V2_DQTREEOFF, 0);
  824 }
  825 
  826 
  827 
  828 /* Remove reference to dquot from tree */
  829 static void
  830 remove_tree(struct quota_handle * const quotaFileP,
  831             struct dquot *        const dquot,
  832             uint *                const blkP,
  833             int                   const depth) {
  834 
  835     dqbuf buf;
  836     uint newblk;
  837 
  838     read_blk(quotaFileP, *blkP, &buf);
  839     if (depth == V2_DQTREEDEPTH - 1) {
  840         loff_t const offset = find_dqentry(quotaFileP, dquot);
  841         free_dqentry(quotaFileP, offset);
  842         newblk = 0;
  843     } else {
  844         newblk = __le32_to_cpu(buf.words[V2_GETIDINDEX(dquot->dq_id, depth)]);
  845         remove_tree(quotaFileP, dquot, &newblk, depth + 1);
  846     }
  847     if (!newblk) {
  848         unsigned int i;
  849 
  850         buf.words[V2_GETIDINDEX(dquot->dq_id, depth)] = __cpu_to_le32(0);
  851 
  852         /* Find a nonzero byte, if any */
  853         for (i = 0; i < sizeof(buf.bytes) && !buf.bytes[i]; ++i);
  854 
  855         if (i == sizeof(buf) && *blkP != V2_DQTREEOFF) {
  856             /* It's an all-zero block, and not the root block.  Discard it */
  857             put_free_dqblk(quotaFileP, &buf, *blkP);
  858             *blkP = 0;
  859         } else
  860             write_blk(quotaFileP, *blkP, &buf);
  861     }
  862 }
  863 
  864 
  865 
  866 /*
  867  *  Read dquot from quota file.
  868  *  User can use errno to detect errstr when NULL is returned
  869  */
  870 static struct dquot *
  871 v2_read_dquot(struct quota_handle * const quotaFileP,
  872               qid_t                 const id) {
  873 
  874     loff_t offset;
  875     ssize_t ret;
  876     struct v2_disk_dqblk ddquot;
  877     struct dquot *dquot = get_empty_dquot();
  878 
  879     dquot->dq_id = id;
  880     memset(&dquot->dq_dqb, 0, sizeof(struct util_dqblk));
  881 
  882     offset = find_dqentry(quotaFileP, dquot);
  883     if (offset > 0) {
  884         lseek(quotaFileP->qh_fd, offset, SEEK_SET);
  885         ret = read(quotaFileP->qh_fd, &ddquot, sizeof(ddquot));
  886         if (ret != sizeof(struct v2_disk_dqblk)) {
  887             if (ret > 0)
  888                 errno = EIO;
  889             fatal("Can't read quota structure for id %u.  errno=%d (%s)",
  890                   dquot->dq_id, errno, strerror(errno));
  891         }
  892         v2_disk2memdqblk(&dquot->dq_dqb, &ddquot);
  893         /* Unescape all-zero structure (it can be on disk after a crash) */
  894         if (!dquot->dq_id &&
  895             !dquot->dq_dqb.dqb_bhardlimit &&
  896             !dquot->dq_dqb.dqb_bsoftlimit &&
  897             !dquot->dq_dqb.dqb_curspace &&
  898             !dquot->dq_dqb.dqb_ihardlimit &&
  899             !dquot->dq_dqb.dqb_isoftlimit &&
  900             !dquot->dq_dqb.dqb_curinodes &&
  901             !dquot->dq_dqb.dqb_btime &&
  902             dquot->dq_dqb.dqb_itime == 1)
  903             dquot->dq_dqb.dqb_itime = 0;
  904     }
  905     return dquot;
  906 }
  907 
  908 
  909 
  910 /*
  911  *  Scan all dquots in file and call callback on each
  912  */
  913 #define set_bit(bmp, ind) ((bmp)[(ind) >> 3] |= (1 << ((ind) & 7)))
  914 #define get_bit(bmp, ind) ((bmp)[(ind) >> 3] & (1 << ((ind) & 7)))
  915 
  916 static void
  917 report_block(struct quota_handle * const quotaFileP,
  918              struct dquot *        const dquot,
  919              uint                  const blk,
  920              char *                const bitmap,
  921              dquotReportFn               process_dquot,
  922              unsigned int *        const entryCountP) {
  923 
  924     dqbuf buf;
  925     struct v2_disk_dqdbheader * dh;
  926     struct v2_disk_dqblk * ddata;
  927     unsigned int entries;
  928     unsigned int i;
  929 
  930     set_bit(bitmap, blk);
  931     read_blk(quotaFileP, blk, &buf);
  932     dh = (struct v2_disk_dqdbheader *)&buf.bytes[0];
  933     ddata = V2_GETENTRIES(&buf);
  934     entries = __le16_to_cpu(dh->dqdh_entries);
  935     for (i = 0; i < V2_DQSTRINBLK; ++i) {
  936         if (!dquotIsEmpty(&ddata[i])) {
  937             v2_disk2memdqblk(&dquot->dq_dqb, ddata + i);
  938             dquot->dq_id = __le32_to_cpu(ddata[i].dqb_id);
  939             if (process_dquot(dquot, NULL) < 0)
  940                 break;
  941         }
  942     }
  943     *entryCountP = entries;
  944 }
  945 
  946 
  947 
  948 static void
  949 check_reference(struct quota_handle * const quotaFileP,
  950                 uint                  const blk) {
  951 
  952     if (blk >= quotaFileP->qh_info.u.v2_mdqi.dqi_blocks)
  953         fatal("Illegal reference in '%s' quota file on '%s'.  "
  954               "Quota file is probably corrupted.  "
  955               "Please run quotacheck(8) and try again.",
  956               type2name(quotaFileP->qh_type), quotaFileP->qh_quotadev);
  957 }
  958 
  959 
  960 
  961 static unsigned int
  962 report_tree(struct quota_handle * const quotaFileP,
  963             struct dquot *        const dquot,
  964             uint                  const blk,
  965             int                   const depth,
  966             char *                const bitmap,
  967             dquotReportFn               process_dquot) {
  968 
  969     dqbuf buf;
  970 
  971     unsigned int entries;
  972 
  973     read_blk(quotaFileP, blk, &buf);
  974 
  975     if (depth == V2_DQTREEDEPTH - 1) {
  976         unsigned int i;
  977         for (i = 0; i < ARRAY_SIZE(buf.words); ++i) {
  978             uint const blk2 = __le32_to_cpu(buf.words[i]);
  979             check_reference(quotaFileP, blk2);
  980             if (blk2 && !get_bit(bitmap, blk2)) {
  981                 uint thisBlockEntryCount;
  982                 report_block(quotaFileP, dquot, blk2,
  983                              bitmap, process_dquot, &thisBlockEntryCount);
  984                 entries += thisBlockEntryCount;
  985             }
  986         }
  987     } else {
  988         unsigned int i;
  989         for (i = 0; i < ARRAY_SIZE(buf.words); ++i) {
  990             uint const blk2 = __le32_to_cpu(buf.words[i]);
  991             if (blk2) {
  992                 check_reference(quotaFileP, blk2);
  993                 entries +=
  994                     report_tree(quotaFileP, dquot, blk2, depth + 1,
  995                                 bitmap, process_dquot);
  996             }
  997         }
  998     }
  999     return entries;
 1000 }
 1001 
 1002 
 1003 
 1004 static uint
 1005 find_set_bits(char *       const bmp,
 1006               unsigned int const blocks) {
 1007     uint i;
 1008     uint used;
 1009 
 1010     for (i = 0, used = 0; i < blocks; ++i)
 1011         if (get_bit(bmp, i))
 1012             used++;
 1013 
 1014     return used;
 1015 }
 1016 
 1017 
 1018 
 1019 static int
 1020 v2_scan_dquots(struct quota_handle * const quotaFileP,
 1021                dquotReportFn               process_dquot) {
 1022 
 1023     struct v2_mem_dqinfo * const infoP = &quotaFileP->qh_info.u.v2_mdqi;
 1024 
 1025     int retval;
 1026     ssize_t rc;
 1027     unsigned int bmpByteCount;
 1028     char *bitmap;
 1029     struct v2_disk_dqinfo ddqinfo;
 1030     struct dquot * dquotP;
 1031 
 1032     dquotP = get_empty_dquot();
 1033 
 1034     lseek(quotaFileP->qh_fd, V2_DQINFOOFF, SEEK_SET);
 1035     rc = read(quotaFileP->qh_fd, &ddqinfo, sizeof(ddqinfo));
 1036     if (rc != sizeof(ddqinfo))
 1037         retval = -1;
 1038     else {
 1039         infoP->dqi_blocks = __le32_to_cpu(ddqinfo.dqi_blocks);
 1040         bmpByteCount = (infoP->dqi_blocks + 7) / 8;
 1041         MALLOCARRAY_NOFAIL(bitmap, bmpByteCount);
 1042         memset(bitmap, 0, bmpByteCount);
 1043         infoP->dqi_used_entries =
 1044             report_tree(quotaFileP, dquotP, V2_DQTREEOFF, 0,
 1045                         bitmap, process_dquot);
 1046         infoP->dqi_data_blocks = find_set_bits(bitmap, infoP->dqi_blocks);
 1047         free(bitmap);
 1048         retval = 0;
 1049     }
 1050     free(dquotP);
 1051     return retval;
 1052 }
 1053 
 1054 
 1055 
 1056 /* Report information about quotafile */
 1057 static int
 1058 v2_report(struct quota_handle * const quotaFileP,
 1059           bool                  const verbose) {
 1060     if (verbose) {
 1061         struct v2_mem_dqinfo *info = &quotaFileP->qh_info.u.v2_mdqi;
 1062 
 1063         printf("Statistics:\n");
 1064         printf("Total blocks: %u\n", info->dqi_blocks);
 1065         printf("Data blocks: %u\n", info->dqi_data_blocks);
 1066         printf("Entries: %u\n", info->dqi_used_entries);
 1067         printf("Used average: %f\n",
 1068                ((float)info->dqi_used_entries) / info->dqi_data_blocks);
 1069     }
 1070     return 0;
 1071 }
 1072 
 1073 
 1074 
 1075 struct quotafile_ops quotafile_ops_2 = {
 1076     check_file:  v2_check_file,
 1077     create_file: v2_create_file,
 1078     init_read:   v2_init_read,
 1079     write_info:  v2_write_info,
 1080     read_dquot:  v2_read_dquot,
 1081     write_dquot: v2_write_dquot,
 1082     scan_dquots: v2_scan_dquots,
 1083     report:      v2_report,
 1084     end_io:      NULL,
 1085 };