Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * bitmap.c - NTFS kernel bitmap handling.  Part of the Linux-NTFS project.
0004  *
0005  * Copyright (c) 2004-2005 Anton Altaparmakov
0006  */
0007 
0008 #ifdef NTFS_RW
0009 
0010 #include <linux/pagemap.h>
0011 
0012 #include "bitmap.h"
0013 #include "debug.h"
0014 #include "aops.h"
0015 #include "ntfs.h"
0016 
0017 /**
0018  * __ntfs_bitmap_set_bits_in_run - set a run of bits in a bitmap to a value
0019  * @vi:         vfs inode describing the bitmap
0020  * @start_bit:      first bit to set
0021  * @count:      number of bits to set
0022  * @value:      value to set the bits to (i.e. 0 or 1)
0023  * @is_rollback:    if 'true' this is a rollback operation
0024  *
0025  * Set @count bits starting at bit @start_bit in the bitmap described by the
0026  * vfs inode @vi to @value, where @value is either 0 or 1.
0027  *
0028  * @is_rollback should always be 'false', it is for internal use to rollback
0029  * errors.  You probably want to use ntfs_bitmap_set_bits_in_run() instead.
0030  *
0031  * Return 0 on success and -errno on error.
0032  */
0033 int __ntfs_bitmap_set_bits_in_run(struct inode *vi, const s64 start_bit,
0034         const s64 count, const u8 value, const bool is_rollback)
0035 {
0036     s64 cnt = count;
0037     pgoff_t index, end_index;
0038     struct address_space *mapping;
0039     struct page *page;
0040     u8 *kaddr;
0041     int pos, len;
0042     u8 bit;
0043 
0044     BUG_ON(!vi);
0045     ntfs_debug("Entering for i_ino 0x%lx, start_bit 0x%llx, count 0x%llx, "
0046             "value %u.%s", vi->i_ino, (unsigned long long)start_bit,
0047             (unsigned long long)cnt, (unsigned int)value,
0048             is_rollback ? " (rollback)" : "");
0049     BUG_ON(start_bit < 0);
0050     BUG_ON(cnt < 0);
0051     BUG_ON(value > 1);
0052     /*
0053      * Calculate the indices for the pages containing the first and last
0054      * bits, i.e. @start_bit and @start_bit + @cnt - 1, respectively.
0055      */
0056     index = start_bit >> (3 + PAGE_SHIFT);
0057     end_index = (start_bit + cnt - 1) >> (3 + PAGE_SHIFT);
0058 
0059     /* Get the page containing the first bit (@start_bit). */
0060     mapping = vi->i_mapping;
0061     page = ntfs_map_page(mapping, index);
0062     if (IS_ERR(page)) {
0063         if (!is_rollback)
0064             ntfs_error(vi->i_sb, "Failed to map first page (error "
0065                     "%li), aborting.", PTR_ERR(page));
0066         return PTR_ERR(page);
0067     }
0068     kaddr = page_address(page);
0069 
0070     /* Set @pos to the position of the byte containing @start_bit. */
0071     pos = (start_bit >> 3) & ~PAGE_MASK;
0072 
0073     /* Calculate the position of @start_bit in the first byte. */
0074     bit = start_bit & 7;
0075 
0076     /* If the first byte is partial, modify the appropriate bits in it. */
0077     if (bit) {
0078         u8 *byte = kaddr + pos;
0079         while ((bit & 7) && cnt) {
0080             cnt--;
0081             if (value)
0082                 *byte |= 1 << bit++;
0083             else
0084                 *byte &= ~(1 << bit++);
0085         }
0086         /* If we are done, unmap the page and return success. */
0087         if (!cnt)
0088             goto done;
0089 
0090         /* Update @pos to the new position. */
0091         pos++;
0092     }
0093     /*
0094      * Depending on @value, modify all remaining whole bytes in the page up
0095      * to @cnt.
0096      */
0097     len = min_t(s64, cnt >> 3, PAGE_SIZE - pos);
0098     memset(kaddr + pos, value ? 0xff : 0, len);
0099     cnt -= len << 3;
0100 
0101     /* Update @len to point to the first not-done byte in the page. */
0102     if (cnt < 8)
0103         len += pos;
0104 
0105     /* If we are not in the last page, deal with all subsequent pages. */
0106     while (index < end_index) {
0107         BUG_ON(cnt <= 0);
0108 
0109         /* Update @index and get the next page. */
0110         flush_dcache_page(page);
0111         set_page_dirty(page);
0112         ntfs_unmap_page(page);
0113         page = ntfs_map_page(mapping, ++index);
0114         if (IS_ERR(page))
0115             goto rollback;
0116         kaddr = page_address(page);
0117         /*
0118          * Depending on @value, modify all remaining whole bytes in the
0119          * page up to @cnt.
0120          */
0121         len = min_t(s64, cnt >> 3, PAGE_SIZE);
0122         memset(kaddr, value ? 0xff : 0, len);
0123         cnt -= len << 3;
0124     }
0125     /*
0126      * The currently mapped page is the last one.  If the last byte is
0127      * partial, modify the appropriate bits in it.  Note, @len is the
0128      * position of the last byte inside the page.
0129      */
0130     if (cnt) {
0131         u8 *byte;
0132 
0133         BUG_ON(cnt > 7);
0134 
0135         bit = cnt;
0136         byte = kaddr + len;
0137         while (bit--) {
0138             if (value)
0139                 *byte |= 1 << bit;
0140             else
0141                 *byte &= ~(1 << bit);
0142         }
0143     }
0144 done:
0145     /* We are done.  Unmap the page and return success. */
0146     flush_dcache_page(page);
0147     set_page_dirty(page);
0148     ntfs_unmap_page(page);
0149     ntfs_debug("Done.");
0150     return 0;
0151 rollback:
0152     /*
0153      * Current state:
0154      *  - no pages are mapped
0155      *  - @count - @cnt is the number of bits that have been modified
0156      */
0157     if (is_rollback)
0158         return PTR_ERR(page);
0159     if (count != cnt)
0160         pos = __ntfs_bitmap_set_bits_in_run(vi, start_bit, count - cnt,
0161                 value ? 0 : 1, true);
0162     else
0163         pos = 0;
0164     if (!pos) {
0165         /* Rollback was successful. */
0166         ntfs_error(vi->i_sb, "Failed to map subsequent page (error "
0167                 "%li), aborting.", PTR_ERR(page));
0168     } else {
0169         /* Rollback failed. */
0170         ntfs_error(vi->i_sb, "Failed to map subsequent page (error "
0171                 "%li) and rollback failed (error %i).  "
0172                 "Aborting and leaving inconsistent metadata.  "
0173                 "Unmount and run chkdsk.", PTR_ERR(page), pos);
0174         NVolSetErrors(NTFS_SB(vi->i_sb));
0175     }
0176     return PTR_ERR(page);
0177 }
0178 
0179 #endif /* NTFS_RW */