uboot/fs/erofs/data.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2#include "internal.h"
   3#include "decompress.h"
   4
   5static int erofs_map_blocks_flatmode(struct erofs_inode *inode,
   6                                     struct erofs_map_blocks *map,
   7                                     int flags)
   8{
   9        int err = 0;
  10        erofs_blk_t nblocks, lastblk;
  11        u64 offset = map->m_la;
  12        struct erofs_inode *vi = inode;
  13        bool tailendpacking = (vi->datalayout == EROFS_INODE_FLAT_INLINE);
  14
  15        nblocks = DIV_ROUND_UP(inode->i_size, EROFS_BLKSIZ);
  16        lastblk = nblocks - tailendpacking;
  17
  18        /* there is no hole in flatmode */
  19        map->m_flags = EROFS_MAP_MAPPED;
  20
  21        if (offset < blknr_to_addr(lastblk)) {
  22                map->m_pa = blknr_to_addr(vi->u.i_blkaddr) + map->m_la;
  23                map->m_plen = blknr_to_addr(lastblk) - offset;
  24        } else if (tailendpacking) {
  25                /* 2 - inode inline B: inode, [xattrs], inline last blk... */
  26                map->m_pa = iloc(vi->nid) + vi->inode_isize +
  27                        vi->xattr_isize + erofs_blkoff(map->m_la);
  28                map->m_plen = inode->i_size - offset;
  29
  30                /* inline data should be located in one meta block */
  31                if (erofs_blkoff(map->m_pa) + map->m_plen > PAGE_SIZE) {
  32                        erofs_err("inline data cross block boundary @ nid %" PRIu64,
  33                                  vi->nid);
  34                        DBG_BUGON(1);
  35                        err = -EFSCORRUPTED;
  36                        goto err_out;
  37                }
  38
  39                map->m_flags |= EROFS_MAP_META;
  40        } else {
  41                erofs_err("internal error @ nid: %" PRIu64 " (size %llu), m_la 0x%" PRIx64,
  42                          vi->nid, (unsigned long long)inode->i_size, map->m_la);
  43                DBG_BUGON(1);
  44                err = -EIO;
  45                goto err_out;
  46        }
  47
  48        map->m_llen = map->m_plen;
  49err_out:
  50        return err;
  51}
  52
  53int erofs_map_blocks(struct erofs_inode *inode,
  54                     struct erofs_map_blocks *map, int flags)
  55{
  56        struct erofs_inode *vi = inode;
  57        struct erofs_inode_chunk_index *idx;
  58        u8 buf[EROFS_BLKSIZ];
  59        u64 chunknr;
  60        unsigned int unit;
  61        erofs_off_t pos;
  62        int err = 0;
  63
  64        map->m_deviceid = 0;
  65        if (map->m_la >= inode->i_size) {
  66                /* leave out-of-bound access unmapped */
  67                map->m_flags = 0;
  68                map->m_plen = 0;
  69                goto out;
  70        }
  71
  72        if (vi->datalayout != EROFS_INODE_CHUNK_BASED)
  73                return erofs_map_blocks_flatmode(inode, map, flags);
  74
  75        if (vi->u.chunkformat & EROFS_CHUNK_FORMAT_INDEXES)
  76                unit = sizeof(*idx);                    /* chunk index */
  77        else
  78                unit = EROFS_BLOCK_MAP_ENTRY_SIZE;      /* block map */
  79
  80        chunknr = map->m_la >> vi->u.chunkbits;
  81        pos = roundup(iloc(vi->nid) + vi->inode_isize +
  82                      vi->xattr_isize, unit) + unit * chunknr;
  83
  84        err = erofs_blk_read(buf, erofs_blknr(pos), 1);
  85        if (err < 0)
  86                return -EIO;
  87
  88        map->m_la = chunknr << vi->u.chunkbits;
  89        map->m_plen = min_t(erofs_off_t, 1UL << vi->u.chunkbits,
  90                            roundup(inode->i_size - map->m_la, EROFS_BLKSIZ));
  91
  92        /* handle block map */
  93        if (!(vi->u.chunkformat & EROFS_CHUNK_FORMAT_INDEXES)) {
  94                __le32 *blkaddr = (void *)buf + erofs_blkoff(pos);
  95
  96                if (le32_to_cpu(*blkaddr) == EROFS_NULL_ADDR) {
  97                        map->m_flags = 0;
  98                } else {
  99                        map->m_pa = blknr_to_addr(le32_to_cpu(*blkaddr));
 100                        map->m_flags = EROFS_MAP_MAPPED;
 101                }
 102                goto out;
 103        }
 104        /* parse chunk indexes */
 105        idx = (void *)buf + erofs_blkoff(pos);
 106        switch (le32_to_cpu(idx->blkaddr)) {
 107        case EROFS_NULL_ADDR:
 108                map->m_flags = 0;
 109                break;
 110        default:
 111                map->m_deviceid = le16_to_cpu(idx->device_id) &
 112                        sbi.device_id_mask;
 113                map->m_pa = blknr_to_addr(le32_to_cpu(idx->blkaddr));
 114                map->m_flags = EROFS_MAP_MAPPED;
 115                break;
 116        }
 117out:
 118        map->m_llen = map->m_plen;
 119        return err;
 120}
 121
 122int erofs_map_dev(struct erofs_sb_info *sbi, struct erofs_map_dev *map)
 123{
 124        struct erofs_device_info *dif;
 125        int id;
 126
 127        if (map->m_deviceid) {
 128                if (sbi->extra_devices < map->m_deviceid)
 129                        return -ENODEV;
 130        } else if (sbi->extra_devices) {
 131                for (id = 0; id < sbi->extra_devices; ++id) {
 132                        erofs_off_t startoff, length;
 133
 134                        dif = sbi->devs + id;
 135                        if (!dif->mapped_blkaddr)
 136                                continue;
 137                        startoff = blknr_to_addr(dif->mapped_blkaddr);
 138                        length = blknr_to_addr(dif->blocks);
 139
 140                        if (map->m_pa >= startoff &&
 141                            map->m_pa < startoff + length) {
 142                                map->m_pa -= startoff;
 143                                break;
 144                        }
 145                }
 146        }
 147        return 0;
 148}
 149
 150static int erofs_read_raw_data(struct erofs_inode *inode, char *buffer,
 151                               erofs_off_t size, erofs_off_t offset)
 152{
 153        struct erofs_map_blocks map = {
 154                .index = UINT_MAX,
 155        };
 156        struct erofs_map_dev mdev;
 157        int ret;
 158        erofs_off_t ptr = offset;
 159
 160        while (ptr < offset + size) {
 161                char *const estart = buffer + ptr - offset;
 162                erofs_off_t eend;
 163
 164                map.m_la = ptr;
 165                ret = erofs_map_blocks(inode, &map, 0);
 166                if (ret)
 167                        return ret;
 168
 169                DBG_BUGON(map.m_plen != map.m_llen);
 170
 171                mdev = (struct erofs_map_dev) {
 172                        .m_deviceid = map.m_deviceid,
 173                        .m_pa = map.m_pa,
 174                };
 175                ret = erofs_map_dev(&sbi, &mdev);
 176                if (ret)
 177                        return ret;
 178
 179                /* trim extent */
 180                eend = min(offset + size, map.m_la + map.m_llen);
 181                DBG_BUGON(ptr < map.m_la);
 182
 183                if (!(map.m_flags & EROFS_MAP_MAPPED)) {
 184                        if (!map.m_llen) {
 185                                /* reached EOF */
 186                                memset(estart, 0, offset + size - ptr);
 187                                ptr = offset + size;
 188                                continue;
 189                        }
 190                        memset(estart, 0, eend - ptr);
 191                        ptr = eend;
 192                        continue;
 193                }
 194
 195                if (ptr > map.m_la) {
 196                        mdev.m_pa += ptr - map.m_la;
 197                        map.m_la = ptr;
 198                }
 199
 200                ret = erofs_dev_read(mdev.m_deviceid, estart, mdev.m_pa,
 201                                     eend - map.m_la);
 202                if (ret < 0)
 203                        return -EIO;
 204                ptr = eend;
 205        }
 206        return 0;
 207}
 208
 209static int z_erofs_read_data(struct erofs_inode *inode, char *buffer,
 210                             erofs_off_t size, erofs_off_t offset)
 211{
 212        erofs_off_t end, length, skip;
 213        struct erofs_map_blocks map = {
 214                .index = UINT_MAX,
 215        };
 216        struct erofs_map_dev mdev;
 217        bool partial;
 218        unsigned int bufsize = 0;
 219        char *raw = NULL;
 220        int ret = 0;
 221
 222        end = offset + size;
 223        while (end > offset) {
 224                map.m_la = end - 1;
 225
 226                ret = z_erofs_map_blocks_iter(inode, &map, 0);
 227                if (ret)
 228                        break;
 229
 230                /* no device id here, thus it will always succeed */
 231                mdev = (struct erofs_map_dev) {
 232                        .m_pa = map.m_pa,
 233                };
 234                ret = erofs_map_dev(&sbi, &mdev);
 235                if (ret) {
 236                        DBG_BUGON(1);
 237                        break;
 238                }
 239
 240                /*
 241                 * trim to the needed size if the returned extent is quite
 242                 * larger than requested, and set up partial flag as well.
 243                 */
 244                if (end < map.m_la + map.m_llen) {
 245                        length = end - map.m_la;
 246                        partial = true;
 247                } else {
 248                        DBG_BUGON(end != map.m_la + map.m_llen);
 249                        length = map.m_llen;
 250                        partial = !(map.m_flags & EROFS_MAP_FULL_MAPPED);
 251                }
 252
 253                if (map.m_la < offset) {
 254                        skip = offset - map.m_la;
 255                        end = offset;
 256                } else {
 257                        skip = 0;
 258                        end = map.m_la;
 259                }
 260
 261                if (!(map.m_flags & EROFS_MAP_MAPPED)) {
 262                        memset(buffer + end - offset, 0, length);
 263                        end = map.m_la;
 264                        continue;
 265                }
 266
 267                if (map.m_plen > bufsize) {
 268                        bufsize = map.m_plen;
 269                        raw = realloc(raw, bufsize);
 270                        if (!raw) {
 271                                ret = -ENOMEM;
 272                                break;
 273                        }
 274                }
 275                ret = erofs_dev_read(mdev.m_deviceid, raw, mdev.m_pa, map.m_plen);
 276                if (ret < 0)
 277                        break;
 278
 279                ret = z_erofs_decompress(&(struct z_erofs_decompress_req) {
 280                                        .in = raw,
 281                                        .out = buffer + end - offset,
 282                                        .decodedskip = skip,
 283                                        .inputsize = map.m_plen,
 284                                        .decodedlength = length,
 285                                        .alg = map.m_algorithmformat,
 286                                        .partial_decoding = partial
 287                                         });
 288                if (ret < 0)
 289                        break;
 290        }
 291        if (raw)
 292                free(raw);
 293        return ret < 0 ? ret : 0;
 294}
 295
 296int erofs_pread(struct erofs_inode *inode, char *buf,
 297                erofs_off_t count, erofs_off_t offset)
 298{
 299        switch (inode->datalayout) {
 300        case EROFS_INODE_FLAT_PLAIN:
 301        case EROFS_INODE_FLAT_INLINE:
 302        case EROFS_INODE_CHUNK_BASED:
 303                return erofs_read_raw_data(inode, buf, count, offset);
 304        case EROFS_INODE_FLAT_COMPRESSION_LEGACY:
 305        case EROFS_INODE_FLAT_COMPRESSION:
 306                return z_erofs_read_data(inode, buf, count, offset);
 307        default:
 308                break;
 309        }
 310        return -EINVAL;
 311}
 312