xref: /linux/fs/hfsplus/bitmap.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  linux/fs/hfsplus/bitmap.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Copyright (C) 2001
61da177e4SLinus Torvalds  * Brad Boyer (flar@allandria.com)
71da177e4SLinus Torvalds  * (C) 2003 Ardis Technologies <roman@ardistech.com>
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  * Handling of allocation file
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
121da177e4SLinus Torvalds #include <linux/pagemap.h>
131da177e4SLinus Torvalds 
141da177e4SLinus Torvalds #include "hfsplus_fs.h"
151da177e4SLinus Torvalds #include "hfsplus_raw.h"
161da177e4SLinus Torvalds 
1709cbfeafSKirill A. Shutemov #define PAGE_CACHE_BITS	(PAGE_SIZE * 8)
181da177e4SLinus Torvalds 
hfsplus_block_allocate(struct super_block * sb,u32 size,u32 offset,u32 * max)192753cc28SAnton Salikhmetov int hfsplus_block_allocate(struct super_block *sb, u32 size,
202753cc28SAnton Salikhmetov 		u32 offset, u32 *max)
211da177e4SLinus Torvalds {
22dd73a01aSChristoph Hellwig 	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
231da177e4SLinus Torvalds 	struct page *page;
241da177e4SLinus Torvalds 	struct address_space *mapping;
251da177e4SLinus Torvalds 	__be32 *pptr, *curr, *end;
261da177e4SLinus Torvalds 	u32 mask, start, len, n;
271da177e4SLinus Torvalds 	__be32 val;
281da177e4SLinus Torvalds 	int i;
291da177e4SLinus Torvalds 
301da177e4SLinus Torvalds 	len = *max;
311da177e4SLinus Torvalds 	if (!len)
321da177e4SLinus Torvalds 		return size;
331da177e4SLinus Torvalds 
34c2b3e1f7SJoe Perches 	hfs_dbg(BITMAP, "block_allocate: %u,%u,%u\n", size, offset, len);
35dd73a01aSChristoph Hellwig 	mutex_lock(&sbi->alloc_mutex);
36dd73a01aSChristoph Hellwig 	mapping = sbi->alloc_file->i_mapping;
37090d2b18SPekka Enberg 	page = read_mapping_page(mapping, offset / PAGE_CACHE_BITS, NULL);
38649f1ee6SEric Sesterhenn 	if (IS_ERR(page)) {
39649f1ee6SEric Sesterhenn 		start = size;
40649f1ee6SEric Sesterhenn 		goto out;
41649f1ee6SEric Sesterhenn 	}
42*f9ef3b95SFabio M. De Francesco 	pptr = kmap_local_page(page);
431da177e4SLinus Torvalds 	curr = pptr + (offset & (PAGE_CACHE_BITS - 1)) / 32;
441da177e4SLinus Torvalds 	i = offset % 32;
451da177e4SLinus Torvalds 	offset &= ~(PAGE_CACHE_BITS - 1);
461da177e4SLinus Torvalds 	if ((size ^ offset) / PAGE_CACHE_BITS)
471da177e4SLinus Torvalds 		end = pptr + PAGE_CACHE_BITS / 32;
481da177e4SLinus Torvalds 	else
491da177e4SLinus Torvalds 		end = pptr + ((size + 31) & (PAGE_CACHE_BITS - 1)) / 32;
501da177e4SLinus Torvalds 
511da177e4SLinus Torvalds 	/* scan the first partial u32 for zero bits */
521da177e4SLinus Torvalds 	val = *curr;
531da177e4SLinus Torvalds 	if (~val) {
541da177e4SLinus Torvalds 		n = be32_to_cpu(val);
551da177e4SLinus Torvalds 		mask = (1U << 31) >> i;
561da177e4SLinus Torvalds 		for (; i < 32; mask >>= 1, i++) {
571da177e4SLinus Torvalds 			if (!(n & mask))
581da177e4SLinus Torvalds 				goto found;
591da177e4SLinus Torvalds 		}
601da177e4SLinus Torvalds 	}
611da177e4SLinus Torvalds 	curr++;
621da177e4SLinus Torvalds 
631da177e4SLinus Torvalds 	/* scan complete u32s for the first zero bit */
641da177e4SLinus Torvalds 	while (1) {
651da177e4SLinus Torvalds 		while (curr < end) {
661da177e4SLinus Torvalds 			val = *curr;
671da177e4SLinus Torvalds 			if (~val) {
681da177e4SLinus Torvalds 				n = be32_to_cpu(val);
691da177e4SLinus Torvalds 				mask = 1 << 31;
701da177e4SLinus Torvalds 				for (i = 0; i < 32; mask >>= 1, i++) {
711da177e4SLinus Torvalds 					if (!(n & mask))
721da177e4SLinus Torvalds 						goto found;
731da177e4SLinus Torvalds 				}
741da177e4SLinus Torvalds 			}
751da177e4SLinus Torvalds 			curr++;
761da177e4SLinus Torvalds 		}
77*f9ef3b95SFabio M. De Francesco 		kunmap_local(pptr);
781da177e4SLinus Torvalds 		offset += PAGE_CACHE_BITS;
791da177e4SLinus Torvalds 		if (offset >= size)
801da177e4SLinus Torvalds 			break;
81090d2b18SPekka Enberg 		page = read_mapping_page(mapping, offset / PAGE_CACHE_BITS,
82090d2b18SPekka Enberg 					 NULL);
83649f1ee6SEric Sesterhenn 		if (IS_ERR(page)) {
84649f1ee6SEric Sesterhenn 			start = size;
85649f1ee6SEric Sesterhenn 			goto out;
86649f1ee6SEric Sesterhenn 		}
87*f9ef3b95SFabio M. De Francesco 		curr = pptr = kmap_local_page(page);
881da177e4SLinus Torvalds 		if ((size ^ offset) / PAGE_CACHE_BITS)
891da177e4SLinus Torvalds 			end = pptr + PAGE_CACHE_BITS / 32;
901da177e4SLinus Torvalds 		else
911da177e4SLinus Torvalds 			end = pptr + ((size + 31) & (PAGE_CACHE_BITS - 1)) / 32;
921da177e4SLinus Torvalds 	}
93c2b3e1f7SJoe Perches 	hfs_dbg(BITMAP, "bitmap full\n");
941da177e4SLinus Torvalds 	start = size;
951da177e4SLinus Torvalds 	goto out;
961da177e4SLinus Torvalds 
971da177e4SLinus Torvalds found:
981da177e4SLinus Torvalds 	start = offset + (curr - pptr) * 32 + i;
991da177e4SLinus Torvalds 	if (start >= size) {
100c2b3e1f7SJoe Perches 		hfs_dbg(BITMAP, "bitmap full\n");
1011da177e4SLinus Torvalds 		goto out;
1021da177e4SLinus Torvalds 	}
1031da177e4SLinus Torvalds 	/* do any partial u32 at the start */
1041da177e4SLinus Torvalds 	len = min(size - start, len);
1051da177e4SLinus Torvalds 	while (1) {
1061da177e4SLinus Torvalds 		n |= mask;
1071da177e4SLinus Torvalds 		if (++i >= 32)
1081da177e4SLinus Torvalds 			break;
1091da177e4SLinus Torvalds 		mask >>= 1;
1101da177e4SLinus Torvalds 		if (!--len || n & mask)
1111da177e4SLinus Torvalds 			goto done;
1121da177e4SLinus Torvalds 	}
1131da177e4SLinus Torvalds 	if (!--len)
1141da177e4SLinus Torvalds 		goto done;
1151da177e4SLinus Torvalds 	*curr++ = cpu_to_be32(n);
1161da177e4SLinus Torvalds 	/* do full u32s */
1171da177e4SLinus Torvalds 	while (1) {
1181da177e4SLinus Torvalds 		while (curr < end) {
1191da177e4SLinus Torvalds 			n = be32_to_cpu(*curr);
1201da177e4SLinus Torvalds 			if (len < 32)
1211da177e4SLinus Torvalds 				goto last;
1221da177e4SLinus Torvalds 			if (n) {
1231da177e4SLinus Torvalds 				len = 32;
1241da177e4SLinus Torvalds 				goto last;
1251da177e4SLinus Torvalds 			}
1261da177e4SLinus Torvalds 			*curr++ = cpu_to_be32(0xffffffff);
1271da177e4SLinus Torvalds 			len -= 32;
1281da177e4SLinus Torvalds 		}
1291da177e4SLinus Torvalds 		set_page_dirty(page);
130*f9ef3b95SFabio M. De Francesco 		kunmap_local(pptr);
1311da177e4SLinus Torvalds 		offset += PAGE_CACHE_BITS;
132090d2b18SPekka Enberg 		page = read_mapping_page(mapping, offset / PAGE_CACHE_BITS,
133090d2b18SPekka Enberg 					 NULL);
134649f1ee6SEric Sesterhenn 		if (IS_ERR(page)) {
135649f1ee6SEric Sesterhenn 			start = size;
136649f1ee6SEric Sesterhenn 			goto out;
137649f1ee6SEric Sesterhenn 		}
138*f9ef3b95SFabio M. De Francesco 		pptr = kmap_local_page(page);
1391da177e4SLinus Torvalds 		curr = pptr;
1401da177e4SLinus Torvalds 		end = pptr + PAGE_CACHE_BITS / 32;
1411da177e4SLinus Torvalds 	}
1421da177e4SLinus Torvalds last:
1431da177e4SLinus Torvalds 	/* do any partial u32 at end */
1441da177e4SLinus Torvalds 	mask = 1U << 31;
1451da177e4SLinus Torvalds 	for (i = 0; i < len; i++) {
1461da177e4SLinus Torvalds 		if (n & mask)
1471da177e4SLinus Torvalds 			break;
1481da177e4SLinus Torvalds 		n |= mask;
1491da177e4SLinus Torvalds 		mask >>= 1;
1501da177e4SLinus Torvalds 	}
1511da177e4SLinus Torvalds done:
1521da177e4SLinus Torvalds 	*curr = cpu_to_be32(n);
1531da177e4SLinus Torvalds 	set_page_dirty(page);
154*f9ef3b95SFabio M. De Francesco 	kunmap_local(pptr);
1551da177e4SLinus Torvalds 	*max = offset + (curr - pptr) * 32 + i - start;
156dd73a01aSChristoph Hellwig 	sbi->free_blocks -= *max;
1579e6c5829SArtem Bityutskiy 	hfsplus_mark_mdb_dirty(sb);
158c2b3e1f7SJoe Perches 	hfs_dbg(BITMAP, "-> %u,%u\n", start, *max);
1591da177e4SLinus Torvalds out:
160dd73a01aSChristoph Hellwig 	mutex_unlock(&sbi->alloc_mutex);
1611da177e4SLinus Torvalds 	return start;
1621da177e4SLinus Torvalds }
1631da177e4SLinus Torvalds 
hfsplus_block_free(struct super_block * sb,u32 offset,u32 count)1641da177e4SLinus Torvalds int hfsplus_block_free(struct super_block *sb, u32 offset, u32 count)
1651da177e4SLinus Torvalds {
166dd73a01aSChristoph Hellwig 	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
1671da177e4SLinus Torvalds 	struct page *page;
1681da177e4SLinus Torvalds 	struct address_space *mapping;
1691da177e4SLinus Torvalds 	__be32 *pptr, *curr, *end;
1701da177e4SLinus Torvalds 	u32 mask, len, pnr;
1711da177e4SLinus Torvalds 	int i;
1721da177e4SLinus Torvalds 
1731da177e4SLinus Torvalds 	/* is there any actual work to be done? */
1741da177e4SLinus Torvalds 	if (!count)
1751da177e4SLinus Torvalds 		return 0;
1761da177e4SLinus Torvalds 
177c2b3e1f7SJoe Perches 	hfs_dbg(BITMAP, "block_free: %u,%u\n", offset, count);
1781da177e4SLinus Torvalds 	/* are all of the bits in range? */
179dd73a01aSChristoph Hellwig 	if ((offset + count) > sbi->total_blocks)
1805daa669cSAlan Cox 		return -ENOENT;
1811da177e4SLinus Torvalds 
182dd73a01aSChristoph Hellwig 	mutex_lock(&sbi->alloc_mutex);
183dd73a01aSChristoph Hellwig 	mapping = sbi->alloc_file->i_mapping;
1841da177e4SLinus Torvalds 	pnr = offset / PAGE_CACHE_BITS;
185090d2b18SPekka Enberg 	page = read_mapping_page(mapping, pnr, NULL);
1865daa669cSAlan Cox 	if (IS_ERR(page))
1875daa669cSAlan Cox 		goto kaboom;
188*f9ef3b95SFabio M. De Francesco 	pptr = kmap_local_page(page);
1891da177e4SLinus Torvalds 	curr = pptr + (offset & (PAGE_CACHE_BITS - 1)) / 32;
1901da177e4SLinus Torvalds 	end = pptr + PAGE_CACHE_BITS / 32;
1911da177e4SLinus Torvalds 	len = count;
1921da177e4SLinus Torvalds 
1931da177e4SLinus Torvalds 	/* do any partial u32 at the start */
1941da177e4SLinus Torvalds 	i = offset % 32;
1951da177e4SLinus Torvalds 	if (i) {
1961da177e4SLinus Torvalds 		int j = 32 - i;
1971da177e4SLinus Torvalds 		mask = 0xffffffffU << j;
1981da177e4SLinus Torvalds 		if (j > count) {
1991da177e4SLinus Torvalds 			mask |= 0xffffffffU >> (i + count);
2001da177e4SLinus Torvalds 			*curr++ &= cpu_to_be32(mask);
2011da177e4SLinus Torvalds 			goto out;
2021da177e4SLinus Torvalds 		}
2031da177e4SLinus Torvalds 		*curr++ &= cpu_to_be32(mask);
2041da177e4SLinus Torvalds 		count -= j;
2051da177e4SLinus Torvalds 	}
2061da177e4SLinus Torvalds 
2071da177e4SLinus Torvalds 	/* do full u32s */
2081da177e4SLinus Torvalds 	while (1) {
2091da177e4SLinus Torvalds 		while (curr < end) {
2101da177e4SLinus Torvalds 			if (count < 32)
2111da177e4SLinus Torvalds 				goto done;
2121da177e4SLinus Torvalds 			*curr++ = 0;
2131da177e4SLinus Torvalds 			count -= 32;
2141da177e4SLinus Torvalds 		}
2151da177e4SLinus Torvalds 		if (!count)
2161da177e4SLinus Torvalds 			break;
2171da177e4SLinus Torvalds 		set_page_dirty(page);
218*f9ef3b95SFabio M. De Francesco 		kunmap_local(pptr);
219090d2b18SPekka Enberg 		page = read_mapping_page(mapping, ++pnr, NULL);
2205daa669cSAlan Cox 		if (IS_ERR(page))
2215daa669cSAlan Cox 			goto kaboom;
222*f9ef3b95SFabio M. De Francesco 		pptr = kmap_local_page(page);
2231da177e4SLinus Torvalds 		curr = pptr;
2241da177e4SLinus Torvalds 		end = pptr + PAGE_CACHE_BITS / 32;
2251da177e4SLinus Torvalds 	}
2261da177e4SLinus Torvalds done:
2271da177e4SLinus Torvalds 	/* do any partial u32 at end */
2281da177e4SLinus Torvalds 	if (count) {
2291da177e4SLinus Torvalds 		mask = 0xffffffffU >> count;
2301da177e4SLinus Torvalds 		*curr &= cpu_to_be32(mask);
2311da177e4SLinus Torvalds 	}
2321da177e4SLinus Torvalds out:
2331da177e4SLinus Torvalds 	set_page_dirty(page);
234*f9ef3b95SFabio M. De Francesco 	kunmap_local(pptr);
235dd73a01aSChristoph Hellwig 	sbi->free_blocks += len;
2369e6c5829SArtem Bityutskiy 	hfsplus_mark_mdb_dirty(sb);
237dd73a01aSChristoph Hellwig 	mutex_unlock(&sbi->alloc_mutex);
2381da177e4SLinus Torvalds 
2391da177e4SLinus Torvalds 	return 0;
2405daa669cSAlan Cox 
2415daa669cSAlan Cox kaboom:
242865f38a3SVyacheslav Dubeyko 	pr_crit("unable to mark blocks free: error %ld\n", PTR_ERR(page));
2435daa669cSAlan Cox 	mutex_unlock(&sbi->alloc_mutex);
2445daa669cSAlan Cox 
2455daa669cSAlan Cox 	return -EIO;
2461da177e4SLinus Torvalds }
247