Coverage Report

Created: 2026-03-11 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/u-boot/fs/erofs/namei.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0+
2
#include "internal.h"
3
4
0
#define makedev(major, minor) ((dev_t)((((major) & 0xfff) << 8) | ((minor) & 0xff)))
5
static dev_t erofs_new_decode_dev(u32 dev)
6
0
{
7
0
  const unsigned int major = (dev & 0xfff00) >> 8;
8
0
  const unsigned int minor = (dev & 0xff) | ((dev >> 12) & 0xfff00);
9
10
0
  return makedev(major, minor);
11
0
}
12
13
int erofs_read_inode_from_disk(struct erofs_inode *vi)
14
0
{
15
0
  int ret, ifmt;
16
0
  char buf[sizeof(struct erofs_inode_extended)];
17
0
  struct erofs_inode_compact *dic;
18
0
  struct erofs_inode_extended *die;
19
0
  const erofs_off_t inode_loc = iloc(vi->nid);
20
21
0
  ret = erofs_dev_read(0, buf, inode_loc, sizeof(*dic));
22
0
  if (ret < 0)
23
0
    return -EIO;
24
25
0
  dic = (struct erofs_inode_compact *)buf;
26
0
  ifmt = le16_to_cpu(dic->i_format);
27
28
0
  vi->datalayout = erofs_inode_datalayout(ifmt);
29
0
  if (vi->datalayout >= EROFS_INODE_DATALAYOUT_MAX) {
30
0
    erofs_err("unsupported datalayout %u of nid %llu",
31
0
        vi->datalayout, vi->nid | 0ULL);
32
0
    return -EOPNOTSUPP;
33
0
  }
34
0
  switch (erofs_inode_version(ifmt)) {
35
0
  case EROFS_INODE_LAYOUT_EXTENDED:
36
0
    vi->inode_isize = sizeof(struct erofs_inode_extended);
37
38
0
    ret = erofs_dev_read(0, buf + sizeof(*dic),
39
0
             inode_loc + sizeof(*dic),
40
0
             sizeof(*die) - sizeof(*dic));
41
0
    if (ret < 0)
42
0
      return -EIO;
43
44
0
    die = (struct erofs_inode_extended *)buf;
45
0
    vi->xattr_isize = erofs_xattr_ibody_size(die->i_xattr_icount);
46
0
    vi->i_mode = le16_to_cpu(die->i_mode);
47
48
0
    switch (vi->i_mode & S_IFMT) {
49
0
    case S_IFREG:
50
0
    case S_IFDIR:
51
0
    case S_IFLNK:
52
0
      vi->u.i_blkaddr = le32_to_cpu(die->i_u.raw_blkaddr);
53
0
      break;
54
0
    case S_IFCHR:
55
0
    case S_IFBLK:
56
0
      vi->u.i_rdev =
57
0
        erofs_new_decode_dev(le32_to_cpu(die->i_u.rdev));
58
0
      break;
59
0
    case S_IFIFO:
60
0
    case S_IFSOCK:
61
0
      vi->u.i_rdev = 0;
62
0
      break;
63
0
    default:
64
0
      goto bogusimode;
65
0
    }
66
67
0
    vi->i_uid = le32_to_cpu(die->i_uid);
68
0
    vi->i_gid = le32_to_cpu(die->i_gid);
69
0
    vi->i_nlink = le32_to_cpu(die->i_nlink);
70
71
0
    vi->i_mtime = le64_to_cpu(die->i_mtime);
72
0
    vi->i_mtime_nsec = le64_to_cpu(die->i_mtime_nsec);
73
0
    vi->i_size = le64_to_cpu(die->i_size);
74
0
    if (vi->datalayout == EROFS_INODE_CHUNK_BASED)
75
      /* fill chunked inode summary info */
76
0
      vi->u.chunkformat = le16_to_cpu(die->i_u.c.format);
77
0
    break;
78
0
  case EROFS_INODE_LAYOUT_COMPACT:
79
0
    vi->inode_isize = sizeof(struct erofs_inode_compact);
80
0
    vi->xattr_isize = erofs_xattr_ibody_size(dic->i_xattr_icount);
81
0
    vi->i_mode = le16_to_cpu(dic->i_mode);
82
83
0
    switch (vi->i_mode & S_IFMT) {
84
0
    case S_IFREG:
85
0
    case S_IFDIR:
86
0
    case S_IFLNK:
87
0
      vi->u.i_blkaddr = le32_to_cpu(dic->i_u.raw_blkaddr);
88
0
      break;
89
0
    case S_IFCHR:
90
0
    case S_IFBLK:
91
0
      vi->u.i_rdev =
92
0
        erofs_new_decode_dev(le32_to_cpu(dic->i_u.rdev));
93
0
      break;
94
0
    case S_IFIFO:
95
0
    case S_IFSOCK:
96
0
      vi->u.i_rdev = 0;
97
0
      break;
98
0
    default:
99
0
      goto bogusimode;
100
0
    }
101
102
0
    vi->i_uid = le16_to_cpu(dic->i_uid);
103
0
    vi->i_gid = le16_to_cpu(dic->i_gid);
104
0
    vi->i_nlink = le16_to_cpu(dic->i_nlink);
105
106
0
    vi->i_mtime = sbi.build_time;
107
0
    vi->i_mtime_nsec = sbi.build_time_nsec;
108
109
0
    vi->i_size = le32_to_cpu(dic->i_size);
110
0
    if (vi->datalayout == EROFS_INODE_CHUNK_BASED)
111
0
      vi->u.chunkformat = le16_to_cpu(dic->i_u.c.format);
112
0
    break;
113
0
  default:
114
0
    erofs_err("unsupported on-disk inode version %u of nid %llu",
115
0
        erofs_inode_version(ifmt), vi->nid | 0ULL);
116
0
    return -EOPNOTSUPP;
117
0
  }
118
119
0
  vi->flags = 0;
120
0
  if (vi->datalayout == EROFS_INODE_CHUNK_BASED) {
121
0
    if (vi->u.chunkformat & ~EROFS_CHUNK_FORMAT_ALL) {
122
0
      erofs_err("unsupported chunk format %x of nid %llu",
123
0
          vi->u.chunkformat, vi->nid | 0ULL);
124
0
      return -EOPNOTSUPP;
125
0
    }
126
0
    vi->u.chunkbits = sbi.blkszbits +
127
0
      (vi->u.chunkformat & EROFS_CHUNK_FORMAT_BLKBITS_MASK);
128
0
  } else if (erofs_inode_is_data_compressed(vi->datalayout)) {
129
0
    if (erofs_blksiz() != EROFS_MAX_BLOCK_SIZE)
130
0
      return -EOPNOTSUPP;
131
0
    return z_erofs_fill_inode(vi);
132
0
  }
133
0
  return 0;
134
0
bogusimode:
135
0
  erofs_err("bogus i_mode (%o) @ nid %llu", vi->i_mode, vi->nid | 0ULL);
136
0
  return -EFSCORRUPTED;
137
0
}
138
139
struct erofs_dirent *find_target_dirent(erofs_nid_t pnid,
140
          void *dentry_blk,
141
          const char *name, unsigned int len,
142
          unsigned int nameoff,
143
          unsigned int maxsize)
