blob: 81e82829c72831d20c29eec47a06c1157ab4b21e [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__
600struct exfat_dir_stream {
601 struct fs_dir_stream fs_dirs;
602 struct fs_dirent dirent;
603
604 struct exfat_node* node;
605 struct exfat_iterator it;
606 /* State tracker flags for emulated . and .. dirents */
607 bool dot;
608 bool dotdot;
609};
610
611int exfat_fs_probe(struct blk_desc *fs_dev_desc,
612 struct disk_partition *fs_partition)
613{
614 int ret;
615
616 ctxt.cur_dev = fs_dev_desc;
617 ctxt.cur_part_info = *fs_partition;
618
619 ret = exfat_mount(&ctxt.ef, NULL, "");
620 if (ret)
621 goto error;
622
623 return 0;
624error:
625 ctxt.cur_dev = NULL;
626 return ret;
627}
628
629#define PATH_MAX FS_DIRENT_NAME_LEN
630
631/* Adapted from uclibc 1.0.35 */
632static char *exfat_realpath(const char *path, char got_path[])
633{
634 char copy_path[PATH_MAX];
635 char *max_path, *new_path;
636 size_t path_len;
637
638 if (path == NULL)
639 return NULL;
640
641 if (*path == '\0')
642 return NULL;
643
644 /* Make a copy of the source path since we may need to modify it. */
645 path_len = strlen(path);
646 if (path_len >= PATH_MAX - 2)
647 return NULL;
648
649 /* Copy so that path is at the end of copy_path[] */
650 strcpy(copy_path + (PATH_MAX-1) - path_len, path);
651 path = copy_path + (PATH_MAX-1) - path_len;
652 max_path = got_path + PATH_MAX - 2; /* points to last non-NUL char */
653 new_path = got_path;
654 *new_path++ = '/';
655 path++;
656
657 /* Expand each slash-separated pathname component. */
658 while (*path != '\0') {
659 /* Ignore stray "/". */
660 if (*path == '/') {
661 path++;
662 continue;
663 }
664
665 if (*path == '.') {
666 /* Ignore ".". */
667 if (path[1] == '\0' || path[1] == '/') {
668 path++;
669 continue;
670 }
671
672 if (path[1] == '.') {
673 if (path[2] == '\0' || path[2] == '/') {
674 path += 2;
675 /* Ignore ".." at root. */
676 if (new_path == got_path + 1)
677 continue;
678 /* Handle ".." by backing up. */
679 while ((--new_path)[-1] != '/')
680 ;
681 continue;
682 }
683 }
684 }
685
686 /* Safely copy the next pathname component. */
687 while (*path != '\0' && *path != '/') {
688 if (new_path > max_path)
689 return NULL;
690 *new_path++ = *path++;
691 }
692
693 *new_path++ = '/';
694 }
695
696 /* Delete trailing slash but don't whomp a lone slash. */
697 if (new_path != got_path + 1 && new_path[-1] == '/')
698 new_path--;
699
700 /* Make sure it's null terminated. */
701 *new_path = '\0';
702 return got_path;
703}
704
705int exfat_lookup_realpath(struct exfat* ef, struct exfat_node** node,
706 const char* path)
707{
708 char input_path[FS_DIRENT_NAME_LEN];
709 char real_path[FS_DIRENT_NAME_LEN];
710 char *name;
711
712 /* Input is always absolute path */
713 snprintf(input_path, FS_DIRENT_NAME_LEN, "/%s", path);
714 name = exfat_realpath(input_path, real_path);
715 if (!name)
716 return -EINVAL;
717
718 return exfat_lookup(ef, node, real_path);
719}
720
721int exfat_fs_opendir(const char *filename, struct fs_dir_stream **dirsp)
722{
723 struct exfat_dir_stream *dirs;
724 int err;
725
726 dirs = calloc(1, sizeof(*dirs));
727 if (!dirs)
728 return -ENOMEM;
729
730 err = exfat_lookup_realpath(&ctxt.ef, &dirs->node, filename);
731 if (err)
732 goto err_out;
733
734 if (!(dirs->node->attrib & EXFAT_ATTRIB_DIR)) {
735 err = -ENOTDIR;
736 goto err_out;
737 }
738
739 err = exfat_opendir(&ctxt.ef, dirs->node, &dirs->it);
740 if (err)
741 goto err_out;
742
743 *dirsp = &dirs->fs_dirs;
744
745 return 0;
746err_out:
747 free(dirs);
748 return err;
749}
750
751int exfat_fs_readdir(struct fs_dir_stream *fs_dirs, struct fs_dirent **dentp)
752{
753 struct exfat_dir_stream *dirs =
754 container_of(fs_dirs, struct exfat_dir_stream, fs_dirs);
755 struct fs_dirent *dent = &dirs->dirent;
756 struct exfat_node* node;
757
758 /* Emulate current directory ./ */
759 if (!dirs->dot) {
760 dirs->dot = true;
761 snprintf(dent->name, FS_DIRENT_NAME_LEN, ".");
762 dent->type = FS_DT_DIR;
763 *dentp = dent;
764 return 0;
765 }
766
767 /* Emulate parent directory ../ */
768 if (!dirs->dotdot) {
769 dirs->dotdot = true;
770 snprintf(dent->name, FS_DIRENT_NAME_LEN, "..");
771 dent->type = FS_DT_DIR;
772 *dentp = dent;
773 return 0;
774 }
775
776 /* Read actual directory content */
777 node = exfat_readdir(&dirs->it);
778 if (!node) { /* No more content, reset . and .. emulation */
779 dirs->dot = false;
780 dirs->dotdot = false;
781 return 1;
782 }
783
784 exfat_get_name(node, dent->name);
785 if (node->attrib & EXFAT_ATTRIB_DIR) {
786 dent->type = FS_DT_DIR;
787 } else {
788 dent->type = FS_DT_REG;
789 dent->size = node->size;
790 }
791
792 *dentp = dent;
793
794 return 0;
795}
796
797void exfat_fs_closedir(struct fs_dir_stream *fs_dirs)
798{
799 free(fs_dirs);
800}
801
802int exfat_fs_ls(const char *dirname)
803{
804 struct exfat_node *dnode, *node;
805 char name[FS_DIRENT_NAME_LEN];
806 int nfiles = 0, ndirs = 2;
807 struct exfat_iterator it;
808 int err;
809
810 err = exfat_lookup_realpath(&ctxt.ef, &dnode, dirname);
811 if (err)
812 return err;
813
814 if (!(dnode->attrib & EXFAT_ATTRIB_DIR)) {
815 err = -ENOTDIR;
816 goto err_out;
817 }
818
819 err = exfat_opendir(&ctxt.ef, dnode, &it);
820 if (err)
821 goto err_out;
822
823 printf(" ./\n");
824 printf(" ../\n");
825
826 /* Read actual directory content */
827 while ((node = exfat_readdir(&it))) {
828 exfat_get_name(node, name);
829 if (node->attrib & EXFAT_ATTRIB_DIR) {
830 printf(" %s/\n", name);
831 ndirs++;
832 } else {
833 printf(" %8lld %s\n", node->size, name);
834 nfiles++;
835 }
836 exfat_put_node(&ctxt.ef, node);
837 }
838
839 printf("\n%d file(s), %d dir(s)\n\n", nfiles, ndirs);
840
841 exfat_closedir(&ctxt.ef, &it);
842
843err_out:
844 exfat_put_node(&ctxt.ef, dnode);
845 return err;
846}
847
848int exfat_fs_exists(const char *filename)
849{
850 struct exfat_node* node;
851 int err;
852
853 err = exfat_lookup_realpath(&ctxt.ef, &node, filename);
854 if (err)
855 return err;
856
857 exfat_put_node(&ctxt.ef, node);
858
859 return 0;
860}
861
862int exfat_fs_size(const char *filename, loff_t *size)
863{
864 struct exfat_node* node;
865 int err;
866
867 err = exfat_lookup_realpath(&ctxt.ef, &node, filename);
868 if (err)
869 return err;
870
871 *size = node->size;
872
873 exfat_put_node(&ctxt.ef, node);
874
875 return 0;
876}
877
878int exfat_fs_read(const char *filename, void *buf, loff_t offset, loff_t len,
879 loff_t *actread)
880{
881 struct exfat_node* node;
882 ssize_t sz;
883 int err;
884
885 err = exfat_lookup_realpath(&ctxt.ef, &node, filename);
886 if (err)
887 return err;
888
889 if (!len)
890 len = node->size;
891
892 sz = exfat_generic_pread(&ctxt.ef, node, buf, len, offset);
893 if (sz < 0) {
894 *actread = 0;
895 err = -EINVAL;
896 goto exit;
897 }
898
899 *actread = sz;
900
901 exfat_put_node(&ctxt.ef, node);
902
903 return exfat_flush_node(&ctxt.ef, node);
904exit:
905 exfat_put_node(&ctxt.ef, node);
906 return err;
907}
908
909int exfat_fs_unlink(const char *filename)
910{
911 struct exfat_node* node;
912 int err;
913
914 err = exfat_lookup_realpath(&ctxt.ef, &node, filename);
915 if (err) {
916 printf("%s: doesn't exist (%d)\n", filename, err);
917 return err;
918 }
919
920 if (node->attrib & EXFAT_ATTRIB_DIR) {
921 err = exfat_rmdir(&ctxt.ef, node);
922 if (err == -ENOTEMPTY)
923 printf("Error: directory is not empty: %d\n", err);
924 } else {
925 err = exfat_unlink(&ctxt.ef, node);
926 }
927
928 if (err)
929 goto exit;
930
931 exfat_put_node(&ctxt.ef, node);
932
933 return exfat_cleanup_node(&ctxt.ef, node);
934exit:
935 exfat_put_node(&ctxt.ef, node);
936 return err;
937}
938
939int exfat_fs_mkdir(const char *dirname)
940{
941 if (!strcmp(dirname, ".") || !strcmp(dirname, ".."))
942 return -EINVAL;
943
944 return exfat_mkdir(&ctxt.ef, dirname);
945}
946
947int exfat_fs_write(const char *filename, void *buf, loff_t offset,
948 loff_t len, loff_t *actwrite)
949{
950 struct exfat_node* node;
951 ssize_t sz;
952 int err;
953
954 /*
955 * Ignore -EEXIST error here, if the file exists,
956 * this write should act as an append to offset.
957 */
958 err = exfat_mknod(&ctxt.ef, filename);
959 if (err && err != -EEXIST)
960 return err;
961
962 err = exfat_lookup_realpath(&ctxt.ef, &node, filename);
963 if (err)
964 return err;
965
966 /* Write into directories is not allowed. */
967 if (node->attrib & EXFAT_ATTRIB_DIR)
968 return -EISDIR;
969
970 /* Write past end of file is not allowed. */
971 if (offset > node->size) {
972 err = -EINVAL;
973 goto exit;
974 }
975
976 sz = exfat_generic_pwrite(&ctxt.ef, node, buf, len, offset);
977 if (sz < 0) {
978 *actwrite = 0;
979 err = -EINVAL;
980 goto exit;
981 }
982
983 err = exfat_truncate(&ctxt.ef, node, offset + sz, false);
984 if (err)
985 goto exit;
986
987 *actwrite = sz;
988
989 err = exfat_flush_node(&ctxt.ef, node);
990exit:
991 exfat_put_node(&ctxt.ef, node);
992 return err;
993}
994
995void exfat_fs_close(void)
996{
997 exfat_unmount(&ctxt.ef);
998 ctxt.cur_dev = NULL;
999}
1000#endif /* __U_BOOT__ */