Coverage Report

Created: 2026-10-13 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/barebox/fs/fat/fat.c
Line
Count
Source
1
/*
2
 * fat.c - FAT filesystem barebox driver
3
 *
4
 * Copyright (c) 2011 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
5
 *
6
 * This program is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License version 2
8
 * as published by the Free Software Foundation.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 */
16
17
#include <common.h>
18
#include <driver.h>
19
#include <init.h>
20
#include <malloc.h>
21
#include <fs.h>
22
#include <command.h>
23
#include <errno.h>
24
#include <linux/stat.h>
25
#include <linux/ctype.h>
26
#include <xfuncs.h>
27
#include <fcntl.h>
28
#include <block.h>
29
#include <linux/sizes.h>
30
#include <fuzz.h>
31
#include "ff.h"
32
#include "integer.h"
33
#include "diskio.h"
34
35
struct fat_priv {
36
  struct cdev *cdev;
37
  FATFS fat;
38
};
39
40
/* ---------------------------------------------------------------*/
41
42
unsigned int disk_sector_size(FATFS *fat)
43
12.5M
{
44
12.5M
  struct fat_priv *priv = fat->userdata;
45
46
12.5M
  return cdev_blocksize(priv->cdev);
47
12.5M
}
48
49
DRESULT disk_read(FATFS *fat, BYTE *buf, DWORD sector, BYTE count)
50
12.5M
{
51
12.5M
  struct fat_priv *priv = fat->userdata;
52
12.5M
  unsigned int sector_size = disk_sector_size(fat);
53
12.5M
  size_t len = count * sector_size;
54
12.5M
  int ret;
55
56
12.5M
  debug("%s: sector: %u count: %d\n", __func__, sector, count);
57
58
12.5M
  ret = cdev_read(priv->cdev, buf, len, (loff_t)sector * sector_size, 0);
59
12.5M
  if (ret != len)
60
3.47k
    return RES_ERROR;
61
62
12.5M
  return RES_OK;
63
12.5M
}
64
65
DRESULT disk_write(FATFS *fat, const BYTE *buf, DWORD sector, BYTE count)
66
0
{
67
0
  struct fat_priv *priv = fat->userdata;
68
0
  unsigned int sector_size = disk_sector_size(fat);
69
0
  size_t len = count * sector_size;
70
0
  int ret;
71
72
0
  debug("%s: buf: %p sector: %u count: %d\n",
73
0
      __func__, buf, sector, count);
74
75
0
  ret = cdev_write(priv->cdev, buf, len, (loff_t)sector * sector_size, 0);
76
0
  if (ret != len)
77
0
    return RES_ERROR;
78
79
0
  return RES_OK;
80
0
}
81
82
DRESULT disk_flush(FATFS *fat)
83
0
{
84
0
  struct fat_priv *priv = fat->userdata;
85
86
0
  return cdev_flush(priv->cdev) ? RES_ERROR : RES_OK;
87
0
}
88
89
/* ---------------------------------------------------------------*/
90
91
#ifdef CONFIG_FS_FAT_WRITE
92
static int fat_create(struct device *dev, const char *pathname, mode_t mode)
93
0
{
94
0
  struct fat_priv *priv = dev->priv;
95
0
  FIL f_file;
96
0
  int ret;
97
98
0
  ret = f_open(&priv->fat, &f_file, pathname, FA_OPEN_ALWAYS);
99
0
  if (ret)
100
0
    return ret;
101
102
0
  return f_close(&f_file);
103
0
}
104
105
static int fat_unlink(struct device *dev, const char *pathname)
106
0
{
107
0
  struct fat_priv *priv = dev->priv;
108
109
0
  return f_unlink(&priv->fat, pathname);
110
0
}
111
112
static int fat_mkdir(struct device *dev, const char *pathname)
113
0
{
114
0
  struct fat_priv *priv = dev->priv;
115
116
0
  return f_mkdir(&priv->fat, pathname);
117
0
}
118
119
static int fat_rmdir(struct device *dev, const char *pathname)
120
0
{
121
0
  struct fat_priv *priv = dev->priv;
122
123
0
  return f_unlink(&priv->fat, pathname);
124
0
}
125
126
static int fat_write(struct file *f, const void *buf, size_t insize)
127
0
{
128
0
  FIL *f_file = f->private_data;
129
0
  int outsize;
130
0
  int ret;
131
132
0
  ret = f_write(f_file, buf, insize, &outsize);
133
134
0
  debug("%s: %d %zd %d %p\n", __func__, ret, insize, outsize, f_file);
135
136
0
  if (ret)
137
0
    return ret;
138
0
  if (!outsize)
139
0
    return -ENOSPC;
140
141
0
  return outsize;
142
0
}
143
144
static int fat_truncate(struct file *f, loff_t size)
145
0
{
146
0
  FIL *f_file = f->private_data;
147
0
  unsigned long lastofs;
148
0
  int ret;
149
150
0
  lastofs = f_file->fptr;
151
152
0
  ret = f_lseek(f_file, size);
153
0
  if (ret)
154
0
    return ret;
155
156
0
  ret = f_truncate(f_file);
157
0
  if (ret)
158
0
    return ret;
159
160
0
  ret = f_lseek(f_file, lastofs);
161
0
  if (ret)
162
0
    return ret;
163
164
0
  return 0;
165
0
}
166
#endif /* CONFIG_FS_FAT_WRITE */
167
168
static int fat_open(struct device *dev, struct file *file, const char *filename)
169
0
{
170
0
  struct fat_priv *priv = dev->priv;
171
0
  FIL *f_file;
172
0
  int ret;
173
0
  unsigned long flags = 0;
174
175
0
  f_file = xzalloc(sizeof(*f_file));
176
177
0
  switch (file->f_flags & O_ACCMODE) {
178
0
  case O_RDONLY:
179
0
    flags = FA_READ;
180
0
    break;
181
0
  case O_WRONLY:
182
0
    flags = FA_WRITE;
183
0
    break;
184
0
  case O_RDWR:
185
0
    flags = FA_READ | FA_WRITE;
186
0
    break;
187
0
  }
188
189
0
  ret = f_open(&priv->fat, f_file, filename, flags);
190
0
  if (ret) {
191
0
    free(f_file);
192
0
    return ret;
193
0
  }
194
195
0
  if (file->f_flags & O_APPEND) {
196
0
    ret = f_lseek(f_file, f_file->fsize);
197
0
    if (ret) {
198
0
      f_close(f_file);
199
0
      free(f_file);
200
0
      return ret;
201
0
    }
202
0
  }
203
204
0
  file->private_data = f_file;
205
0
  file->f_size = f_file->fsize;
206
207
0
  return 0;
208
0
}
209
210
static int fat_close(struct device *dev, struct file *f)
211
0
{
212
0
  FIL *f_file = f->private_data;
213
0
  int ret;
214
215
0
  ret = f_close(f_file);
216
217
0
  free(f_file);
218
219
0
  return ret;
220
0
}
221
222
static int fat_read(struct file *f, void *buf, size_t insize)
223
0
{
224
0
  int ret;
225
0
  FIL *f_file = f->private_data;
226
0
  int outsize;
227
228
0
  ret = f_read(f_file, buf, insize, &outsize);
229
230
0
  debug("%s: %d %zd %d %p\n", __func__, ret, insize, outsize, f_file);
231
232
0
  if (ret)
233
0
    return ret;
234
235
0
  return outsize;
236
0
}
237
238
static int fat_lseek(struct file *f, loff_t pos)
239
0
{
240
0
  FIL *f_file = f->private_data;
241
0
  int ret;
242
243
0
  ret = f_lseek(f_file, pos);
244
0
  if (ret)
245
0
    return ret;
246
247
0
  return 0;
248
0
}
249
250
static DIR* fat_opendir(struct device *dev, const char *pathname)
251
0
{
252
0
  struct fat_priv *priv = dev->priv;
253
0
  DIR *dir;
254
0
  FF_DIR *ff_dir;
255
0
  int ret;
256
257
0
  debug("%s: %s\n", __func__, pathname);
258
259
0
  ff_dir = xzalloc(sizeof(*ff_dir));
260
0
  if (pathname)
261
0
    ret = f_opendir(&priv->fat, ff_dir, pathname);
262
0
  else
263
0
    ret = f_opendir(&priv->fat, ff_dir, "/");
264
265
0
  if (ret)
266
0
    return NULL;
267
268
0
  dir = xzalloc(sizeof(*dir));
269
270
0
  dir->priv = ff_dir;
271
272
0
  return dir;
273
0
}
274
275
static struct dirent* fat_readdir(struct device *dev, DIR *dir)
276
0
{
277
0
  FF_DIR *ff_dir = dir->priv;
278
0
  FILINFO finfo;
279
0
  int ret;
280
0
#ifdef CONFIG_FS_FAT_LFN
281
0
  char name[PATH_MAX];
282
0
#endif
283
0
  memset(&finfo, 0, sizeof(finfo));
284
0
#ifdef CONFIG_FS_FAT_LFN
285
0
  finfo.lfname = name;
286
0
  finfo.lfsize = PATH_MAX;
287
0
#endif
288
0
  ret = f_readdir(ff_dir, &finfo);
289
0
  if (ret)
290
0
    return NULL;
291
292
0
  if (finfo.fname[0] == '\0')
293
0
    return NULL;
294
295
0
#ifdef CONFIG_FS_FAT_LFN
296
0
  if (*finfo.lfname)
297
0
    strcpy(dir->d.d_name, finfo.lfname);
298
0
  else
299
0
#endif
300
0
    strcpy(dir->d.d_name, finfo.fname);
301
302
0
  return &dir->d;
303
0
}
304
305
static int fat_closedir(struct device *dev, DIR *dir)
306
0
{
307
0
  FF_DIR *ff_dir = dir->priv;
308
309
0
  free(ff_dir);
310
0
  free(dir);
311
312
0
  return 0;
313
0
}
314
315
static int fat_stat(struct device *dev, const char *filename, struct stat *s)
316
0
{
317
0
  struct fat_priv *priv = dev->priv;
318
0
  FILINFO finfo;
319
0
  int ret;
320
321
0
  memset(&finfo, 0, sizeof(FILINFO));
322
323
0
  ret = f_stat(&priv->fat, filename, &finfo);
324
0
  if (ret)
325
0
    return ret;
326
327
0
  s->st_size = finfo.fsize;
328
0
  s->st_mode = S_IRWXU | S_IRWXG | S_IRWXO;
329
330
0
  if (finfo.fattrib & AM_DIR)
331
0
    s->st_mode |= S_IFDIR;
332
0
  else
333
0
    s->st_mode |= S_IFREG;
334
335
0
  return 0;
336
0
}
337
338
static int fat_probe(struct device *dev)
339
0
{
340
0
  struct fs_device *fsdev = dev_to_fs_device(dev);
341
0
  struct fat_priv *priv;
342
0
  unsigned int blocksize;
343
0
  int ret;
344
345
0
  ret = fsdev_open_cdev(fsdev);
346
0
  if (ret)
347
0
    return ret;
348
349
  /*
350
   * No need to cleanup fsdev_open_cdev(), only way to invoke this
351
   * probe function is via mount() and that will already take care
352
   * of calling fs_remove for us.
353
   */
354
0
  blocksize = cdev_blocksize(fsdev->cdev);
355
0
  if (blocksize > SZ_4K) {
356
0
    dev_err(dev, "FAT on %u-byte block devices is unsupported\n",
357
0
      blocksize);
358
0
    return -ENOSYS;
359
0
  }
360
361
0
  priv = dev->priv = xzalloc(sizeof(struct fat_priv));
362
363
0
  priv->cdev = fsdev->cdev;
364
0
  fsdev->sb.s_casefold = true;
365
366
0
  priv->fat.userdata = priv;
367
0
  ret = f_mount(&priv->fat);
368
0
  if (ret)
369
0
    goto err_mount;
370
371
0
  return 0;
372
373
0
err_mount:
374
0
  free(priv);
375
376
0
  return ret;
377
0
}
378
379
static void fat_remove(struct device *dev)
380
0
{
381
0
  free(dev->priv);
382
0
}
383
384
static int fuzz_fat(struct block_device *ramdisk)
385
1.19k
{
386
1.19k
  FATFS fat = {};
387
1.19k
  struct fat_priv priv = {};
388
1.19k
  FF_DIR dir;
389
1.19k
  FILINFO finfo;
390
1.19k
  FIL fil;
391
1.19k
  BYTE buf[512];
392
1.19k
  UINT br;
393
1.19k
  int ret, i;
394
1.19k
  char lfname[256];
395
396
1.19k
  priv.cdev = &ramdisk->cdev;
397
1.19k
  fat.userdata = &priv;
398
399
1.19k
  ret = f_mount(&fat);
400
1.19k
  if (ret)
401
260
    return 0;
402
403
930
  ret = f_opendir(&fat, &dir, "/");
404
930
  if (ret)
405
15
    return 0;
406
407
  /*
408
   * Bound the number of entries and the number of reads per file:
409
   * a crafted image can otherwise describe an arbitrarily long
410
   * directory or a cyclic cluster chain.
411
   */
412
12.7k
  for (i = 0; i < 64; i++) {
413
12.6k
    memset(&finfo, 0, sizeof(finfo));
414
415
12.6k
    if (IS_ENABLED(CONFIG_FS_FAT_LFN)) {
416
12.6k
      finfo.lfname = lfname;
417
12.6k
      finfo.lfsize = sizeof(lfname);
418
12.6k
    }
419
420
12.6k
    ret = f_readdir(&dir, &finfo);
421
12.6k
    if (ret || finfo.fname[0] == '\0')
422
834
      break;
423
424
11.7k
    if (!(finfo.fattrib & AM_DIR)) {
425
9.90k
      char path[14];
426
427
9.90k
      snprintf(path, sizeof(path), "/%s", finfo.fname);
428
9.90k
      ret = f_open(&fat, &fil, path, FA_READ);
429
9.90k
      if (ret == 0) {
430
2.05k
        int reads = 0;
431
432
28.5k
        do {
433
28.5k
          ret = f_read(&fil, buf, sizeof(buf),
434
28.5k
                 &br);
435
28.5k
        } while (!ret && br == sizeof(buf) &&
436
26.5k
           ++reads < 1024);
437
2.05k
        f_close(&fil);
438
2.05k
      }
439
9.90k
    }
440
11.7k
  }
441
442
915
  return 0;
443
930
}
444
fuzz_test_ramdisk("fat", fuzz_fat, 512, NULL);
445
446
static const struct fs_legacy_ops fat_ops = {
447
  .open      = fat_open,
448
  .close     = fat_close,
449
  .opendir   = fat_opendir,
450
  .readdir   = fat_readdir,
451
  .closedir  = fat_closedir,
452
  .stat      = fat_stat,
453
#ifdef CONFIG_FS_FAT_WRITE
454
  .create    = fat_create,
455
  .unlink    = fat_unlink,
456
  .mkdir     = fat_mkdir,
457
  .rmdir     = fat_rmdir,
458
  .write     = fat_write,
459
  .truncate  = fat_truncate,
460
#endif
461
  .read      = fat_read,
462
  .lseek     = fat_lseek,
463
};
464
465
static struct fs_driver fat_driver = {
466
  .legacy_ops = &fat_ops,
467
  .type = filetype_fat,
468
  .drv = {
469
    .probe  = fat_probe,
470
    .remove = fat_remove,
471
    .name = "fat",
472
  }
473
};
474
475
static int fat_init(void)
476
26
{
477
26
  return register_fs_driver(&fat_driver);
478
26
}
479
480
coredevice_initcall(fat_init);