162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/fs/hfsplus/bitmap.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2001 662306a36Sopenharmony_ci * Brad Boyer (flar@allandria.com) 762306a36Sopenharmony_ci * (C) 2003 Ardis Technologies <roman@ardistech.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Handling of allocation file 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/pagemap.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "hfsplus_fs.h" 1562306a36Sopenharmony_ci#include "hfsplus_raw.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define PAGE_CACHE_BITS (PAGE_SIZE * 8) 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ciint hfsplus_block_allocate(struct super_block *sb, u32 size, 2062306a36Sopenharmony_ci u32 offset, u32 *max) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); 2362306a36Sopenharmony_ci struct page *page; 2462306a36Sopenharmony_ci struct address_space *mapping; 2562306a36Sopenharmony_ci __be32 *pptr, *curr, *end; 2662306a36Sopenharmony_ci u32 mask, start, len, n; 2762306a36Sopenharmony_ci __be32 val; 2862306a36Sopenharmony_ci int i; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci len = *max; 3162306a36Sopenharmony_ci if (!len) 3262306a36Sopenharmony_ci return size; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci hfs_dbg(BITMAP, "block_allocate: %u,%u,%u\n", size, offset, len); 3562306a36Sopenharmony_ci mutex_lock(&sbi->alloc_mutex); 3662306a36Sopenharmony_ci mapping = sbi->alloc_file->i_mapping; 3762306a36Sopenharmony_ci page = read_mapping_page(mapping, offset / PAGE_CACHE_BITS, NULL); 3862306a36Sopenharmony_ci if (IS_ERR(page)) { 3962306a36Sopenharmony_ci start = size; 4062306a36Sopenharmony_ci goto out; 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci pptr = kmap_local_page(page); 4362306a36Sopenharmony_ci curr = pptr + (offset & (PAGE_CACHE_BITS - 1)) / 32; 4462306a36Sopenharmony_ci i = offset % 32; 4562306a36Sopenharmony_ci offset &= ~(PAGE_CACHE_BITS - 1); 4662306a36Sopenharmony_ci if ((size ^ offset) / PAGE_CACHE_BITS) 4762306a36Sopenharmony_ci end = pptr + PAGE_CACHE_BITS / 32; 4862306a36Sopenharmony_ci else 4962306a36Sopenharmony_ci end = pptr + ((size + 31) & (PAGE_CACHE_BITS - 1)) / 32; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci /* scan the first partial u32 for zero bits */ 5262306a36Sopenharmony_ci val = *curr; 5362306a36Sopenharmony_ci if (~val) { 5462306a36Sopenharmony_ci n = be32_to_cpu(val); 5562306a36Sopenharmony_ci mask = (1U << 31) >> i; 5662306a36Sopenharmony_ci for (; i < 32; mask >>= 1, i++) { 5762306a36Sopenharmony_ci if (!(n & mask)) 5862306a36Sopenharmony_ci goto found; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci curr++; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci /* scan complete u32s for the first zero bit */ 6462306a36Sopenharmony_ci while (1) { 6562306a36Sopenharmony_ci while (curr < end) { 6662306a36Sopenharmony_ci val = *curr; 6762306a36Sopenharmony_ci if (~val) { 6862306a36Sopenharmony_ci n = be32_to_cpu(val); 6962306a36Sopenharmony_ci mask = 1 << 31; 7062306a36Sopenharmony_ci for (i = 0; i < 32; mask >>= 1, i++) { 7162306a36Sopenharmony_ci if (!(n & mask)) 7262306a36Sopenharmony_ci goto found; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci curr++; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci kunmap_local(pptr); 7862306a36Sopenharmony_ci offset += PAGE_CACHE_BITS; 7962306a36Sopenharmony_ci if (offset >= size) 8062306a36Sopenharmony_ci break; 8162306a36Sopenharmony_ci page = read_mapping_page(mapping, offset / PAGE_CACHE_BITS, 8262306a36Sopenharmony_ci NULL); 8362306a36Sopenharmony_ci if (IS_ERR(page)) { 8462306a36Sopenharmony_ci start = size; 8562306a36Sopenharmony_ci goto out; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci curr = pptr = kmap_local_page(page); 8862306a36Sopenharmony_ci if ((size ^ offset) / PAGE_CACHE_BITS) 8962306a36Sopenharmony_ci end = pptr + PAGE_CACHE_BITS / 32; 9062306a36Sopenharmony_ci else 9162306a36Sopenharmony_ci end = pptr + ((size + 31) & (PAGE_CACHE_BITS - 1)) / 32; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci hfs_dbg(BITMAP, "bitmap full\n"); 9462306a36Sopenharmony_ci start = size; 9562306a36Sopenharmony_ci goto out; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cifound: 9862306a36Sopenharmony_ci start = offset + (curr - pptr) * 32 + i; 9962306a36Sopenharmony_ci if (start >= size) { 10062306a36Sopenharmony_ci hfs_dbg(BITMAP, "bitmap full\n"); 10162306a36Sopenharmony_ci goto out; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci /* do any partial u32 at the start */ 10462306a36Sopenharmony_ci len = min(size - start, len); 10562306a36Sopenharmony_ci while (1) { 10662306a36Sopenharmony_ci n |= mask; 10762306a36Sopenharmony_ci if (++i >= 32) 10862306a36Sopenharmony_ci break; 10962306a36Sopenharmony_ci mask >>= 1; 11062306a36Sopenharmony_ci if (!--len || n & mask) 11162306a36Sopenharmony_ci goto done; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci if (!--len) 11462306a36Sopenharmony_ci goto done; 11562306a36Sopenharmony_ci *curr++ = cpu_to_be32(n); 11662306a36Sopenharmony_ci /* do full u32s */ 11762306a36Sopenharmony_ci while (1) { 11862306a36Sopenharmony_ci while (curr < end) { 11962306a36Sopenharmony_ci n = be32_to_cpu(*curr); 12062306a36Sopenharmony_ci if (len < 32) 12162306a36Sopenharmony_ci goto last; 12262306a36Sopenharmony_ci if (n) { 12362306a36Sopenharmony_ci len = 32; 12462306a36Sopenharmony_ci goto last; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci *curr++ = cpu_to_be32(0xffffffff); 12762306a36Sopenharmony_ci len -= 32; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci set_page_dirty(page); 13062306a36Sopenharmony_ci kunmap_local(pptr); 13162306a36Sopenharmony_ci offset += PAGE_CACHE_BITS; 13262306a36Sopenharmony_ci page = read_mapping_page(mapping, offset / PAGE_CACHE_BITS, 13362306a36Sopenharmony_ci NULL); 13462306a36Sopenharmony_ci if (IS_ERR(page)) { 13562306a36Sopenharmony_ci start = size; 13662306a36Sopenharmony_ci goto out; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci pptr = kmap_local_page(page); 13962306a36Sopenharmony_ci curr = pptr; 14062306a36Sopenharmony_ci end = pptr + PAGE_CACHE_BITS / 32; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_cilast: 14362306a36Sopenharmony_ci /* do any partial u32 at end */ 14462306a36Sopenharmony_ci mask = 1U << 31; 14562306a36Sopenharmony_ci for (i = 0; i < len; i++) { 14662306a36Sopenharmony_ci if (n & mask) 14762306a36Sopenharmony_ci break; 14862306a36Sopenharmony_ci n |= mask; 14962306a36Sopenharmony_ci mask >>= 1; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_cidone: 15262306a36Sopenharmony_ci *curr = cpu_to_be32(n); 15362306a36Sopenharmony_ci set_page_dirty(page); 15462306a36Sopenharmony_ci kunmap_local(pptr); 15562306a36Sopenharmony_ci *max = offset + (curr - pptr) * 32 + i - start; 15662306a36Sopenharmony_ci sbi->free_blocks -= *max; 15762306a36Sopenharmony_ci hfsplus_mark_mdb_dirty(sb); 15862306a36Sopenharmony_ci hfs_dbg(BITMAP, "-> %u,%u\n", start, *max); 15962306a36Sopenharmony_ciout: 16062306a36Sopenharmony_ci mutex_unlock(&sbi->alloc_mutex); 16162306a36Sopenharmony_ci return start; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ciint hfsplus_block_free(struct super_block *sb, u32 offset, u32 count) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); 16762306a36Sopenharmony_ci struct page *page; 16862306a36Sopenharmony_ci struct address_space *mapping; 16962306a36Sopenharmony_ci __be32 *pptr, *curr, *end; 17062306a36Sopenharmony_ci u32 mask, len, pnr; 17162306a36Sopenharmony_ci int i; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* is there any actual work to be done? */ 17462306a36Sopenharmony_ci if (!count) 17562306a36Sopenharmony_ci return 0; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci hfs_dbg(BITMAP, "block_free: %u,%u\n", offset, count); 17862306a36Sopenharmony_ci /* are all of the bits in range? */ 17962306a36Sopenharmony_ci if ((offset + count) > sbi->total_blocks) 18062306a36Sopenharmony_ci return -ENOENT; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci mutex_lock(&sbi->alloc_mutex); 18362306a36Sopenharmony_ci mapping = sbi->alloc_file->i_mapping; 18462306a36Sopenharmony_ci pnr = offset / PAGE_CACHE_BITS; 18562306a36Sopenharmony_ci page = read_mapping_page(mapping, pnr, NULL); 18662306a36Sopenharmony_ci if (IS_ERR(page)) 18762306a36Sopenharmony_ci goto kaboom; 18862306a36Sopenharmony_ci pptr = kmap_local_page(page); 18962306a36Sopenharmony_ci curr = pptr + (offset & (PAGE_CACHE_BITS - 1)) / 32; 19062306a36Sopenharmony_ci end = pptr + PAGE_CACHE_BITS / 32; 19162306a36Sopenharmony_ci len = count; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* do any partial u32 at the start */ 19462306a36Sopenharmony_ci i = offset % 32; 19562306a36Sopenharmony_ci if (i) { 19662306a36Sopenharmony_ci int j = 32 - i; 19762306a36Sopenharmony_ci mask = 0xffffffffU << j; 19862306a36Sopenharmony_ci if (j > count) { 19962306a36Sopenharmony_ci mask |= 0xffffffffU >> (i + count); 20062306a36Sopenharmony_ci *curr++ &= cpu_to_be32(mask); 20162306a36Sopenharmony_ci goto out; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci *curr++ &= cpu_to_be32(mask); 20462306a36Sopenharmony_ci count -= j; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* do full u32s */ 20862306a36Sopenharmony_ci while (1) { 20962306a36Sopenharmony_ci while (curr < end) { 21062306a36Sopenharmony_ci if (count < 32) 21162306a36Sopenharmony_ci goto done; 21262306a36Sopenharmony_ci *curr++ = 0; 21362306a36Sopenharmony_ci count -= 32; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci if (!count) 21662306a36Sopenharmony_ci break; 21762306a36Sopenharmony_ci set_page_dirty(page); 21862306a36Sopenharmony_ci kunmap_local(pptr); 21962306a36Sopenharmony_ci page = read_mapping_page(mapping, ++pnr, NULL); 22062306a36Sopenharmony_ci if (IS_ERR(page)) 22162306a36Sopenharmony_ci goto kaboom; 22262306a36Sopenharmony_ci pptr = kmap_local_page(page); 22362306a36Sopenharmony_ci curr = pptr; 22462306a36Sopenharmony_ci end = pptr + PAGE_CACHE_BITS / 32; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_cidone: 22762306a36Sopenharmony_ci /* do any partial u32 at end */ 22862306a36Sopenharmony_ci if (count) { 22962306a36Sopenharmony_ci mask = 0xffffffffU >> count; 23062306a36Sopenharmony_ci *curr &= cpu_to_be32(mask); 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ciout: 23362306a36Sopenharmony_ci set_page_dirty(page); 23462306a36Sopenharmony_ci kunmap_local(pptr); 23562306a36Sopenharmony_ci sbi->free_blocks += len; 23662306a36Sopenharmony_ci hfsplus_mark_mdb_dirty(sb); 23762306a36Sopenharmony_ci mutex_unlock(&sbi->alloc_mutex); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return 0; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cikaboom: 24262306a36Sopenharmony_ci pr_crit("unable to mark blocks free: error %ld\n", PTR_ERR(page)); 24362306a36Sopenharmony_ci mutex_unlock(&sbi->alloc_mutex); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci return -EIO; 24662306a36Sopenharmony_ci} 247