blob: 77cd2dfb6dc945cad72f61a2011cb0cee97c9ae3 [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 Vasute1ba77c2025-04-30 18:45:51 +0200723 if (strlen(filename) >= PATH_MAX)
724 return -ENAMETOOLONG;
725
Marek Vasut94825602025-04-13 10:55:01 +0200726 err = exfat_lookup_realpath(&ctxt.ef, &dnode, filename);
Marek Vasut894cf722025-03-17 04:12:46 +0100727 if (err)
Marek Vasut94825602025-04-13 10:55:01 +0200728 return err;
Marek Vasut894cf722025-03-17 04:12:46 +0100729
Marek Vasut94825602025-04-13 10:55:01 +0200730 if (!(dnode->attrib & EXFAT_ATTRIB_DIR))
Marek Vasut894cf722025-03-17 04:12:46 +0100731 err = -ENOTDIR;
Marek Vasut894cf722025-03-17 04:12:46 +0100732
Marek Vasut94825602025-04-13 10:55:01 +0200733 exfat_put_node(&ctxt.ef, dnode);
734
Marek Vasut894cf722025-03-17 04:12:46 +0100735 if (err)
Marek Vasut94825602025-04-13 10:55:01 +0200736 return err;
737
738 dirs = calloc(1, sizeof(*dirs));
739 if (!dirs)
740 return -ENOMEM;
741
Marek Vasute1ba77c2025-04-30 18:45:51 +0200742 strncpy(dirs->dirname, filename, PATH_MAX - 1);
Marek Vasut94825602025-04-13 10:55:01 +0200743 dirs->offset = -1;
Marek Vasut894cf722025-03-17 04:12:46 +0100744
745 *dirsp = &dirs->fs_dirs;
746
747 return 0;
Marek Vasut894cf722025-03-17 04:12:46 +0100748}
749
750int exfat_fs_readdir(struct fs_dir_stream *fs_dirs, struct fs_dirent **dentp)
751{
752 struct exfat_dir_stream *dirs =
753 container_of(fs_dirs, struct exfat_dir_stream, fs_dirs);
754 struct fs_dirent *dent = &dirs->dirent;
Marek Vasut94825602025-04-13 10:55:01 +0200755 struct exfat_node *dnode, *node;
756 struct exfat_iterator it;
757 int offset = 0;
758 int err;
759
760 err = exfat_lookup_realpath(&ctxt.ef, &dnode, dirs->dirname);
761 if (err)
762 return err;
763
764 if (!(dnode->attrib & EXFAT_ATTRIB_DIR)) {
765 err = -ENOTDIR;
766 goto err_out;
767 }
Marek Vasut894cf722025-03-17 04:12:46 +0100768
769 /* Emulate current directory ./ */
Marek Vasut94825602025-04-13 10:55:01 +0200770 if (dirs->offset == -1) {
771 dirs->offset++;
Marek Vasut894cf722025-03-17 04:12:46 +0100772 snprintf(dent->name, FS_DIRENT_NAME_LEN, ".");
773 dent->type = FS_DT_DIR;
774 *dentp = dent;
Marek Vasut94825602025-04-13 10:55:01 +0200775 goto err_out;
Marek Vasut894cf722025-03-17 04:12:46 +0100776 }
777
778 /* Emulate parent directory ../ */
Marek Vasut94825602025-04-13 10:55:01 +0200779 if (dirs->offset == 0) {
780 dirs->offset++;
Marek Vasut894cf722025-03-17 04:12:46 +0100781 snprintf(dent->name, FS_DIRENT_NAME_LEN, "..");
782 dent->type = FS_DT_DIR;
783 *dentp = dent;
Marek Vasut94825602025-04-13 10:55:01 +0200784 goto err_out;
Marek Vasut894cf722025-03-17 04:12:46 +0100785 }
786
Marek Vasut94825602025-04-13 10:55:01 +0200787 err = exfat_opendir(&ctxt.ef, dnode, &it);
788 if (err)
789 goto err_out;
790
791 *dentp = NULL;
792
Marek Vasut894cf722025-03-17 04:12:46 +0100793 /* Read actual directory content */
Marek Vasut94825602025-04-13 10:55:01 +0200794 while ((node = exfat_readdir(&it))) {
795 if (dirs->offset != ++offset) {
796 exfat_put_node(&ctxt.ef, node);
797 continue;
798 }
Marek Vasut894cf722025-03-17 04:12:46 +0100799
Marek Vasut94825602025-04-13 10:55:01 +0200800 exfat_get_name(node, dent->name);
801 if (node->attrib & EXFAT_ATTRIB_DIR) {
802 dent->type = FS_DT_DIR;
803 } else {
804 dent->type = FS_DT_REG;
805 dent->size = node->size;
806 }
807 exfat_put_node(&ctxt.ef, node);
808 *dentp = dent;
809 dirs->offset++;
810 break;
Marek Vasut894cf722025-03-17 04:12:46 +0100811 }
812
Marek Vasut94825602025-04-13 10:55:01 +0200813 exfat_closedir(&ctxt.ef, &it);
Marek Vasut894cf722025-03-17 04:12:46 +0100814
Marek Vasut94825602025-04-13 10:55:01 +0200815err_out:
816 exfat_put_node(&ctxt.ef, dnode);
817 return err;
Marek Vasut894cf722025-03-17 04:12:46 +0100818}
819
820void exfat_fs_closedir(struct fs_dir_stream *fs_dirs)
821{
Marek Vasut94825602025-04-13 10:55:01 +0200822 struct exfat_dir_stream *dirs =
823 container_of(fs_dirs, struct exfat_dir_stream, fs_dirs);
824
825 free(dirs);
Marek Vasut894cf722025-03-17 04:12:46 +0100826}
827
828int exfat_fs_ls(const char *dirname)
829{
830 struct exfat_node *dnode, *node;
831 char name[FS_DIRENT_NAME_LEN];
832 int nfiles = 0, ndirs = 2;
833 struct exfat_iterator it;
834 int err;
835
836 err = exfat_lookup_realpath(&ctxt.ef, &dnode, dirname);
837 if (err)
838 return err;
839
840 if (!(dnode->attrib & EXFAT_ATTRIB_DIR)) {
841 err = -ENOTDIR;
842 goto err_out;
843 }
844
845 err = exfat_opendir(&ctxt.ef, dnode, &it);
846 if (err)
847 goto err_out;
848
849 printf(" ./\n");
850 printf(" ../\n");
851
852 /* Read actual directory content */
853 while ((node = exfat_readdir(&it))) {
854 exfat_get_name(node, name);
855 if (node->attrib & EXFAT_ATTRIB_DIR) {
856 printf(" %s/\n", name);
857 ndirs++;
858 } else {
859 printf(" %8lld %s\n", node->size, name);
860 nfiles++;
861 }
862 exfat_put_node(&ctxt.ef, node);
863 }
864
865 printf("\n%d file(s), %d dir(s)\n\n", nfiles, ndirs);
866
867 exfat_closedir(&ctxt.ef, &it);
868
869err_out:
870 exfat_put_node(&ctxt.ef, dnode);
871 return err;
872}
873
874int exfat_fs_exists(const char *filename)
875{
876 struct exfat_node* node;
877 int err;
878
879 err = exfat_lookup_realpath(&ctxt.ef, &node, filename);
880 if (err)
Marek Vasut3bd905d2025-04-13 10:55:02 +0200881 return 0;
Marek Vasut894cf722025-03-17 04:12:46 +0100882
883 exfat_put_node(&ctxt.ef, node);
884
Marek Vasut3bd905d2025-04-13 10:55:02 +0200885 return 1;
Marek Vasut894cf722025-03-17 04:12:46 +0100886}
887
888int exfat_fs_size(const char *filename, loff_t *size)
889{
890 struct exfat_node* node;
891 int err;
892
893 err = exfat_lookup_realpath(&ctxt.ef, &node, filename);
894 if (err)
895 return err;
896
897 *size = node->size;
898
899 exfat_put_node(&ctxt.ef, node);
900
901 return 0;
902}
903
904int exfat_fs_read(const char *filename, void *buf, loff_t offset, loff_t len,
905 loff_t *actread)
906{
907 struct exfat_node* node;
908 ssize_t sz;
909 int err;
910
911 err = exfat_lookup_realpath(&ctxt.ef, &node, filename);
912 if (err)
913 return err;
914
915 if (!len)
916 len = node->size;
917
918 sz = exfat_generic_pread(&ctxt.ef, node, buf, len, offset);
919 if (sz < 0) {
920 *actread = 0;
921 err = -EINVAL;
922 goto exit;
923 }
924
925 *actread = sz;
926
Marek Vasuta72c4d92025-04-13 10:54:59 +0200927 err = exfat_flush_node(&ctxt.ef, node);
Marek Vasut894cf722025-03-17 04:12:46 +0100928exit:
929 exfat_put_node(&ctxt.ef, node);
930 return err;
931}
932
933int exfat_fs_unlink(const char *filename)
934{
935 struct exfat_node* node;
936 int err;
937
938 err = exfat_lookup_realpath(&ctxt.ef, &node, filename);
939 if (err) {
940 printf("%s: doesn't exist (%d)\n", filename, err);
941 return err;
942 }
943
944 if (node->attrib & EXFAT_ATTRIB_DIR) {
945 err = exfat_rmdir(&ctxt.ef, node);
946 if (err == -ENOTEMPTY)
947 printf("Error: directory is not empty: %d\n", err);
948 } else {
949 err = exfat_unlink(&ctxt.ef, node);
950 }
951
952 if (err)
953 goto exit;
954
955 exfat_put_node(&ctxt.ef, node);
956
957 return exfat_cleanup_node(&ctxt.ef, node);
958exit:
959 exfat_put_node(&ctxt.ef, node);
960 return err;
961}
962
963int exfat_fs_mkdir(const char *dirname)
964{
965 if (!strcmp(dirname, ".") || !strcmp(dirname, ".."))
966 return -EINVAL;
967
968 return exfat_mkdir(&ctxt.ef, dirname);
969}
970
971int exfat_fs_write(const char *filename, void *buf, loff_t offset,
972 loff_t len, loff_t *actwrite)
973{
974 struct exfat_node* node;
975 ssize_t sz;
976 int err;
977
978 /*
979 * Ignore -EEXIST error here, if the file exists,
980 * this write should act as an append to offset.
981 */
982 err = exfat_mknod(&ctxt.ef, filename);
983 if (err && err != -EEXIST)
984 return err;
985
986 err = exfat_lookup_realpath(&ctxt.ef, &node, filename);
987 if (err)
988 return err;
989
990 /* Write into directories is not allowed. */
991 if (node->attrib & EXFAT_ATTRIB_DIR)
992 return -EISDIR;
993
994 /* Write past end of file is not allowed. */
995 if (offset > node->size) {
996 err = -EINVAL;
997 goto exit;
998 }
999
1000 sz = exfat_generic_pwrite(&ctxt.ef, node, buf, len, offset);
1001 if (sz < 0) {
1002 *actwrite = 0;
1003 err = -EINVAL;
1004 goto exit;
1005 }
1006
1007 err = exfat_truncate(&ctxt.ef, node, offset + sz, false);
1008 if (err)
1009 goto exit;
1010
1011 *actwrite = sz;
1012
1013 err = exfat_flush_node(&ctxt.ef, node);
1014exit:
1015 exfat_put_node(&ctxt.ef, node);
1016 return err;
1017}
1018
Marek Vasut6c561d62025-04-13 10:55:04 +02001019int exfat_fs_rename(const char *old_path, const char *new_path)
1020{
1021 return exfat_rename(&ctxt.ef, old_path, new_path);
1022}
1023
Marek Vasut894cf722025-03-17 04:12:46 +01001024void exfat_fs_close(void)
1025{
1026 exfat_unmount(&ctxt.ef);
1027 ctxt.cur_dev = NULL;
1028}
1029#endif /* __U_BOOT__ */