1 /*
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>
25 #ifndef __UBOOT__
26 #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
48 #else
49 #include <fs.h>
50 #include <fs_internal.h>
51
52 static struct exfat_ctxt {
53 struct disk_partition cur_part_info;
54 struct blk_desc *cur_dev;
55 struct exfat ef;
56 } ctxt;
57 #endif
58
59 struct 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
68 #ifdef __UBOOT__
69 struct exfat_ctxt *ctxt;
70 #endif
71 };
72
73 #ifndef __UBOOT__
is_open(int fd)74 static bool is_open(int fd)
75 {
76 return fcntl(fd, F_GETFD) != -1;
77 }
78
open_ro(const char * spec)79 static int open_ro(const char* spec)
80 {
81 return open(spec, O_RDONLY);
82 }
83
open_rw(const char * spec)84 static 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
exfat_open(const char * spec,enum exfat_mode mode)104 struct 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
exfat_close(struct exfat_dev * dev)302 int 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
exfat_fsync(struct exfat_dev * dev)322 int 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
exfat_get_mode(const struct exfat_dev * dev)341 enum exfat_mode exfat_get_mode(const struct exfat_dev* dev)
342 {
343 return dev->mode;
344 }
345
exfat_get_size(const struct exfat_dev * dev)346 off_t exfat_get_size(const struct exfat_dev* dev)
347 {
348 return dev->size;
349 }
350
exfat_seek(struct exfat_dev * dev,off_t offset,int whence)351 off_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
exfat_read(struct exfat_dev * dev,void * buffer,size_t size)361 ssize_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
exfat_write(struct exfat_dev * dev,const void * buffer,size_t size)373 ssize_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
exfat_pread(struct exfat_dev * dev,void * buffer,size_t size,off_t offset)385 ssize_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
exfat_pwrite(struct exfat_dev * dev,const void * buffer,size_t size,off_t offset)395 ssize_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 }
404 #else /* U-Boot */
exfat_open(const char * spec,enum exfat_mode mode)405 struct 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
exfat_close(struct exfat_dev * dev)421 int exfat_close(struct exfat_dev* dev)
422 {
423 free(dev);
424 return 0;
425 }
426
exfat_fsync(struct exfat_dev * dev)427 int exfat_fsync(struct exfat_dev* dev)
428 {
429 return 0;
430 }
431
exfat_get_mode(const struct exfat_dev * dev)432 enum exfat_mode exfat_get_mode(const struct exfat_dev* dev)
433 {
434 return dev->mode;
435 }
436
exfat_get_size(const struct exfat_dev * dev)437 off_t exfat_get_size(const struct exfat_dev* dev)
438 {
439 return dev->size;
440 }
441
exfat_pread(struct exfat_dev * dev,void * buffer,size_t size,off_t offset)442 ssize_t exfat_pread(struct exfat_dev* dev, void* buffer, size_t size,
443 off_t offset)
444 {
445 lbaint_t sect;
446 int off;
447
448 if (!ctxt.cur_dev)
449 return -EIO;
450
451 sect = offset >> ctxt.cur_dev->log2blksz;
452 off = offset & (ctxt.cur_dev->blksz - 1);
453
454 if (fs_devread(ctxt.cur_dev, &ctxt.cur_part_info, sect,
455 off, size, buffer))
456 return 0;
457 return -EIO;
458 }
459
exfat_pwrite(struct exfat_dev * dev,const void * buffer,size_t size,off_t offset)460 ssize_t exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size,
461 off_t offset)
462 {
463 lbaint_t sect;
464 int off;
465
466 if (!ctxt.cur_dev)
467 return -EIO;
468
469 sect = offset >> ctxt.cur_dev->log2blksz;
470 off = offset & (ctxt.cur_dev->blksz - 1);
471
472 if (fs_devwrite(ctxt.cur_dev, &ctxt.cur_part_info, sect,
473 off, size, buffer))
474 return 0;
475 return -EIO;
476 }
477 #endif
478
exfat_generic_pread(const struct exfat * ef,struct exfat_node * node,void * buffer,size_t size,off_t offset)479 ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node,
480 void* buffer, size_t size, off_t offset)
481 {
482 cluster_t cluster;
483 char* bufp = buffer;
484 off_t lsize, loffset, remainder;
485
486 if (offset >= node->size)
487 return 0;
488 if (size == 0)
489 return 0;
490
491 if (offset + size > node->valid_size)
492 {
493 ssize_t bytes = 0;
494
495 if (offset < node->valid_size)
496 {
497 bytes = exfat_generic_pread(ef, node, buffer,
498 node->valid_size - offset, offset);
499 if (bytes < 0 || (size_t)bytes < node->valid_size - offset)
500 return bytes;
501 }
502 memset(buffer + bytes, 0,
503 MIN(size - bytes, node->size - node->valid_size));
504 return MIN(size, node->size - offset);
505 }
506
507 cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb));
508 if (CLUSTER_INVALID(*ef->sb, cluster))
509 {
510 exfat_error("invalid cluster 0x%x while reading", cluster);
511 return -EIO;
512 }
513
514 loffset = offset % CLUSTER_SIZE(*ef->sb);
515 remainder = MIN(size, node->size - offset);
516 while (remainder > 0)
517 {
518 if (CLUSTER_INVALID(*ef->sb, cluster))
519 {
520 exfat_error("invalid cluster 0x%x while reading", cluster);
521 return -EIO;
522 }
523 lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
524 if (exfat_pread(ef->dev, bufp, lsize,
525 exfat_c2o(ef, cluster) + loffset) < 0)
526 {
527 exfat_error("failed to read cluster %#x", cluster);
528 return -EIO;
529 }
530 bufp += lsize;
531 loffset = 0;
532 remainder -= lsize;
533 cluster = exfat_next_cluster(ef, node, cluster);
534 }
535 if (!(node->attrib & EXFAT_ATTRIB_DIR) && !ef->ro && !ef->noatime)
536 exfat_update_atime(node);
537 return MIN(size, node->size - offset) - remainder;
538 }
539
exfat_generic_pwrite(struct exfat * ef,struct exfat_node * node,const void * buffer,size_t size,off_t offset)540 ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node,
541 const void* buffer, size_t size, off_t offset)
542 {
543 int rc;
544 cluster_t cluster;
545 const char* bufp = buffer;
546 off_t lsize, loffset, remainder;
547
548 if (offset > node->size)
549 {
550 rc = exfat_truncate(ef, node, offset, true);
551 if (rc != 0)
552 return rc;
553 }
554 if (offset + size > node->size)
555 {
556 rc = exfat_truncate(ef, node, offset + 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, offset / 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 = offset % 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, offset + 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 }
598
599 #ifdef __UBOOT__
600 #define PATH_MAX FS_DIRENT_NAME_LEN
601
602 struct exfat_dir_stream {
603 char dirname[PATH_MAX];
604 struct fs_dir_stream fs_dirs;
605 struct fs_dirent dirent;
606 int offset;
607 };
608
exfat_fs_probe(struct blk_desc * fs_dev_desc,struct disk_partition * fs_partition)609 int 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;
622 error:
623 ctxt.cur_dev = NULL;
624 return ret;
625 }
626
627 /* Adapted from uclibc 1.0.35 */
exfat_realpath(const char * path,char got_path[])628 static 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
exfat_lookup_realpath(struct exfat * ef,struct exfat_node ** node,const char * path)701 int 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
exfat_fs_opendir(const char * filename,struct fs_dir_stream ** dirsp)717 int exfat_fs_opendir(const char *filename, struct fs_dir_stream **dirsp)
718 {
719 struct exfat_dir_stream *dirs;
720 struct exfat_node *dnode;
721 int err;
722
723 if (strlen(filename) >= PATH_MAX)
724 return -ENAMETOOLONG;
725
726 err = exfat_lookup_realpath(&ctxt.ef, &dnode, filename);
727 if (err)
728 return err;
729
730 if (!(dnode->attrib & EXFAT_ATTRIB_DIR))
731 err = -ENOTDIR;
732
733 exfat_put_node(&ctxt.ef, dnode);
734
735 if (err)
736 return err;
737
738 dirs = calloc(1, sizeof(*dirs));
739 if (!dirs)
740 return -ENOMEM;
741
742 strncpy(dirs->dirname, filename, PATH_MAX - 1);
743 dirs->offset = -1;
744
745 *dirsp = &dirs->fs_dirs;
746
747 return 0;
748 }
749
exfat_fs_readdir(struct fs_dir_stream * fs_dirs,struct fs_dirent ** dentp)750 int 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;
755 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 }
768
769 /* Emulate current directory ./ */
770 if (dirs->offset == -1) {
771 dirs->offset++;
772 snprintf(dent->name, FS_DIRENT_NAME_LEN, ".");
773 dent->type = FS_DT_DIR;
774 *dentp = dent;
775 goto err_out;
776 }
777
778 /* Emulate parent directory ../ */
779 if (dirs->offset == 0) {
780 dirs->offset++;
781 snprintf(dent->name, FS_DIRENT_NAME_LEN, "..");
782 dent->type = FS_DT_DIR;
783 *dentp = dent;
784 goto err_out;
785 }
786
787 err = exfat_opendir(&ctxt.ef, dnode, &it);
788 if (err)
789 goto err_out;
790
791 *dentp = NULL;
792
793 /* Read actual directory content */
794 while ((node = exfat_readdir(&it))) {
795 if (dirs->offset != ++offset) {
796 exfat_put_node(&ctxt.ef, node);
797 continue;
798 }
799
800 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;
811 }
812
813 exfat_closedir(&ctxt.ef, &it);
814
815 err_out:
816 exfat_put_node(&ctxt.ef, dnode);
817 return err;
818 }
819
exfat_fs_closedir(struct fs_dir_stream * fs_dirs)820 void exfat_fs_closedir(struct fs_dir_stream *fs_dirs)
821 {
822 struct exfat_dir_stream *dirs =
823 container_of(fs_dirs, struct exfat_dir_stream, fs_dirs);
824
825 free(dirs);
826 }
827
exfat_fs_ls(const char * dirname)828 int 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
869 err_out:
870 exfat_put_node(&ctxt.ef, dnode);
871 return err;
872 }
873
exfat_fs_exists(const char * filename)874 int 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)
881 return 0;
882
883 exfat_put_node(&ctxt.ef, node);
884
885 return 1;
886 }
887
exfat_fs_size(const char * filename,loff_t * size)888 int 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
exfat_fs_read(const char * filename,void * buf,loff_t offset,loff_t len,loff_t * actread)904 int 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
927 err = exfat_flush_node(&ctxt.ef, node);
928 exit:
929 exfat_put_node(&ctxt.ef, node);
930 return err;
931 }
932
exfat_fs_unlink(const char * filename)933 int 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);
958 exit:
959 exfat_put_node(&ctxt.ef, node);
960 return err;
961 }
962
exfat_fs_mkdir(const char * dirname)963 int 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
exfat_fs_write(const char * filename,void * buf,loff_t offset,loff_t len,loff_t * actwrite)971 int 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);
1014 exit:
1015 exfat_put_node(&ctxt.ef, node);
1016 return err;
1017 }
1018
exfat_fs_rename(const char * old_path,const char * new_path)1019 int exfat_fs_rename(const char *old_path, const char *new_path)
1020 {
1021 return exfat_rename(&ctxt.ef, old_path, new_path);
1022 }
1023
exfat_fs_close(void)1024 void exfat_fs_close(void)
1025 {
1026 exfat_unmount(&ctxt.ef);
1027 ctxt.cur_dev = NULL;
1028 }
1029 #endif /* __U_BOOT__ */
1030