1 /**
   2  * bitmap.c - Bitmap handling code. Part of the Linux-NTFS project.
   3  *
   4  * Copyright (c) 2002-2006 Anton Altaparmakov
   5  * Copyright (c) 2004-2005 Richard Russon
   6  *
   7  * This program/include file is free software; you can redistribute it and/or
   8  * modify it under the terms of the GNU General Public License as published
   9  * by the Free Software Foundation; either version 2 of the License, or
  10  * (at your option) any later version.
  11  *
  12  * This program/include file is distributed in the hope that it will be
  13  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
  14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15  * GNU General Public License for more details.
  16  *
  17  * You should have received a copy of the GNU General Public License
  18  * along with this program (in the main directory of the Linux-NTFS
  19  * distribution in the file COPYING); if not, write to the Free Software
  20  * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  21  */
  22 
  23 #ifdef HAVE_CONFIG_H
  24 #include "config.h"
  25 #endif
  26 
  27 #ifdef HAVE_STDLIB_H
  28 #include <stdlib.h>
  29 #endif
  30 #ifdef HAVE_STDIO_H
  31 #include <stdio.h>
  32 #endif
  33 #ifdef HAVE_STRING_H
  34 #include <string.h>
  35 #endif
  36 #ifdef HAVE_ERRNO_H
  37 #include <errno.h>
  38 #endif
  39 
  40 #include "compat.h"
  41 #include "types.h"
  42 #include "attrib.h"
  43 #include "bitmap.h"
  44 #include "debug.h"
  45 #include "logging.h"
  46 
  47 /**
  48  * ntfs_bitmap_set_bits_in_run - set a run of bits in a bitmap to a value
  49  * @na:         attribute containing the bitmap
  50  * @start_bit:  first bit to set
  51  * @count:      number of bits to set
  52  * @value:      value to set the bits to (i.e. 0 or 1)
  53  *
  54  * Set @count bits starting at bit @start_bit in the bitmap described by the
  55  * attribute @na to @value, where @value is either 0 or 1.
  56  *
  57  * On success return 0 and on error return -1 with errno set to the error code.
  58  */
  59 static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit,
  60                 s64 count, int value)
  61 {
  62         ntfs_volume *vol = na->ni->vol;
  63         s64 bufsize, br, left = count;
  64         u8 *buf, *lastbyte_buf;
  65         int bit, firstbyte, lastbyte, lastbyte_pos, tmp, err;
  66 
  67         if (!na || start_bit < 0 || count < 0) {
  68                 errno = EINVAL;
  69                 return -1;
  70         }
  71 
  72         bit = start_bit & 7;
  73         if (bit)
  74                 firstbyte = 1;
  75         else
  76                 firstbyte = 0;
  77 
  78         /* Calculate the required buffer size in bytes, capping it at 8kiB. */
  79         bufsize = ((count - (bit ? 8 - bit : 0) + 7) >> 3) + firstbyte;
  80         if (bufsize > 8192)
  81                 bufsize = 8192;
  82 
  83         buf = (u8*)ntfs_malloc(bufsize);
  84         if (!buf)
  85                 return -1;
  86 
  87         /* Depending on @value, zero or set all bits in the allocated buffer. */
  88         memset(buf, value ? 0xff : 0, bufsize);
  89 
  90         /* If there is a first partial byte... */
  91         if (bit) {
  92                 /* read it in... */
  93                 br = ntfs_attr_pread(na, start_bit >> 3, 1, buf);
  94                 if (br != 1) {
  95                         free(buf);
  96                         errno = EIO;
  97                         return -1;
  98                 }
  99                 /* and set or clear the appropriate bits in it. */
 100                 while ((bit & 7) && left--) {
 101                         if (value)
 102                                 *buf |= 1 << bit++;
 103                         else
 104                                 *buf &= ~(1 << bit++);
 105                 }
 106                 /* Update @start_bit to the new position. */
 107                 start_bit = (start_bit + 7) & ~7;
 108         }
 109 
 110         /* Loop until @left reaches zero. */
 111         lastbyte = 0;
 112         lastbyte_buf = NULL;
 113         bit = left & 7;
 114         do {
 115                 /* If there is a last partial byte... */
 116                 if (left > 0 && bit) {
 117                         lastbyte_pos = ((left + 7) >> 3) + firstbyte;
 118                         if (!lastbyte_pos) {
 119                                 // FIXME: Eeek! BUG!
 120                                 ntfs_log_trace("lastbyte is zero. Leaving "
 121                                                 "inconsistent metadata.\n");
 122                                 err = EIO;
 123                                 goto free_err_out;
 124                         }
 125                         /* and it is in the currently loaded bitmap window... */
 126                         if (lastbyte_pos <= bufsize) {
 127                                 lastbyte_buf = buf + lastbyte_pos - 1;
 128 
 129                                 /* read the byte in... */
 130                                 br = ntfs_attr_pread(na, (start_bit + left) >>
 131                                                 3, 1, lastbyte_buf);
 132                                 if (br != 1) {
 133                                         // FIXME: Eeek! We need rollback! (AIA)
 134                                         ntfs_log_trace("Read of last byte "
 135                                                         "failed. Leaving "
 136                                                         "inconsistent "
 137                                                         "metadata.\n");
 138                                         err = EIO;
 139                                         goto free_err_out;
 140                                 }
 141                                 /* and set/clear the appropriate bits in it. */
 142                                 while (bit && left--) {
 143                                         if (value)
 144                                                 *lastbyte_buf |= 1 << --bit;
 145                                         else
 146                                                 *lastbyte_buf &= ~(1 << --bit);
 147                                 }
 148                                 /* We don't want to come back here... */
 149                                 bit = 0;
 150                                 /* We have a last byte that we have handled. */
 151                                 lastbyte = 1;
 152                         }
 153                 }
 154 
 155                 /* Write the prepared buffer to disk. */
 156                 tmp = (start_bit >> 3) - firstbyte;
 157                 br = ntfs_attr_pwrite(na, tmp, bufsize, buf);
 158                 if (br != bufsize) {
 159                         // FIXME: Eeek! We need rollback! (AIA)
 160                         ntfs_log_trace("Failed to write buffer to bitmap. "
 161                                         "Leaving inconsistent metadata.\n");
 162                         err = EIO;
 163                         goto free_err_out;
 164                 }
 165 
 166                 /* Update counters. */
 167                 tmp = (bufsize - firstbyte - lastbyte) << 3;
 168                 if (firstbyte) {
 169                         firstbyte = 0;
 170                         /*
 171                          * Re-set the partial first byte so a subsequent write
 172                          * of the buffer does not have stale, incorrect bits.
 173                          */
 174                         *buf = value ? 0xff : 0;
 175                 }
 176                 start_bit += tmp;
 177                 left -= tmp;
 178                 if (bufsize > (tmp = (left + 7) >> 3))
 179                         bufsize = tmp;
 180 
 181                 if (lastbyte && left != 0) {
 182                         // FIXME: Eeek! BUG!
 183                         ntfs_log_trace("Last buffer but count is not zero (= "
 184                                         "%lli). Leaving inconsistent metadata."
 185                                         "\n", (long long)left);
 186                         err = EIO;
 187                         goto free_err_out;
 188                 }
 189         } while (left > 0);
 190 
 191         /* Update free clusters and MFT records. */
 192         if (na == vol->mftbmp_na) {
 193                 if (value)
 194                         vol->nr_free_mft_records -= count;
 195                 else
 196                         vol->nr_free_mft_records += count;
 197         }
 198         if (na == vol->lcnbmp_na) {
 199                 if (value)
 200                         vol->nr_free_clusters -= count;
 201                 else
 202                         vol->nr_free_clusters += count;
 203         }
 204 
 205         /* Done! */
 206         free(buf);
 207         return 0;
 208 
 209 free_err_out:
 210         free(buf);
 211         errno = err;
 212         return -1;
 213 }
 214 
 215 /**
 216  * ntfs_bitmap_set_run - set a run of bits in a bitmap
 217  * @na:         attribute containing the bitmap
 218  * @start_bit:  first bit to set
 219  * @count:      number of bits to set
 220  *
 221  * Set @count bits starting at bit @start_bit in the bitmap described by the
 222  * attribute @na.
 223  *
 224  * On success return 0 and on error return -1 with errno set to the error code.
 225  */
 226 int ntfs_bitmap_set_run(ntfs_attr *na, s64 start_bit, s64 count)
 227 {
 228         return ntfs_bitmap_set_bits_in_run(na, start_bit, count, 1);
 229 }
 230 
 231 /**
 232  * ntfs_bitmap_clear_run - clear a run of bits in a bitmap
 233  * @na:         attribute containing the bitmap
 234  * @start_bit:  first bit to clear
 235  * @count:      number of bits to clear
 236  *
 237  * Clear @count bits starting at bit @start_bit in the bitmap described by the
 238  * attribute @na.
 239  *
 240  * On success return 0 and on error return -1 with errno set to the error code.
 241  */
 242 int ntfs_bitmap_clear_run(ntfs_attr *na, s64 start_bit, s64 count)
 243 {
 244         ntfs_log_trace("Dealloc from bit 0x%llx, count 0x%llx.\n",
 245                        (long long)start_bit, (long long)count);
 246 
 247         return ntfs_bitmap_set_bits_in_run(na, start_bit, count, 0);
 248 }
 249