144
0
{
145
0
  struct erofs_dirent *de = dentry_blk;
146
0
  const struct erofs_dirent *end = dentry_blk + nameoff;
147
148
0
  while (de < end) {
149
0
    const char *de_name;
150
0
    unsigned int de_namelen;
151
152
0
    nameoff = le16_to_cpu(de->nameoff);
153
0
    de_name = (char *)dentry_blk + nameoff;
154
155
    /* the last dirent in the block? */
156
0
    if (de + 1 >= end)
157
0
      de_namelen = strnlen(de_name, maxsize - nameoff);
158
0
    else
159
0
      de_namelen = le16_to_cpu(de[1].nameoff) - nameoff;
160
161
    /* a corrupted entry is found */
162
0
    if (nameoff + de_namelen > maxsize ||
163
0
        de_namelen > EROFS_NAME_LEN) {
164
0
      erofs_err("bogus dirent @ nid %llu", pnid | 0ULL);
165
0
      DBG_BUGON(1);
166
0
      return ERR_PTR(-EFSCORRUPTED);
167
0
    }
168
169
0
    if (len == de_namelen && !memcmp(de_name, name, de_namelen))
170
0
      return de;
171
0
    ++de;
172
0
  }
173
0
  return NULL;
174
0
}
175
176
struct nameidata {
177
  erofs_nid_t nid;
178
  unsigned int  ftype;
179
};
180
181
int erofs_namei(struct nameidata *nd, const char *name, unsigned int len)
182
0
{
183
0
  erofs_nid_t nid = nd->nid;
184
0
  int ret;
185
0
  char buf[EROFS_MAX_BLOCK_SIZE];
186
0
  struct erofs_inode vi = { .nid = nid };
187
0
  erofs_off_t offset;
188
189
0
  ret = erofs_read_inode_from_disk(&vi);
190
0
  if (ret)
191
0
    return ret;
192
193
0
  offset = 0;
194
0
  while (offset < vi.i_size) {
195
0
    erofs_off_t maxsize = min_t(erofs_off_t,
196
0
              vi.i_size - offset, erofs_blksiz());
197
0
    struct erofs_dirent *de = (void *)buf;
198
0
    unsigned int nameoff;
199
200
0
    ret = erofs_pread(&vi, buf, maxsize, offset);
201
0
    if (ret)
202
0
      return ret;
203
204
0
    nameoff = le16_to_cpu(de->nameoff);
205
0
    if (nameoff < sizeof(struct erofs_dirent) ||
206
0
        nameoff >= erofs_blksiz()) {
207
0
      erofs_err("invalid de[0].nameoff %u @ nid %llu",
208
0
          nameoff, nid | 0ULL);
209
0
      return -EFSCORRUPTED;
210
0
    }
211
212
0
    de = find_target_dirent(nid, buf, name, len,
213
0
          nameoff, maxsize);
214
0
    if (IS_ERR(de))
215
0
      return PTR_ERR(de);
216
217
0
    if (de) {
218
0
      nd->nid = le64_to_cpu(de->nid);
219
0
      return 0;
220
0
    }
221
0
    offset += maxsize;
222
0
  }
223
0
  return -ENOENT;
224
0
}
225
226
static int link_path_walk(const char *name, struct nameidata *nd)
227
0
{
228
0
  nd->nid = sbi.root_nid;
229
230
0
  while (*name == '/')
231
0
    name++;
232
233
  /* At this point we know we have a real path component. */
234
0
  while (*name != '\0') {
235
0
    const char *p = name;
236
0
    int ret;
237
238
0
    do {
239
0
      ++p;
240
0
    } while (*p != '\0' && *p != '/');
241
242
0
    DBG_BUGON(p <= name);
243
0
    ret = erofs_namei(nd, name, p - name);
244
0
    if (ret)
245
0
      return ret;
246
247
0
    name = p;
248
    /* Skip until no more slashes. */
249
0
    for (name = p; *name == '/'; ++name)
250
0
      ;
251
0
  }
252
0
  return 0;
253
0
}
254
255
int erofs_ilookup(const char *path, struct erofs_inode *vi)
256
0
{
257
0
  int ret;
258
0
  struct nameidata nd;
259
260
0
  ret = link_path_walk(path, &nd);
261
0
  if (ret)
262
0
    return ret;
263
264
0
  vi->nid = nd.nid;
265
0
  return erofs_read_inode_from_disk(vi);
266
0
}