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