blob: c56f56759875ec426a5abdf8c5d4d3d1b2805ece [file] [log] [blame]
Marek Vasut9ad82a72025-03-17 04:12:45 +01001/*
2 io.c (02.09.09)
3 exFAT file system implementation library.
4
5 Free exFAT implementation.
6 Copyright (C) 2010-2023 Andrew Nayenko
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License along
19 with this program; if not, write to the Free Software Foundation, Inc.,
20 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21*/
22
23#include "exfat.h"
24#include <inttypes.h>
Marek Vasut894cf722025-03-17 04:12:46 +010025#ifndef __UBOOT__
Marek Vasut9ad82a72025-03-17 04:12:45 +010026#include <sys/types.h>
27#include <sys/stat.h>
28#include <fcntl.h>
29#include <unistd.h>
30#include <string.h>
31#include <errno.h>
32#if defined(__APPLE__)
33#include <sys/disk.h>
34#elif defined(__OpenBSD__)
35#include <sys/param.h>
36#include <sys/disklabel.h>
37#include <sys/dkio.h>
38#include <sys/ioctl.h>
39#elif defined(__NetBSD__)
40#include <sys/ioctl.h>
41#elif __linux__
42#include <sys/mount.h>
43#endif
44#ifdef USE_UBLIO
45#include <sys/uio.h>
46#include <ublio.h>
47#endif
Marek Vasut894cf722025-03-17 04:12:46 +010048#else
49#include <fs.h>
50#include <fs_internal.h>
51
52static struct exfat_ctxt {
53 struct disk_partition cur_part_info;
54 struct blk_desc *cur_dev;
55 struct exfat ef;
56} ctxt;
57#endif
Marek Vasut9ad82a72025-03-17 04:12:45 +010058
59struct exfat_dev
60{
61 int fd;
62 enum exfat_mode mode;
63 off_t size; /* in bytes */
64#ifdef USE_UBLIO
65 off_t pos;
66 ublio_filehandle_t ufh;
67#endif
Marek Vasut894cf722025-03-17 04:12:46 +010068#ifdef __UBOOT__
69 struct exfat_ctxt *ctxt;
70#endif
Marek Vasut9ad82a72025-03-17 04:12:45 +010071};
72
Marek Vasut894cf722025-03-17 04:12:46 +010073#ifndef __UBOOT__
Marek Vasut9ad82a72025-03-17 04:12:45 +010074static bool is_open(int fd)
75{
76 return fcntl(fd, F_GETFD) != -1;
77}
78
79static int open_ro(const char* spec)
80{
81 return open(spec, O_RDONLY);
82}
83
84static int open_rw(const char* spec)
85{
86 int fd = open(spec, O_RDWR);
87#ifdef __linux__
88 int ro = 0;
89
90 /*
91 This ioctl is needed because after "blockdev --setro" kernel still
92 allows to open the device in read-write mode but fails writes.
93 */
94 if (fd != -1 && ioctl(fd, BLKROGET, &ro) == 0 && ro)
95 {
96 close(fd);
97 errno = EROFS;
98 return -1;
99 }
100#endif
101 return fd;
102}
103
104struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode)
105{
106 struct exfat_dev* dev;
107 struct stat stbuf;
108#ifdef USE_UBLIO
109 struct ublio_param up;
110#endif
111
112 /* The system allocates file descriptors sequentially. If we have been
113 started with stdin (0), stdout (1) or stderr (2) closed, the system
114 will give us descriptor 0, 1 or 2 later when we open block device,
115 FUSE communication pipe, etc. As a result, functions using stdin,
116 stdout or stderr will actually work with a different thing and can
117 corrupt it. Protect descriptors 0, 1 and 2 from such misuse. */
118 while (!is_open(STDIN_FILENO)
119 || !is_open(STDOUT_FILENO)
120 || !is_open(STDERR_FILENO))
121 {
122 /* we don't need those descriptors, let them leak */
123 if (open("/dev/null", O_RDWR) == -1)
124 {
125 exfat_error("failed to open /dev/null");
126 return NULL;
127 }
128 }
129
130 dev = malloc(sizeof(struct exfat_dev));
131 if (dev == NULL)
132 {
133 exfat_error("failed to allocate memory for device structure");
134 return NULL;
135 }
136
137 switch (mode)
138 {
139 case EXFAT_MODE_RO:
140 dev->fd = open_ro(spec);
141 if (dev->fd == -1)
142 {
143 free(dev);
144 exfat_error("failed to open '%s' in read-only mode: %s", spec,
145 strerror(errno));
146 return NULL;
147 }
148 dev->mode = EXFAT_MODE_RO;
149 break;
150 case EXFAT_MODE_RW:
151 dev->fd = open_rw(spec);
152 if (dev->fd == -1)
153 {
154 free(dev);
155 exfat_error("failed to open '%s' in read-write mode: %s", spec,
156 strerror(errno));
157 return NULL;
158 }
159 dev->mode = EXFAT_MODE_RW;
160 break;
161 case EXFAT_MODE_ANY:
162 dev->fd = open_rw(spec);
163 if (dev->fd != -1)
164 {
165 dev->mode = EXFAT_MODE_RW;
166 break;
167 }
168 dev->fd = open_ro(spec);
169 if (dev->fd != -1)
170 {
171 dev->mode = EXFAT_MODE_RO;
172 exfat_warn("'%s' is write-protected, mounting read-only", spec);
173 break;
174 }
175 free(dev);
176 exfat_error("failed to open '%s': %s", spec, strerror(errno));
177 return NULL;
178 }
179
180 if (fstat(dev->fd, &stbuf) != 0)
181 {
182 close(dev->fd);
183 free(dev);
184 exfat_error("failed to fstat '%s'", spec);
185 return NULL;
186 }
187 if (!S_ISBLK(stbuf.st_mode) &&
188 !S_ISCHR(stbuf.st_mode) &&
189 !S_ISREG(stbuf.st_mode))
190 {
191 close(dev->fd);
192 free(dev);
193 exfat_error("'%s' is neither a device, nor a regular file", spec);
194 return NULL;
195 }
196
197#if defined(__APPLE__)
198 if (!S_ISREG(stbuf.st_mode))
199 {
200 uint32_t block_size = 0;
201 uint64_t blocks = 0;
202
203 if (ioctl(dev->fd, DKIOCGETBLOCKSIZE, &block_size) != 0)
204 {
205 close(dev->fd);
206 free(dev);
207 exfat_error("failed to get block size");
208 return NULL;
209 }
210 if (ioctl(dev->fd, DKIOCGETBLOCKCOUNT, &blocks) != 0)
211 {
212 close(dev->fd);
213 free(dev);
214 exfat_error("failed to get blocks count");
215 return NULL;
216 }
217 dev->size = blocks * block_size;
218 }
219 else
220#elif defined(__OpenBSD__)
221 if (!S_ISREG(stbuf.st_mode))
222 {
223 struct disklabel lab;
224 struct partition* pp;
225 char* partition;
226
227 if (ioctl(dev->fd, DIOCGDINFO, &lab) == -1)
228 {
229 close(dev->fd);
230 free(dev);
231 exfat_error("failed to get disklabel");
232 return NULL;
233 }
234
235 /* Don't need to check that partition letter is valid as we won't get
236 this far otherwise. */
237 partition = strchr(spec, '\0') - 1;
238 pp = &(lab.d_partitions[*partition - 'a']);
239 dev->size = DL_GETPSIZE(pp) * lab.d_secsize;
240
241 if (pp->p_fstype != FS_NTFS)
242 exfat_warn("partition type is not 0x07 (NTFS/exFAT); "
243 "you can fix this with fdisk(8)");
244 }
245 else
246#elif defined(__NetBSD__)
247 if (!S_ISREG(stbuf.st_mode))
248 {
249 off_t size;
250
251 if (ioctl(dev->fd, DIOCGMEDIASIZE, &size) == -1)
252 {
253 close(dev->fd);
254 free(dev);
255 exfat_error("failed to get media size");
256 return NULL;
257 }
258 dev->size = size;
259 }
260 else
261#endif
262 {
263 /* works for Linux, FreeBSD, Solaris */
264 dev->size = exfat_seek(dev, 0, SEEK_END);
265 if (dev->size <= 0)
266 {
267 close(dev->fd);
268 free(dev);
269 exfat_error("failed to get size of '%s'", spec);
270 return NULL;
271 }
272 if (exfat_seek(dev, 0, SEEK_SET) == -1)
273 {
274 close(dev->fd);
275 free(dev);
276 exfat_error("failed to seek to the beginning of '%s'", spec);
277 return NULL;
278 }
279 }
280
281#ifdef USE_UBLIO
282 memset(&up, 0, sizeof(struct ublio_param));
283 up.up_blocksize = 256 * 1024;
284 up.up_items = 64;
285 up.up_grace = 32;
286 up.up_priv = &dev->fd;
287
288 dev->pos = 0;
289 dev->ufh = ublio_open(&up);
290 if (dev->ufh == NULL)
291 {
292 close(dev->fd);
293 free(dev);
294 exfat_error("failed to initialize ublio");
295 return NULL;
296 }
297#endif
298
299 return dev;
300}
301
302int exfat_close(struct exfat_dev* dev)
303{
304 int rc = 0;
305
306#ifdef USE_UBLIO
307 if (ublio_close(dev->ufh) != 0)
308 {
309 exfat_error("failed to close ublio");
310 rc = -EIO;
311 }
312#endif
313 if (close(dev->fd) != 0)
314 {
315 exfat_error("failed to close device: %s", strerror(errno));
316 rc = -EIO;
317 }
318 free(dev);
319 return rc;
320}
321
322int exfat_fsync(struct exfat_dev* dev)
323{
324 int rc = 0;
325
326#ifdef USE_UBLIO
327 if (ublio_fsync(dev->ufh) != 0)
328 {
329 exfat_error("ublio fsync failed");
330 rc = -EIO;
331 }
332#endif
333 if (fsync(dev->fd) != 0)
334 {
335 exfat_error("fsync failed: %s", strerror(errno));
336 rc = -EIO;
337 }
338 return rc;
339}
340
341enum exfat_mode exfat_get_mode(const struct exfat_dev* dev)
342{
343 return dev->mode;
344}
345
346off_t exfat_get_size(const struct exfat_dev* dev)
347{
348 return dev->size;
349}
350
351off_t exfat_seek(struct exfat_dev* dev, off_t offset, int whence)
352{
353#ifdef USE_UBLIO
354 /* XXX SEEK_CUR will be handled incorrectly */
355 return dev->pos = lseek(dev->fd, offset, whence);
356#else
357 return lseek(dev->fd, offset, whence);
358#endif
359}
360
361ssize_t exfat_read(struct exfat_dev* dev, void* buffer, size_t size)
362{
363#ifdef USE_UBLIO
364 ssize_t result = ublio_pread(dev->ufh, buffer, size, dev->pos);
365 if (result >= 0)
366 dev->pos += size;
367 return result;
368#else
369 return read(dev->fd, buffer, size);
370#endif
371}
372
373ssize_t exfat_write(struct exfat_dev* dev, const void* buffer, size_t size)
374{
375#ifdef USE_UBLIO
376 ssize_t result = ublio_pwrite(dev->ufh, (void*) buffer, size, dev->pos);
377 if (result >= 0)
378 dev->pos += size;
379 return result;
380#else
381 return write(dev->fd, buffer, size);
382#endif
383}
384
385ssize_t exfat_pread(struct exfat_dev* dev, void* buffer, size_t size,
386 off_t offset)
387{
388#ifdef USE_UBLIO
389 return ublio_pread(dev->ufh, buffer, size, offset);
390#else
391 return pread(dev->fd, buffer, size, offset);
392#endif
393}
394
395ssize_t exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size,
396 off_t offset)
397{
398#ifdef USE_UBLIO
399 return ublio_pwrite(dev->ufh, (void*) buffer, size, offset);
400#else
401 return pwrite(dev->fd, buffer, size, offset);
402#endif
403}
Marek Vasut894cf722025-03-17 04:12:46 +0100404#else /* U-Boot */
405struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode)
406{
407 struct exfat_dev* dev;
408
409 dev = malloc(sizeof(struct exfat_dev));
410 if (!dev) {
411 exfat_error("failed to allocate memory for device structure");
412 return NULL;
413 }
414 dev->mode = EXFAT_MODE_RW;
415 dev->size = ctxt.cur_part_info.size * ctxt.cur_part_info.blksz;
416 dev->ctxt = &ctxt;
417
418 return dev;
419}
420
421int exfat_close(struct exfat_dev* dev)
422{
423 free(dev);
424 return 0;
425}
426
427int exfat_fsync(struct exfat_dev* dev)
428{
429 return 0;
430}
431
432enum exfat_mode exfat_get_mode(const struct exfat_dev* dev)
433{
434 return dev->mode;
435}
436
437off_t exfat_get_size(const struct exfat_dev* dev)
438{
439 return dev->size;
440}
441
442ssize_t exfat_pread(struct exfat_dev* dev, void* buffer, size_t size,
443 off_t offset)
444{
445 lbaint_t sect = offset >> ctxt.cur_dev->log2blksz;
446 int off = offset & (ctxt.cur_dev->blksz - 1);
447
448 if (!ctxt.cur_dev)
449 return -EIO;
450
451 if (fs_devread(ctxt.cur_dev, &ctxt.cur_part_info, sect,
452 off, size, buffer))
453 return 0;
454 return -EIO;
455}
456
457ssize_t exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size,
458 off_t offset)
459{
460 lbaint_t sect = offset >> ctxt.cur_dev->log2blksz;
461 int off = offset & (ctxt.cur_dev->blksz - 1);
462
463 if (!ctxt.cur_dev)
464 return -EIO;
465
466 if (fs_devwrite(ctxt.cur_dev, &ctxt.cur_part_info, sect,
467 off, size, buffer))
468 return 0;
469 return -EIO;
470}
471#endif
Marek Vasut9ad82a72025-03-17 04:12:45 +0100472
473ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node,
474 void* buffer, size_t size, off_t offset)
475{
476 uint64_t uoffset = offset;
477 cluster_t cluster;
478 char* bufp = buffer;
479 off_t lsize, loffset, remainder;
480
481 if (offset < 0)
482 return -EINVAL;
483 if (uoffset >= node->size)
484 return 0;
485 if (size == 0)
486 return 0;
487
488 if (uoffset + size > node->valid_size)
489 {
490 ssize_t bytes = 0;
491
492 if (uoffset < node->valid_size)
493 {
494 bytes = exfat_generic_pread(ef, node, buffer,
495 node->valid_size - uoffset, offset);
496 if (bytes < 0 || (size_t) bytes < node->valid_size - uoffset)
497 return bytes;
498 }
499 memset(buffer + bytes, 0,
500 MIN(size - bytes, node->size - node->valid_size));
501 return MIN(size, node->size - uoffset);
502 }
503
504 cluster = exfat_advance_cluster(ef, node, uoffset / CLUSTER_SIZE(*ef->sb));
505 if (CLUSTER_INVALID(*ef->sb, cluster))
506 {
507 exfat_error("invalid cluster 0x%x while reading", cluster);
508 return -EIO;
509 }
510
511 loffset = uoffset % CLUSTER_SIZE(*ef->sb);
512 remainder = MIN(size, node->size - uoffset);
513 while (remainder > 0)
514 {
515 if (CLUSTER_INVALID(*ef->sb, cluster))
516 {
517 exfat_error("invalid cluster 0x%x while reading", cluster);
518 return -EIO;
519 }
520 lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
521 if (exfat_pread(ef->dev, bufp, lsize,
522 exfat_c2o(ef, cluster) + loffset) < 0)
523 {
524 exfat_error("failed to read cluster %#x", cluster);
525 return -EIO;
526 }
527 bufp += lsize;
528 loffset = 0;
529 remainder -= lsize;
530 cluster = exfat_next_cluster(ef, node, cluster);
531 }
532 if (!(node->attrib & EXFAT_ATTRIB_DIR) && !ef->ro && !ef->noatime)
533 exfat_update_atime(node);
534 return MIN(size, node->size - uoffset) - remainder;
535}
536
537ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node,
538 const void* buffer, size_t size, off_t offset)
539{
540 uint64_t uoffset = offset;
541 int rc;
542 cluster_t cluster;
543 const char* bufp = buffer;
544 off_t lsize, loffset, remainder;
545
546 if (offset < 0)
547 return -EINVAL;
548 if (uoffset > node->size)
549 {
550 rc = exfat_truncate(ef, node, uoffset, true);
551 if (rc != 0)
552 return rc;
553 }
554 if (uoffset + size > node->size)
555 {
556 rc = exfat_truncate(ef, node, uoffset + size, false);
557 if (rc != 0)
558 return rc;
559 }
560 if (size == 0)
561 return 0;
562
563 cluster = exfat_advance_cluster(ef, node, uoffset / CLUSTER_SIZE(*ef->sb));
564 if (CLUSTER_INVALID(*ef->sb, cluster))
565 {
566 exfat_error("invalid cluster 0x%x while writing", cluster);
567 return -EIO;
568 }
569
570 loffset = uoffset % CLUSTER_SIZE(*ef->sb);
571 remainder = size;
572 while (remainder > 0)
573 {
574 if (CLUSTER_INVALID(*ef->sb, cluster))
575 {
576 exfat_error("invalid cluster 0x%x while writing", cluster);
577 return -EIO;
578 }
579 lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
580 if (exfat_pwrite(ef->dev, bufp, lsize,
581 exfat_c2o(ef, cluster) + loffset) < 0)
582 {
583 exfat_error("failed to write cluster %#x", cluster);
584 return -EIO;
585 }
586 bufp += lsize;
587 loffset = 0;
588 remainder -= lsize;
589 node->valid_size = MAX(node->valid_size, uoffset + size - remainder);
590 cluster = exfat_next_cluster(ef, node, cluster);
591 }
592 if (!(node->attrib & EXFAT_ATTRIB_DIR))
593 /* directory's mtime should be updated by the caller only when it
594 creates or removes something in this directory */
595 exfat_update_mtime(node);
596 return size - remainder;
597}
Marek Vasut894cf722025-03-17 04:12:46 +0100598
599#ifdef __UBOOT__
Marek Vasut94825602025-04-13 10:55:01 +0200600#define PATH_MAX FS_DIRENT_NAME_LEN
601
Marek Vasut894cf722025-03-17 04:12:46 +0100602struct exfat_dir_stream {
Marek Vasut94825602025-04-13 10:55:01 +0200603 char dirname[PATH_MAX];
Marek Vasut894cf722025-03-17 04:12:46 +0100604 struct fs_dir_stream fs_dirs;
605 struct fs_dirent dirent;
Marek Vasut94825602025-04-13 10:55:01 +0200606 int offset;
Marek Vasut894cf722025-03-17 04:12:46 +0100607};
608
609int exfat_fs_probe(struct blk_desc *fs_dev_desc,
610 struct disk_partition *fs_partition)
611{
612 int ret;
613
614 ctxt.cur_dev = fs_dev_desc;
615 ctxt.cur_part_info = *fs_partition;
616
617 ret = exfat_mount(&ctxt.ef, NULL, "");
618 if (ret)
619 goto error;
620
621 return 0;
622error:
623 ctxt.cur_dev = NULL;
624 return ret;
625}
626
Marek Vasut894cf722025-03-17 04:12:46 +0100627/* Adapted from uclibc 1.0.35 */
628static char *exfat_realpath(const char *path, char got_path[])
629{
630 char copy_path[PATH_MAX];
631 char *max_path, *new_path;
632 size_t path_len;
633
634 if (path == NULL)
635 return NULL;
636
637 if (*path == '\0')
638 return NULL;
639
640 /* Make a copy of the source path since we may need to modify it. */
641 path_len = strlen(path);
642 if (path_len >= PATH_MAX - 2)
643 return NULL;
644
645 /* Copy so that path is at the end of copy_path[] */
646 strcpy(copy_path + (PATH_MAX-1) - path_len, path);
647 path = copy_path + (PATH_MAX-1) - path_len;
648 max_path = got_path + PATH_MAX - 2; /* points to last non-NUL char */
649 new_path = got_path;
650 *new_path++ = '/';
651 path++;
652
653 /* Expand each slash-separated pathname component. */
654 while (*path != '\0') {
655 /* Ignore stray "/". */
656 if (*path == '/') {
657 path++;
658 continue;
659 }
660
661 if (*path == '.') {
662 /* Ignore ".". */
663 if (path[1] == '\0' || path[1] == '/') {
664 path++;
665 continue;
666 }
667
668 if (path[1] == '.') {
669 if (path[2] == '\0' || path[2] == '/') {
670 path += 2;
671 /* Ignore ".." at root. */
672 if (new_path == got_path + 1)
673 continue;
674 /* Handle ".." by backing up. */
675 while ((--new_path)[-1] != '/')
676 ;
677 continue;
678 }
679 }
680 }
681
682 /* Safely copy the next pathname component. */
683 while (*path != '\0' && *path != '/') {
684 if (new_path > max_path)
685 return NULL;
686 *new_path++ = *path++;
687 }
688
689 *new_path++ = '/';
690 }
691
692 /* Delete trailing slash but don't whomp a lone slash. */
693 if (new_path != got_path + 1 && new_path[-1] == '/')
694 new_path--;
695
696 /* Make sure it's null terminated. */
697 *new_path = '\0';
698 return got_path;
699}
700
701int exfat_lookup_realpath(struct exfat* ef, struct exfat_node** node,
702 const char* path)
703{
704 char input_path[FS_DIRENT_NAME_LEN];
705 char real_path[FS_DIRENT_NAME_LEN];
706 char *name;
707
708 /* Input is always absolute path */
709 snprintf(input_path, FS_DIRENT_NAME_LEN, "/%s", path);
710 name = exfat_realpath(input_path, real_path);
711 if (!name)
712 return -EINVAL;
713
714 return exfat_lookup(ef, node, real_path);
715}
716
717int exfat_fs_opendir(const char *filename, struct fs_dir_stream **dirsp)
718{
719 struct exfat_dir_stream *dirs;
Marek Vasut94825602025-04-13 10:55:01 +0200720 struct exfat_node *dnode;
Marek Vasut894cf722025-03-17 04:12:46 +0100721 int err;
722
Marek Vasut94825602025-04-13 10:55:01 +0200723 err = exfat_lookup_realpath(&ctxt.ef, &dnode, filename);
Marek Vasut894cf722025-03-17 04:12:46 +0100724 if (err)
Marek Vasut94825602025-04-13 10:55:01 +0200725 return err;
Marek Vasut894cf722025-03-17 04:12:46 +0100726
Marek Vasut94825602025-04-13 10:55:01 +0200727 if (!(dnode->attrib & EXFAT_ATTRIB_DIR))
Marek Vasut894cf722025-03-17 04:12:46 +0100728 err = -ENOTDIR;
Marek Vasut894cf722025-03-17 04:12:46 +0100729
Marek Vasut94825602025-04-13 10:55:01 +0200730 exfat_put_node(&ctxt.ef, dnode);
731
Marek Vasut894cf722025-03-17 04:12:46 +0100732 if (err)
Marek Vasut94825602025-04-13 10:55:01 +0200733 return err;
734
735 dirs = calloc(1, sizeof(*dirs));
736 if (!dirs)
737 return -ENOMEM;
738
739 strcpy(dirs->dirname, filename);
740 dirs->offset = -1;
Marek Vasut894cf722025-03-17 04:12:46 +0100741
742 *dirsp = &dirs->fs_dirs;
743
744 return 0;
Marek Vasut894cf722025-03-17 04:12:46 +0100745}
746
747int exfat_fs_readdir(struct fs_dir_stream *fs_dirs, struct fs_dirent **dentp)
748{
749 struct exfat_dir_stream *dirs =
750 container_of(fs_dirs, struct exfat_dir_stream, fs_dirs);
751 struct fs_dirent *dent = &dirs->dirent;
Marek Vasut94825602025-04-13 10:55:01 +0200752 struct exfat_node *dnode, *node;
753 struct exfat_iterator it;
754 int offset = 0;
755 int err;
756
757 err = exfat_lookup_realpath(&ctxt.ef, &dnode, dirs->dirname);
758 if (err)
759 return err;
760
761 if (!(dnode->attrib & EXFAT_ATTRIB_DIR)) {
762 err = -ENOTDIR;
763 goto err_out;
764 }
Marek Vasut894cf722025-03-17 04:12:46 +0100765
766 /* Emulate current directory ./ */
Marek Vasut94825602025-04-13 10:55:01 +0200767 if (dirs->offset == -1) {
768 dirs->offset++;
Marek Vasut894cf722025-03-17 04:12:46 +0100769 snprintf(dent->name, FS_DIRENT_NAME_LEN, ".");
770 dent->type = FS_DT_DIR;
771 *dentp = dent;
Marek Vasut94825602025-04-13 10:55:01 +0200772 goto err_out;
Marek Vasut894cf722025-03-17 04:12:46 +0100773 }
774
775 /* Emulate parent directory ../ */
Marek Vasut94825602025-04-13 10:55:01 +0200776 if (dirs->offset == 0) {
777 dirs->offset++;
Marek Vasut894cf722025-03-17 04:12:46 +0100778 snprintf(dent->name, FS_DIRENT_NAME_LEN, "..");
779 dent->type = FS_DT_DIR;
780 *dentp = dent;
Marek Vasut94825602025-04-13 10:55:01 +0200781 goto err_out;
Marek Vasut894cf722025-03-17 04:12:46 +0100782 }
783
Marek Vasut94825602025-04-13 10:55:01 +0200784 err = exfat_opendir(&ctxt.ef, dnode, &it);
785 if (err)
786 goto err_out;
787
788 *dentp = NULL;
789
Marek Vasut894cf722025-03-17 04:12:46 +0100790 /* Read actual directory content */
Marek Vasut94825602025-04-13 10:55:01 +0200791 while ((node = exfat_readdir(&it))) {
792 if (dirs->offset != ++offset) {
793 exfat_put_node(&ctxt.ef, node);
794 continue;
795 }
Marek Vasut894cf722025-03-17 04:12:46 +0100796
Marek Vasut94825602025-04-13 10:55:01 +0200797 exfat_get_name(node, dent->name);
798 if (node->attrib & EXFAT_ATTRIB_DIR) {
799 dent->type = FS_DT_DIR;
800 } else {
801 dent->type = FS_DT_REG;
802 dent->size = node->size;
803 }
804 exfat_put_node(&ctxt.ef, node);
805 *dentp = dent;
806 dirs->offset++;
807 break;
Marek Vasut894cf722025-03-17 04:12:46 +0100808 }
809
Marek Vasut94825602025-04-13 10:55:01 +0200810 exfat_closedir(&ctxt.ef, &it);
Marek Vasut894cf722025-03-17 04:12:46 +0100811
Marek Vasut94825602025-04-13 10:55:01 +0200812err_out:
813 exfat_put_node(&ctxt.ef, dnode);
814 return err;
Marek Vasut894cf722025-03-17 04:12:46 +0100815}
816
817void exfat_fs_closedir(struct fs_dir_stream *fs_dirs)
818{
Marek Vasut94825602025-04-13 10:55:01 +0200819 struct exfat_dir_stream *dirs =
820 container_of(fs_dirs, struct exfat_dir_stream, fs_dirs);
821
822 free(dirs);
Marek Vasut894cf722025-03-17 04:12:46 +0100823}
824
825int exfat_fs_ls(const char *dirname)
826{
827 struct exfat_node *dnode, *node;
828 char name[FS_DIRENT_NAME_LEN];
829 int nfiles = 0, ndirs = 2;
830 struct exfat_iterator it;
831 int err;
832
833 err = exfat_lookup_realpath(&ctxt.ef, &dnode, dirname);
834 if (err)
835 return err;
836
837 if (!(dnode->attrib & EXFAT_ATTRIB_DIR)) {
838 err = -ENOTDIR;
839 goto err_out;
840 }
841
842 err = exfat_opendir(&ctxt.ef, dnode, &it);
843 if (err)
844 goto err_out;
845
846 printf(" ./\n");
847 printf(" ../\n");
848
849 /* Read actual directory content */
850 while ((node = exfat_readdir(&it))) {
851 exfat_get_name(node, name);
852 if (node->attrib & EXFAT_ATTRIB_DIR) {
853 printf(" %s/\n", name);
854 ndirs++;
855 } else {
856 printf(" %8lld %s\n", node->size, name);
857 nfiles++;
858 }
859 exfat_put_node(&ctxt.ef, node);
860 }
861
862 printf("\n%d file(s), %d dir(s)\n\n", nfiles, ndirs);
863
864 exfat_closedir(&ctxt.ef, &it);
865
866err_out:
867 exfat_put_node(&ctxt.ef, dnode);
868 return err;
869}
870
871int exfat_fs_exists(const char *filename)
872{
873 struct exfat_node* node;
874 int err;
875
876 err = exfat_lookup_realpath(&ctxt.ef, &node, filename);
877 if (err)
Marek Vasut3bd905d2025-04-13 10:55:02 +0200878 return 0;
Marek Vasut894cf722025-03-17 04:12:46 +0100879
880 exfat_put_node(&ctxt.ef, node);
881
Marek Vasut3bd905d2025-04-13 10:55:02 +0200882 return 1;
Marek Vasut894cf722025-03-17 04:12:46 +0100883}
884
885int exfat_fs_size(const char *filename, loff_t *size)
886{
887 struct exfat_node* node;
888 int err;
889
890 err = exfat_lookup_realpath(&ctxt.ef, &node, filename);
891 if (err)
892 return err;
893
894 *size = node->size;
895
896 exfat_put_node(&ctxt.ef, node);
897
898 return 0;
899}
900
901int exfat_fs_read(const char *filename, void *buf, loff_t offset, loff_t len,
902 loff_t *actread)
903{
904 struct exfat_node* node;
905 ssize_t sz;
906 int err;
907
908 err = exfat_lookup_realpath(&ctxt.ef, &node, filename);
909 if (err)
910 return err;
911
912 if (!len)
913 len = node->size;
914
915 sz = exfat_generic_pread(&ctxt.ef, node, buf, len, offset);
916 if (sz < 0) {
917 *actread = 0;
918 err = -EINVAL;
919 goto exit;
920 }
921
922 *actread = sz;
923
Marek Vasuta72c4d92025-04-13 10:54:59 +0200924 err = exfat_flush_node(&ctxt.ef, node);
Marek Vasut894cf722025-03-17 04:12:46 +0100925exit:
926 exfat_put_node(&ctxt.ef, node);
927 return err;
928}
929
930int exfat_fs_unlink(const char *filename)
931{
932 struct exfat_node* node;
933 int err;
934
935 err = exfat_lookup_realpath(&ctxt.ef, &node, filename);
936 if (err) {
937 printf("%s: doesn't exist (%d)\n", filename, err);
938 return err;
939 }
940
941 if (node->attrib & EXFAT_ATTRIB_DIR) {
942 err = exfat_rmdir(&ctxt.ef, node);
943 if (err == -ENOTEMPTY)
944 printf("Error: directory is not empty: %d\n", err);
945 } else {
946 err = exfat_unlink(&ctxt.ef, node);
947 }
948
949 if (err)
950 goto exit;
951
952 exfat_put_node(&ctxt.ef, node);
953
954 return exfat_cleanup_node(&ctxt.ef, node);
955exit:
956 exfat_put_node(&ctxt.ef, node);
957 return err;
958}
959
960int exfat_fs_mkdir(const char *dirname)
961{
962 if (!strcmp(dirname, ".") || !strcmp(dirname, ".."))
963 return -EINVAL;
964
965 return exfat_mkdir(&ctxt.ef, dirname);
966}
967
968int exfat_fs_write(const char *filename, void *buf, loff_t offset,
969 loff_t len, loff_t *actwrite)
970{
971 struct exfat_node* node;
972 ssize_t sz;
973 int err;
974
975 /*
976 * Ignore -EEXIST error here, if the file exists,
977 * this write should act as an append to offset.
978 */
979 err = exfat_mknod(&ctxt.ef, filename);
980 if (err && err != -EEXIST)
981 return err;
982
983 err = exfat_lookup_realpath(&ctxt.ef, &node, filename);
984 if (err)
985 return err;
986
987 /* Write into directories is not allowed. */
988 if (node->attrib & EXFAT_ATTRIB_DIR)
989 return -EISDIR;
990
991 /* Write past end of file is not allowed. */
992 if (offset > node->size) {
993 err = -EINVAL;
994 goto exit;
995 }
996
997 sz = exfat_generic_pwrite(&ctxt.ef, node, buf, len, offset);
998 if (sz < 0) {
999 *actwrite = 0;
1000 err = -EINVAL;
1001 goto exit;
1002 }
1003
1004 err = exfat_truncate(&ctxt.ef, node, offset + sz, false);
1005 if (err)
1006 goto exit;
1007
1008 *actwrite = sz;
1009
1010 err = exfat_flush_node(&ctxt.ef, node);
1011exit:
1012 exfat_put_node(&ctxt.ef, node);
1013 return err;
1014}
1015
Marek Vasut6c561d62025-04-13 10:55:04 +02001016int exfat_fs_rename(const char *old_path, const char *new_path)
1017{
1018 return exfat_rename(&ctxt.ef, old_path, new_path);
1019}
1020
Marek Vasut894cf722025-03-17 04:12:46 +01001021void exfat_fs_close(void)
1022{
1023 exfat_unmount(&ctxt.ef);
1024 ctxt.cur_dev = NULL;
1025}
1026#endif /* __U_BOOT__ */