LCOV - code coverage report
Current view: top level - fs/iomap - seek.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 0 93 0.0 %
Date: 2022-04-01 14:35:51 Functions: 0 6 0.0 %
Branches: 0 74 0.0 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0
       2                 :            : /*
       3                 :            :  * Copyright (C) 2017 Red Hat, Inc.
       4                 :            :  * Copyright (c) 2018 Christoph Hellwig.
       5                 :            :  */
       6                 :            : #include <linux/module.h>
       7                 :            : #include <linux/compiler.h>
       8                 :            : #include <linux/fs.h>
       9                 :            : #include <linux/iomap.h>
      10                 :            : #include <linux/pagemap.h>
      11                 :            : #include <linux/pagevec.h>
      12                 :            : 
      13                 :            : /*
      14                 :            :  * Seek for SEEK_DATA / SEEK_HOLE within @page, starting at @lastoff.
      15                 :            :  * Returns true if found and updates @lastoff to the offset in file.
      16                 :            :  */
      17                 :            : static bool
      18                 :          0 : page_seek_hole_data(struct inode *inode, struct page *page, loff_t *lastoff,
      19                 :            :                 int whence)
      20                 :            : {
      21                 :          0 :         const struct address_space_operations *ops = inode->i_mapping->a_ops;
      22         [ #  # ]:          0 :         unsigned int bsize = i_blocksize(inode), off;
      23                 :          0 :         bool seek_data = whence == SEEK_DATA;
      24         [ #  # ]:          0 :         loff_t poff = page_offset(page);
      25                 :            : 
      26   [ #  #  #  # ]:          0 :         if (WARN_ON_ONCE(*lastoff >= poff + PAGE_SIZE))
      27                 :            :                 return false;
      28                 :            : 
      29         [ #  # ]:          0 :         if (*lastoff < poff) {
      30                 :            :                 /*
      31                 :            :                  * Last offset smaller than the start of the page means we found
      32                 :            :                  * a hole:
      33                 :            :                  */
      34         [ #  # ]:          0 :                 if (whence == SEEK_HOLE)
      35                 :            :                         return true;
      36                 :          0 :                 *lastoff = poff;
      37                 :            :         }
      38                 :            : 
      39                 :            :         /*
      40                 :            :          * Just check the page unless we can and should check block ranges:
      41                 :            :          */
      42   [ #  #  #  # ]:          0 :         if (bsize == PAGE_SIZE || !ops->is_partially_uptodate)
      43                 :          0 :                 return PageUptodate(page) == seek_data;
      44                 :            : 
      45                 :          0 :         lock_page(page);
      46         [ #  # ]:          0 :         if (unlikely(page->mapping != inode->i_mapping))
      47                 :          0 :                 goto out_unlock_not_found;
      48                 :            : 
      49         [ #  # ]:          0 :         for (off = 0; off < PAGE_SIZE; off += bsize) {
      50         [ #  # ]:          0 :                 if (offset_in_page(*lastoff) >= off + bsize)
      51                 :          0 :                         continue;
      52         [ #  # ]:          0 :                 if (ops->is_partially_uptodate(page, off, bsize) == seek_data) {
      53                 :          0 :                         unlock_page(page);
      54                 :          0 :                         return true;
      55                 :            :                 }
      56                 :          0 :                 *lastoff = poff + off + bsize;
      57                 :            :         }
      58                 :            : 
      59                 :          0 : out_unlock_not_found:
      60                 :          0 :         unlock_page(page);
      61                 :          0 :         return false;
      62                 :            : }
      63                 :            : 
      64                 :            : /*
      65                 :            :  * Seek for SEEK_DATA / SEEK_HOLE in the page cache.
      66                 :            :  *
      67                 :            :  * Within unwritten extents, the page cache determines which parts are holes
      68                 :            :  * and which are data: uptodate buffer heads count as data; everything else
      69                 :            :  * counts as a hole.
      70                 :            :  *
      71                 :            :  * Returns the resulting offset on successs, and -ENOENT otherwise.
      72                 :            :  */
      73                 :            : static loff_t
      74                 :          0 : page_cache_seek_hole_data(struct inode *inode, loff_t offset, loff_t length,
      75                 :            :                 int whence)
      76                 :            : {
      77                 :          0 :         pgoff_t index = offset >> PAGE_SHIFT;
      78                 :          0 :         pgoff_t end = DIV_ROUND_UP(offset + length, PAGE_SIZE);
      79                 :          0 :         loff_t lastoff = offset;
      80                 :          0 :         struct pagevec pvec;
      81                 :            : 
      82         [ #  # ]:          0 :         if (length <= 0)
      83                 :            :                 return -ENOENT;
      84                 :            : 
      85                 :          0 :         pagevec_init(&pvec);
      86                 :            : 
      87                 :          0 :         do {
      88                 :          0 :                 unsigned nr_pages, i;
      89                 :            : 
      90                 :          0 :                 nr_pages = pagevec_lookup_range(&pvec, inode->i_mapping, &index,
      91                 :            :                                                 end - 1);
      92         [ #  # ]:          0 :                 if (nr_pages == 0)
      93                 :            :                         break;
      94                 :            : 
      95         [ #  # ]:          0 :                 for (i = 0; i < nr_pages; i++) {
      96                 :          0 :                         struct page *page = pvec.pages[i];
      97                 :            : 
      98         [ #  # ]:          0 :                         if (page_seek_hole_data(inode, page, &lastoff, whence))
      99                 :          0 :                                 goto check_range;
     100                 :          0 :                         lastoff = page_offset(page) + PAGE_SIZE;
     101                 :            :                 }
     102         [ #  # ]:          0 :                 pagevec_release(&pvec);
     103         [ #  # ]:          0 :         } while (index < end);
     104                 :            : 
     105                 :            :         /* When no page at lastoff and we are not done, we found a hole. */
     106         [ #  # ]:          0 :         if (whence != SEEK_HOLE)
     107                 :          0 :                 goto not_found;
     108                 :            : 
     109                 :          0 : check_range:
     110         [ #  # ]:          0 :         if (lastoff < offset + length)
     111                 :          0 :                 goto out;
     112                 :          0 : not_found:
     113                 :          0 :         lastoff = -ENOENT;
     114                 :          0 : out:
     115         [ #  # ]:          0 :         pagevec_release(&pvec);
     116                 :          0 :         return lastoff;
     117                 :            : }
     118                 :            : 
     119                 :            : 
     120                 :            : static loff_t
     121                 :          0 : iomap_seek_hole_actor(struct inode *inode, loff_t offset, loff_t length,
     122                 :            :                       void *data, struct iomap *iomap, struct iomap *srcmap)
     123                 :            : {
     124      [ #  #  # ]:          0 :         switch (iomap->type) {
     125                 :          0 :         case IOMAP_UNWRITTEN:
     126                 :          0 :                 offset = page_cache_seek_hole_data(inode, offset, length,
     127                 :            :                                                    SEEK_HOLE);
     128         [ #  # ]:          0 :                 if (offset < 0)
     129                 :            :                         return length;
     130                 :            :                 /* fall through */
     131                 :            :         case IOMAP_HOLE:
     132                 :          0 :                 *(loff_t *)data = offset;
     133                 :          0 :                 return 0;
     134                 :            :         default:
     135                 :            :                 return length;
     136                 :            :         }
     137                 :            : }
     138                 :            : 
     139                 :            : loff_t
     140                 :          0 : iomap_seek_hole(struct inode *inode, loff_t offset, const struct iomap_ops *ops)
     141                 :            : {
     142         [ #  # ]:          0 :         loff_t size = i_size_read(inode);
     143                 :          0 :         loff_t length = size - offset;
     144                 :          0 :         loff_t ret;
     145                 :            : 
     146                 :            :         /* Nothing to be found before or beyond the end of the file. */
     147         [ #  # ]:          0 :         if (offset < 0 || offset >= size)
     148                 :            :                 return -ENXIO;
     149                 :            : 
     150         [ #  # ]:          0 :         while (length > 0) {
     151                 :          0 :                 ret = iomap_apply(inode, offset, length, IOMAP_REPORT, ops,
     152                 :            :                                   &offset, iomap_seek_hole_actor);
     153         [ #  # ]:          0 :                 if (ret < 0)
     154                 :          0 :                         return ret;
     155         [ #  # ]:          0 :                 if (ret == 0)
     156                 :            :                         break;
     157                 :            : 
     158                 :          0 :                 offset += ret;
     159                 :          0 :                 length -= ret;
     160                 :            :         }
     161                 :            : 
     162                 :          0 :         return offset;
     163                 :            : }
     164                 :            : EXPORT_SYMBOL_GPL(iomap_seek_hole);
     165                 :            : 
     166                 :            : static loff_t
     167                 :          0 : iomap_seek_data_actor(struct inode *inode, loff_t offset, loff_t length,
     168                 :            :                       void *data, struct iomap *iomap, struct iomap *srcmap)
     169                 :            : {
     170      [ #  #  # ]:          0 :         switch (iomap->type) {
     171                 :            :         case IOMAP_HOLE:
     172                 :            :                 return length;
     173                 :          0 :         case IOMAP_UNWRITTEN:
     174                 :          0 :                 offset = page_cache_seek_hole_data(inode, offset, length,
     175                 :            :                                                    SEEK_DATA);
     176         [ #  # ]:          0 :                 if (offset < 0)
     177                 :            :                         return length;
     178                 :            :                 /*FALLTHRU*/
     179                 :            :         default:
     180                 :          0 :                 *(loff_t *)data = offset;
     181                 :          0 :                 return 0;
     182                 :            :         }
     183                 :            : }
     184                 :            : 
     185                 :            : loff_t
     186                 :          0 : iomap_seek_data(struct inode *inode, loff_t offset, const struct iomap_ops *ops)
     187                 :            : {
     188         [ #  # ]:          0 :         loff_t size = i_size_read(inode);
     189                 :          0 :         loff_t length = size - offset;
     190                 :          0 :         loff_t ret;
     191                 :            : 
     192                 :            :         /* Nothing to be found before or beyond the end of the file. */
     193         [ #  # ]:          0 :         if (offset < 0 || offset >= size)
     194                 :            :                 return -ENXIO;
     195                 :            : 
     196         [ #  # ]:          0 :         while (length > 0) {
     197                 :          0 :                 ret = iomap_apply(inode, offset, length, IOMAP_REPORT, ops,
     198                 :            :                                   &offset, iomap_seek_data_actor);
     199         [ #  # ]:          0 :                 if (ret < 0)
     200                 :          0 :                         return ret;
     201         [ #  # ]:          0 :                 if (ret == 0)
     202                 :            :                         break;
     203                 :            : 
     204                 :          0 :                 offset += ret;
     205                 :          0 :                 length -= ret;
     206                 :            :         }
     207                 :            : 
     208         [ #  # ]:          0 :         if (length <= 0)
     209                 :            :                 return -ENXIO;
     210                 :          0 :         return offset;
     211                 :            : }
     212                 :            : EXPORT_SYMBOL_GPL(iomap_seek_data);

Generated by: LCOV version 1.14