page_actor.c 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2013
  4. * Phillip Lougher <[email protected]>
  5. */
  6. #include <linux/kernel.h>
  7. #include <linux/slab.h>
  8. #include <linux/pagemap.h>
  9. #include "squashfs_fs_sb.h"
  10. #include "decompressor.h"
  11. #include "page_actor.h"
  12. /*
  13. * This file contains implementations of page_actor for decompressing into
  14. * an intermediate buffer, and for decompressing directly into the
  15. * page cache.
  16. *
  17. * Calling code should avoid sleeping between calls to squashfs_first_page()
  18. * and squashfs_finish_page().
  19. */
  20. /* Implementation of page_actor for decompressing into intermediate buffer */
  21. static void *cache_first_page(struct squashfs_page_actor *actor)
  22. {
  23. actor->next_page = 1;
  24. return actor->buffer[0];
  25. }
  26. static void *cache_next_page(struct squashfs_page_actor *actor)
  27. {
  28. if (actor->next_page == actor->pages)
  29. return NULL;
  30. return actor->buffer[actor->next_page++];
  31. }
  32. static void cache_finish_page(struct squashfs_page_actor *actor)
  33. {
  34. /* empty */
  35. }
  36. struct squashfs_page_actor *squashfs_page_actor_init(void **buffer,
  37. int pages, int length)
  38. {
  39. struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
  40. if (actor == NULL)
  41. return NULL;
  42. actor->length = length ? : pages * PAGE_SIZE;
  43. actor->buffer = buffer;
  44. actor->pages = pages;
  45. actor->next_page = 0;
  46. actor->tmp_buffer = NULL;
  47. actor->squashfs_first_page = cache_first_page;
  48. actor->squashfs_next_page = cache_next_page;
  49. actor->squashfs_finish_page = cache_finish_page;
  50. return actor;
  51. }
  52. /* Implementation of page_actor for decompressing directly into page cache. */
  53. static void *handle_next_page(struct squashfs_page_actor *actor)
  54. {
  55. int max_pages = (actor->length + PAGE_SIZE - 1) >> PAGE_SHIFT;
  56. if (actor->returned_pages == max_pages)
  57. return NULL;
  58. if ((actor->next_page == actor->pages) ||
  59. (actor->next_index != actor->page[actor->next_page]->index)) {
  60. actor->next_index++;
  61. actor->returned_pages++;
  62. actor->last_page = NULL;
  63. return actor->alloc_buffer ? actor->tmp_buffer : ERR_PTR(-ENOMEM);
  64. }
  65. actor->next_index++;
  66. actor->returned_pages++;
  67. actor->last_page = actor->page[actor->next_page];
  68. return actor->pageaddr = kmap_local_page(actor->page[actor->next_page++]);
  69. }
  70. static void *direct_first_page(struct squashfs_page_actor *actor)
  71. {
  72. return handle_next_page(actor);
  73. }
  74. static void *direct_next_page(struct squashfs_page_actor *actor)
  75. {
  76. if (actor->pageaddr) {
  77. kunmap_local(actor->pageaddr);
  78. actor->pageaddr = NULL;
  79. }
  80. return handle_next_page(actor);
  81. }
  82. static void direct_finish_page(struct squashfs_page_actor *actor)
  83. {
  84. if (actor->pageaddr)
  85. kunmap_local(actor->pageaddr);
  86. }
  87. struct squashfs_page_actor *squashfs_page_actor_init_special(struct squashfs_sb_info *msblk,
  88. struct page **page, int pages, int length)
  89. {
  90. struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
  91. if (actor == NULL)
  92. return NULL;
  93. if (msblk->decompressor->alloc_buffer) {
  94. actor->tmp_buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
  95. if (actor->tmp_buffer == NULL) {
  96. kfree(actor);
  97. return NULL;
  98. }
  99. } else
  100. actor->tmp_buffer = NULL;
  101. actor->length = length ? : pages * PAGE_SIZE;
  102. actor->page = page;
  103. actor->pages = pages;
  104. actor->next_page = 0;
  105. actor->returned_pages = 0;
  106. actor->next_index = page[0]->index & ~((1 << (msblk->block_log - PAGE_SHIFT)) - 1);
  107. actor->pageaddr = NULL;
  108. actor->last_page = NULL;
  109. actor->alloc_buffer = msblk->decompressor->alloc_buffer;
  110. actor->squashfs_first_page = direct_first_page;
  111. actor->squashfs_next_page = direct_next_page;
  112. actor->squashfs_finish_page = direct_finish_page;
  113. return actor;
  114. }