162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2013
462306a36Sopenharmony_ci * Phillip Lougher <phillip@squashfs.org.uk>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/kernel.h>
862306a36Sopenharmony_ci#include <linux/slab.h>
962306a36Sopenharmony_ci#include <linux/pagemap.h>
1062306a36Sopenharmony_ci#include "squashfs_fs_sb.h"
1162306a36Sopenharmony_ci#include "decompressor.h"
1262306a36Sopenharmony_ci#include "page_actor.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci/*
1562306a36Sopenharmony_ci * This file contains implementations of page_actor for decompressing into
1662306a36Sopenharmony_ci * an intermediate buffer, and for decompressing directly into the
1762306a36Sopenharmony_ci * page cache.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * Calling code should avoid sleeping between calls to squashfs_first_page()
2062306a36Sopenharmony_ci * and squashfs_finish_page().
2162306a36Sopenharmony_ci */
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* Implementation of page_actor for decompressing into intermediate buffer */
2462306a36Sopenharmony_cistatic void *cache_first_page(struct squashfs_page_actor *actor)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	actor->next_page = 1;
2762306a36Sopenharmony_ci	return actor->buffer[0];
2862306a36Sopenharmony_ci}
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic void *cache_next_page(struct squashfs_page_actor *actor)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	if (actor->next_page == actor->pages)
3362306a36Sopenharmony_ci		return NULL;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	return actor->buffer[actor->next_page++];
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic void cache_finish_page(struct squashfs_page_actor *actor)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	/* empty */
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistruct squashfs_page_actor *squashfs_page_actor_init(void **buffer,
4462306a36Sopenharmony_ci	int pages, int length)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	if (actor == NULL)
4962306a36Sopenharmony_ci		return NULL;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	actor->length = length ? : pages * PAGE_SIZE;
5262306a36Sopenharmony_ci	actor->buffer = buffer;
5362306a36Sopenharmony_ci	actor->pages = pages;
5462306a36Sopenharmony_ci	actor->next_page = 0;
5562306a36Sopenharmony_ci	actor->tmp_buffer = NULL;
5662306a36Sopenharmony_ci	actor->squashfs_first_page = cache_first_page;
5762306a36Sopenharmony_ci	actor->squashfs_next_page = cache_next_page;
5862306a36Sopenharmony_ci	actor->squashfs_finish_page = cache_finish_page;
5962306a36Sopenharmony_ci	return actor;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/* Implementation of page_actor for decompressing directly into page cache. */
6362306a36Sopenharmony_cistatic void *handle_next_page(struct squashfs_page_actor *actor)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	int max_pages = (actor->length + PAGE_SIZE - 1) >> PAGE_SHIFT;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	if (actor->returned_pages == max_pages)
6862306a36Sopenharmony_ci		return NULL;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	if ((actor->next_page == actor->pages) ||
7162306a36Sopenharmony_ci			(actor->next_index != actor->page[actor->next_page]->index)) {
7262306a36Sopenharmony_ci		actor->next_index++;
7362306a36Sopenharmony_ci		actor->returned_pages++;
7462306a36Sopenharmony_ci		actor->last_page = NULL;
7562306a36Sopenharmony_ci		return actor->alloc_buffer ? actor->tmp_buffer : ERR_PTR(-ENOMEM);
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	actor->next_index++;
7962306a36Sopenharmony_ci	actor->returned_pages++;
8062306a36Sopenharmony_ci	actor->last_page = actor->page[actor->next_page];
8162306a36Sopenharmony_ci	return actor->pageaddr = kmap_local_page(actor->page[actor->next_page++]);
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic void *direct_first_page(struct squashfs_page_actor *actor)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	return handle_next_page(actor);
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic void *direct_next_page(struct squashfs_page_actor *actor)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	if (actor->pageaddr) {
9262306a36Sopenharmony_ci		kunmap_local(actor->pageaddr);
9362306a36Sopenharmony_ci		actor->pageaddr = NULL;
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	return handle_next_page(actor);
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic void direct_finish_page(struct squashfs_page_actor *actor)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	if (actor->pageaddr)
10262306a36Sopenharmony_ci		kunmap_local(actor->pageaddr);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistruct squashfs_page_actor *squashfs_page_actor_init_special(struct squashfs_sb_info *msblk,
10662306a36Sopenharmony_ci	struct page **page, int pages, int length)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	if (actor == NULL)
11162306a36Sopenharmony_ci		return NULL;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	if (msblk->decompressor->alloc_buffer) {
11462306a36Sopenharmony_ci		actor->tmp_buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci		if (actor->tmp_buffer == NULL) {
11762306a36Sopenharmony_ci			kfree(actor);
11862306a36Sopenharmony_ci			return NULL;
11962306a36Sopenharmony_ci		}
12062306a36Sopenharmony_ci	} else
12162306a36Sopenharmony_ci		actor->tmp_buffer = NULL;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	actor->length = length ? : pages * PAGE_SIZE;
12462306a36Sopenharmony_ci	actor->page = page;
12562306a36Sopenharmony_ci	actor->pages = pages;
12662306a36Sopenharmony_ci	actor->next_page = 0;
12762306a36Sopenharmony_ci	actor->returned_pages = 0;
12862306a36Sopenharmony_ci	actor->next_index = page[0]->index & ~((1 << (msblk->block_log - PAGE_SHIFT)) - 1);
12962306a36Sopenharmony_ci	actor->pageaddr = NULL;
13062306a36Sopenharmony_ci	actor->last_page = NULL;
13162306a36Sopenharmony_ci	actor->alloc_buffer = msblk->decompressor->alloc_buffer;
13262306a36Sopenharmony_ci	actor->squashfs_first_page = direct_first_page;
13362306a36Sopenharmony_ci	actor->squashfs_next_page = direct_next_page;
13462306a36Sopenharmony_ci	actor->squashfs_finish_page = direct_finish_page;
13562306a36Sopenharmony_ci	return actor;
13662306a36Sopenharmony_ci}
137