142 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 20134 * Phillip Lougher <phillip@squashfs.org.uk>5 */6 7#include <linux/kernel.h>8#include <linux/slab.h>9#include <linux/pagemap.h>10#include "squashfs_fs_sb.h"11#include "decompressor.h"12#include "page_actor.h"13 14/*15 * This file contains implementations of page_actor for decompressing into16 * an intermediate buffer, and for decompressing directly into the17 * page cache.18 *19 * Calling code should avoid sleeping between calls to squashfs_first_page()20 * and squashfs_finish_page().21 */22 23/* Implementation of page_actor for decompressing into intermediate buffer */24static void *cache_first_page(struct squashfs_page_actor *actor)25{26 actor->next_page = 1;27 return actor->buffer[0];28}29 30static void *cache_next_page(struct squashfs_page_actor *actor)31{32 if (actor->next_page == actor->pages)33 return NULL;34 35 return actor->buffer[actor->next_page++];36}37 38static void cache_finish_page(struct squashfs_page_actor *actor)39{40 /* empty */41}42 43struct squashfs_page_actor *squashfs_page_actor_init(void **buffer,44 int pages, int length)45{46 struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);47 48 if (actor == NULL)49 return NULL;50 51 actor->length = length ? : pages * PAGE_SIZE;52 actor->buffer = buffer;53 actor->pages = pages;54 actor->next_page = 0;55 actor->tmp_buffer = NULL;56 actor->squashfs_first_page = cache_first_page;57 actor->squashfs_next_page = cache_next_page;58 actor->squashfs_finish_page = cache_finish_page;59 return actor;60}61 62/* Implementation of page_actor for decompressing directly into page cache. */63static loff_t page_next_index(struct squashfs_page_actor *actor)64{65 return page_folio(actor->page[actor->next_page])->index;66}67 68static void *handle_next_page(struct squashfs_page_actor *actor)69{70 int max_pages = (actor->length + PAGE_SIZE - 1) >> PAGE_SHIFT;71 72 if (actor->returned_pages == max_pages)73 return NULL;74 75 if ((actor->next_page == actor->pages) ||76 (actor->next_index != page_next_index(actor))) {77 actor->next_index++;78 actor->returned_pages++;79 actor->last_page = NULL;80 return actor->alloc_buffer ? actor->tmp_buffer : ERR_PTR(-ENOMEM);81 }82 83 actor->next_index++;84 actor->returned_pages++;85 actor->last_page = actor->page[actor->next_page];86 return actor->pageaddr = kmap_local_page(actor->page[actor->next_page++]);87}88 89static void *direct_first_page(struct squashfs_page_actor *actor)90{91 return handle_next_page(actor);92}93 94static void *direct_next_page(struct squashfs_page_actor *actor)95{96 if (actor->pageaddr) {97 kunmap_local(actor->pageaddr);98 actor->pageaddr = NULL;99 }100 101 return handle_next_page(actor);102}103 104static void direct_finish_page(struct squashfs_page_actor *actor)105{106 if (actor->pageaddr)107 kunmap_local(actor->pageaddr);108}109 110struct squashfs_page_actor *squashfs_page_actor_init_special(struct squashfs_sb_info *msblk,111 struct page **page, int pages, int length, loff_t start_index)112{113 struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);114 115 if (actor == NULL)116 return NULL;117 118 if (msblk->decompressor->alloc_buffer) {119 actor->tmp_buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);120 121 if (actor->tmp_buffer == NULL) {122 kfree(actor);123 return NULL;124 }125 } else126 actor->tmp_buffer = NULL;127 128 actor->length = length ? : pages * PAGE_SIZE;129 actor->page = page;130 actor->pages = pages;131 actor->next_page = 0;132 actor->returned_pages = 0;133 actor->next_index = start_index >> PAGE_SHIFT;134 actor->pageaddr = NULL;135 actor->last_page = NULL;136 actor->alloc_buffer = msblk->decompressor->alloc_buffer;137 actor->squashfs_first_page = direct_first_page;138 actor->squashfs_next_page = direct_next_page;139 actor->squashfs_finish_page = direct_finish_page;140 return actor;141}142