1 /***************************************************************************
2 * Copyright (C) 2015
3 * by Dimok
4 *
5 * This software is provided 'as-is', without any express or implied
6 * warranty. In no event will the authors be held liable for any
7 * damages arising from the use of this software.
8 *
9 * Permission is granted to anyone to use this software for any
10 * purpose, including commercial applications, and to alter it and
11 * redistribute it freely, subject to the following restrictions:
12 *
13 * 1. The origin of this software must not be misrepresented; you
14 * must not claim that you wrote the original software. If you use
15 * this software in a product, an acknowledgment in the product
16 * documentation would be appreciated but is not required.
17 *
18 * 2. Altered source versions must be plainly marked as such, and
19 * must not be misrepresented as being the original software.
20 *
21 * 3. This notice may not be removed or altered from any source
22 * distribution.
23 ***************************************************************************/
24 #include <errno.h>
25 #include <sys/statvfs.h>
26 #include <sys/dirent.h>
27 #include <sys/iosupport.h>
28 #include <string.h>
29 #include <malloc.h>
30 #include <fcntl.h>
31 #include <stdio.h>
32 #include "fs_utils.h"
33 #include <wiiu/os/mutex.h>
34 #include <wiiu/fs.h>
35
36 #define FS_ALIGNMENT 0x40
37 #define FS_ALIGN(x) (((x) + FS_ALIGNMENT - 1) & ~(FS_ALIGNMENT - 1))
38
39 typedef struct __attribute__((packed))
40 {
41 uint32_t flag;
42 uint32_t permission;
43 uint32_t owner_id;
44 uint32_t group_id;
45 uint32_t size;
46 uint32_t alloc_size;
47 uint64_t quota_size;
48 uint32_t ent_id;
49 uint64_t ctime;
50 uint64_t mtime;
51 uint8_t attributes[48];
52 } FSStat__;
53
54 typedef struct
55 {
56 FSStat__ stat;
57 char name[256];
58 } FSDirEntry;
59
60 typedef struct _sd_fat_private_t {
61 char *mount_path;
62 void *pClient;
63 void *pCmd;
64 void *pMutex;
65 } sd_fat_private_t;
66
67 typedef struct _sd_fat_file_state_t {
68 sd_fat_private_t *dev;
69 int fd; /* File descriptor */
70 int flags; /* Opening flags */
71 bool read; /* True if allowed to read from file */
72 bool write; /* True if allowed to write to file */
73 bool append; /* True if allowed to append to file */
74 u64 pos; /* Current position within the file (in bytes) */
75 u64 len; /* Total length of the file (in bytes) */
76 struct _sd_fat_file_state_t *prevOpenFile; /* The previous entry in a double-linked FILO list of open files */
77 struct _sd_fat_file_state_t *nextOpenFile; /* The next entry in a double-linked FILO list of open files */
78 } sd_fat_file_state_t;
79
80 typedef struct _sd_fat_dir_entry_t {
81 sd_fat_private_t *dev;
82 int dirHandle;
83 } sd_fat_dir_entry_t;
84
sd_fat_get_device_data(const char * path)85 static sd_fat_private_t *sd_fat_get_device_data(const char *path)
86 {
87 const devoptab_t *devoptab = NULL;
88 char name[128] = {0};
89 int i;
90
91 /* Get the device name from the path */
92 strncpy(name, path, 127);
93 strtok(name, ":/");
94
95 /* Search the devoptab table for the specified device name */
96 /* NOTE: We do this manually due to a 'bug' in GetDeviceOpTab */
97 /* which ignores names with suffixes and causes names */
98 /* like "ntfs" and "ntfs1" to be seen as equals */
99 for (i = 3; i < STD_MAX; i++) {
100 devoptab = devoptab_list[i];
101 if (devoptab && devoptab->name) {
102 if (strcmp(name, devoptab->name) == 0) {
103 return (sd_fat_private_t *)devoptab->deviceData;
104 }
105 }
106 }
107
108 return NULL;
109 }
110
sd_fat_real_path(const char * path,sd_fat_private_t * dev)111 static char *sd_fat_real_path (const char *path, sd_fat_private_t *dev)
112 {
113 /* Sanity check */
114 if (!path)
115 return NULL;
116
117 /* Move the path pointer to the start of the actual path */
118 if (strchr(path, ':') != NULL) {
119 path = strchr(path, ':') + 1;
120 }
121
122 int mount_len = strlen(dev->mount_path);
123
124 char *new_name = (char*)malloc(mount_len + strlen(path) + 1);
125 if(new_name) {
126 strcpy(new_name, dev->mount_path);
127 strcpy(new_name + mount_len, path);
128 return new_name;
129 }
130 return new_name;
131 }
132
sd_fat_open_r(struct _reent * r,void * fileStruct,const char * path,int flags,int mode)133 static int sd_fat_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode)
134 {
135 sd_fat_private_t *dev = sd_fat_get_device_data(path);
136 if(!dev) {
137 r->_errno = ENODEV;
138 return -1;
139 }
140
141 sd_fat_file_state_t *file = (sd_fat_file_state_t *)fileStruct;
142
143 file->dev = dev;
144 /* Determine which mode the file is opened for */
145 file->flags = flags;
146
147 const char *mode_str;
148
149 if ((flags & 0x03) == O_RDONLY) {
150 file->read = true;
151 file->write = false;
152 file->append = false;
153 mode_str = "r";
154 } else if ((flags & 0x03) == O_WRONLY) {
155 file->read = false;
156 file->write = true;
157 file->append = (flags & O_APPEND);
158 mode_str = file->append ? "a" : "w";
159 } else if ((flags & 0x03) == O_RDWR) {
160 file->read = true;
161 file->write = true;
162 file->append = (flags & O_APPEND);
163 mode_str = file->append ? "a+" : "r+";
164 } else {
165 r->_errno = EACCES;
166 return -1;
167 }
168
169 int fd = -1;
170
171 OSLockMutex(dev->pMutex);
172
173 char *real_path = sd_fat_real_path(path, dev);
174 if(!path) {
175 r->_errno = ENOMEM;
176 OSUnlockMutex(dev->pMutex);
177 return -1;
178 }
179
180 int result = FSOpenFile(dev->pClient, dev->pCmd, real_path, mode_str, (FSFileHandle*)&fd, -1);
181
182 free(real_path);
183
184 if(result == 0)
185 {
186 FSStat stats;
187 result = FSGetStatFile(dev->pClient, dev->pCmd, fd, &stats, -1);
188 if(result != 0) {
189 FSCloseFile(dev->pClient, dev->pCmd, fd, -1);
190 r->_errno = result;
191 OSUnlockMutex(dev->pMutex);
192 return -1;
193 }
194 file->fd = fd;
195 file->pos = 0;
196 file->len = stats.size;
197 OSUnlockMutex(dev->pMutex);
198 return (int)file;
199 }
200
201 r->_errno = result;
202 OSUnlockMutex(dev->pMutex);
203 return -1;
204 }
205
sd_fat_close_r(struct _reent * r,void * fd)206 static int sd_fat_close_r (struct _reent *r, void* fd)
207 {
208 sd_fat_file_state_t *file = (sd_fat_file_state_t *)fd;
209 if(!file->dev) {
210 r->_errno = ENODEV;
211 return -1;
212 }
213
214 OSLockMutex(file->dev->pMutex);
215
216 int result = FSCloseFile(file->dev->pClient, file->dev->pCmd, file->fd, -1);
217
218 OSUnlockMutex(file->dev->pMutex);
219
220 if(result < 0)
221 {
222 r->_errno = result;
223 return -1;
224 }
225 return 0;
226 }
227
sd_fat_seek_r(struct _reent * r,void * fd,off_t pos,int dir)228 static off_t sd_fat_seek_r (struct _reent *r, void* fd, off_t pos, int dir)
229 {
230 sd_fat_file_state_t *file = (sd_fat_file_state_t *)fd;
231 if(!file->dev) {
232 r->_errno = ENODEV;
233 return 0;
234 }
235
236 OSLockMutex(file->dev->pMutex);
237
238 switch(dir)
239 {
240 case SEEK_SET:
241 file->pos = pos;
242 break;
243 case SEEK_CUR:
244 file->pos += pos;
245 break;
246 case SEEK_END:
247 file->pos = file->len + pos;
248 break;
249 default:
250 r->_errno = EINVAL;
251 return -1;
252 }
253
254 int result = FSSetPosFile(file->dev->pClient, file->dev->pCmd, file->fd, file->pos, -1);
255
256 OSUnlockMutex(file->dev->pMutex);
257
258 if(result == 0)
259 {
260 return file->pos;
261 }
262
263 return result;
264 }
265
sd_fat_write_r(struct _reent * r,void * fd,const char * ptr,size_t len)266 static ssize_t sd_fat_write_r (struct _reent *r, void* fd, const char *ptr, size_t len)
267 {
268 sd_fat_file_state_t *file = (sd_fat_file_state_t *)fd;
269 if(!file->dev) {
270 r->_errno = ENODEV;
271 return 0;
272 }
273
274 if(!file->write)
275 {
276 r->_errno = EACCES;
277 return 0;
278 }
279
280 OSLockMutex(file->dev->pMutex);
281
282 size_t done = 0;
283
284 /* fast path: buffer is already correctly aligned */
285 if (!((uintptr_t)ptr & (FS_ALIGNMENT-1))) {
286 int result = FSWriteFile(file->dev->pClient, file->dev->pCmd, (uint8_t*)ptr, 1, len, file->fd, 0, -1);
287 if(result < 0) {
288 r->_errno = result;
289 } else {
290 done = result;
291 file->pos += result;
292 }
293 } else {
294 size_t len_aligned = FS_ALIGN(len);
295 if(len_aligned > 0x4000)
296 len_aligned = 0x4000;
297
298 unsigned char *tmpBuf = (unsigned char *)memalign(FS_ALIGNMENT, len_aligned);
299 if(!tmpBuf) {
300 r->_errno = ENOMEM;
301 OSUnlockMutex(file->dev->pMutex);
302 return 0;
303 }
304
305 while(done < len)
306 {
307 size_t write_size = (len_aligned < (len - done)) ? len_aligned : (len - done);
308 memcpy(tmpBuf, ptr + done, write_size);
309
310 int result = FSWriteFile(file->dev->pClient, file->dev->pCmd, tmpBuf, 0x01, write_size, file->fd, 0, -1);
311 if(result < 0)
312 {
313 r->_errno = result;
314 break;
315 }
316 else if(result == 0)
317 {
318 if(write_size > 0)
319 done = 0;
320 break;
321 }
322 else
323 {
324 done += result;
325 file->pos += result;
326 }
327 }
328
329 free(tmpBuf);
330 }
331
332 OSUnlockMutex(file->dev->pMutex);
333 return done;
334 }
335
sd_fat_read_r(struct _reent * r,void * fd,char * ptr,size_t len)336 static ssize_t sd_fat_read_r (struct _reent *r, void* fd, char *ptr, size_t len)
337 {
338 sd_fat_file_state_t *file = (sd_fat_file_state_t *)fd;
339 if(!file->dev) {
340 r->_errno = ENODEV;
341 return 0;
342 }
343
344 if(!file->read)
345 {
346 r->_errno = EACCES;
347 return 0;
348 }
349
350 OSLockMutex(file->dev->pMutex);
351
352 size_t done = 0;
353
354 /* fast path: buffer is already correctly aligned */
355 if (!((uintptr_t)ptr & (FS_ALIGNMENT-1))) {
356 int result = FSReadFile(file->dev->pClient, file->dev->pCmd, (uint8_t*)ptr, 1, len, file->fd, 0, -1);
357 if(result < 0) {
358 r->_errno = result;
359 } else {
360 done = result;
361 file->pos += result;
362 }
363 } else {
364 size_t len_aligned = FS_ALIGN(len);
365 if(len_aligned > 0x4000)
366 len_aligned = 0x4000;
367
368 unsigned char *tmpBuf = (unsigned char *)memalign(FS_ALIGNMENT, len_aligned);
369 if(!tmpBuf) {
370 r->_errno = ENOMEM;
371 OSUnlockMutex(file->dev->pMutex);
372 return 0;
373 }
374
375 while(done < len)
376 {
377 size_t read_size = (len_aligned < (len - done)) ? len_aligned : (len - done);
378
379 int result = FSReadFile(file->dev->pClient, file->dev->pCmd, tmpBuf, 0x01, read_size, file->fd, 0, -1);
380 if(result < 0)
381 {
382 r->_errno = result;
383 done = 0;
384 break;
385 }
386 else if(result == 0)
387 {
388 /*! TODO: error on read_size > 0 */
389 break;
390 }
391 else
392 {
393 memcpy(ptr + done, tmpBuf, read_size);
394 done += result;
395 file->pos += result;
396 }
397 }
398
399 free(tmpBuf);
400 }
401
402 OSUnlockMutex(file->dev->pMutex);
403 return done;
404 }
405
sd_fat_fstat_r(struct _reent * r,void * fd,struct stat * st)406 static int sd_fat_fstat_r (struct _reent *r, void* fd, struct stat *st)
407 {
408 sd_fat_file_state_t *file = (sd_fat_file_state_t *)fd;
409 if(!file->dev)
410 {
411 r->_errno = ENODEV;
412 return -1;
413 }
414
415 OSLockMutex(file->dev->pMutex);
416
417 /* Zero out the stat buffer */
418 memset(st, 0, sizeof(struct stat));
419
420 FSStat__ stats;
421 int result = FSGetStatFile(file->dev->pClient, file->dev->pCmd, file->fd, (FSStat*)&stats, -1);
422 if(result != 0) {
423 r->_errno = result;
424 OSUnlockMutex(file->dev->pMutex);
425 return -1;
426 }
427
428 st->st_mode = S_IFREG;
429 st->st_size = stats.size;
430 st->st_blocks = (stats.size + 511) >> 9;
431 st->st_nlink = 1;
432
433 /* Fill in the generic entry stats */
434 st->st_dev = stats.ent_id;
435 st->st_uid = stats.owner_id;
436 st->st_gid = stats.group_id;
437 st->st_ino = stats.ent_id;
438 st->st_atime = stats.mtime;
439 st->st_ctime = stats.ctime;
440 st->st_mtime = stats.mtime;
441 OSUnlockMutex(file->dev->pMutex);
442 return 0;
443 }
444
sd_fat_ftruncate_r(struct _reent * r,void * fd,off_t len)445 static int sd_fat_ftruncate_r (struct _reent *r, void* fd, off_t len)
446 {
447 sd_fat_file_state_t *file = (sd_fat_file_state_t *)fd;
448 if(!file->dev) {
449 r->_errno = ENODEV;
450 return -1;
451 }
452
453 OSLockMutex(file->dev->pMutex);
454
455 int result = FSTruncateFile(file->dev->pClient, file->dev->pCmd, file->fd, -1);
456
457 OSUnlockMutex(file->dev->pMutex);
458
459 if(result < 0) {
460 r->_errno = result;
461 return -1;
462 }
463
464 return 0;
465 }
466
sd_fat_fsync_r(struct _reent * r,void * fd)467 static int sd_fat_fsync_r (struct _reent *r, void* fd)
468 {
469 sd_fat_file_state_t *file = (sd_fat_file_state_t *)fd;
470 if(!file->dev) {
471 r->_errno = ENODEV;
472 return -1;
473 }
474
475 OSLockMutex(file->dev->pMutex);
476
477 int result = FSFlushFile(file->dev->pClient, file->dev->pCmd, file->fd, -1);
478
479 OSUnlockMutex(file->dev->pMutex);
480
481 if(result < 0) {
482 r->_errno = result;
483 return -1;
484 }
485
486 return 0;
487 }
488
sd_fat_stat_r(struct _reent * r,const char * path,struct stat * st)489 static int sd_fat_stat_r (struct _reent *r, const char *path, struct stat *st)
490 {
491 sd_fat_private_t *dev = sd_fat_get_device_data(path);
492 if(!dev) {
493 r->_errno = ENODEV;
494 return -1;
495 }
496
497 OSLockMutex(dev->pMutex);
498
499 /* Zero out the stat buffer */
500 memset(st, 0, sizeof(struct stat));
501
502 char *real_path = sd_fat_real_path(path, dev);
503 if(!real_path) {
504 r->_errno = ENOMEM;
505 OSUnlockMutex(dev->pMutex);
506 return -1;
507 }
508
509 FSStat__ stats;
510
511 int result = FSGetStat(dev->pClient, dev->pCmd, real_path, (FSStat*)&stats, -1);
512
513 free(real_path);
514
515 if(result < 0) {
516 r->_errno = result;
517 OSUnlockMutex(dev->pMutex);
518 return -1;
519 }
520
521 /* mark root also as directory */
522 st->st_mode = ((stats.flag & 0x80000000) || (strlen(dev->mount_path) + 1 == strlen(real_path)))? S_IFDIR : S_IFREG;
523 st->st_nlink = 1;
524 st->st_size = stats.size;
525 st->st_blocks = (stats.size + 511) >> 9;
526 /* Fill in the generic entry stats */
527 st->st_dev = stats.ent_id;
528 st->st_uid = stats.owner_id;
529 st->st_gid = stats.group_id;
530 st->st_ino = stats.ent_id;
531 st->st_atime = stats.mtime;
532 st->st_ctime = stats.ctime;
533 st->st_mtime = stats.mtime;
534
535 OSUnlockMutex(dev->pMutex);
536
537 return 0;
538 }
539
sd_fat_link_r(struct _reent * r,const char * existing,const char * newLink)540 static int sd_fat_link_r (struct _reent *r, const char *existing, const char *newLink)
541 {
542 r->_errno = ENOTSUP;
543 return -1;
544 }
545
sd_fat_unlink_r(struct _reent * r,const char * name)546 static int sd_fat_unlink_r (struct _reent *r, const char *name)
547 {
548 sd_fat_private_t *dev = sd_fat_get_device_data(name);
549 if(!dev) {
550 r->_errno = ENODEV;
551 return -1;
552 }
553
554 OSLockMutex(dev->pMutex);
555
556 char *real_path = sd_fat_real_path(name, dev);
557 if(!real_path) {
558 r->_errno = ENOMEM;
559 OSUnlockMutex(dev->pMutex);
560 return -1;
561 }
562
563 int result = FSRemove(dev->pClient, dev->pCmd, real_path, -1);
564
565 free(real_path);
566
567 OSUnlockMutex(dev->pMutex);
568
569 if(result < 0) {
570 r->_errno = result;
571 return -1;
572 }
573
574 return 0;
575 }
576
sd_fat_chdir_r(struct _reent * r,const char * name)577 static int sd_fat_chdir_r (struct _reent *r, const char *name)
578 {
579 sd_fat_private_t *dev = sd_fat_get_device_data(name);
580 if(!dev) {
581 r->_errno = ENODEV;
582 return -1;
583 }
584
585 OSLockMutex(dev->pMutex);
586
587 char *real_path = sd_fat_real_path(name, dev);
588 if(!real_path) {
589 r->_errno = ENOMEM;
590 OSUnlockMutex(dev->pMutex);
591 return -1;
592 }
593
594 int result = FSChangeDir(dev->pClient, dev->pCmd, real_path, -1);
595
596 free(real_path);
597
598 OSUnlockMutex(dev->pMutex);
599
600 if(result < 0) {
601 r->_errno = result;
602 return -1;
603 }
604
605 return 0;
606 }
607
sd_fat_rename_r(struct _reent * r,const char * oldName,const char * newName)608 static int sd_fat_rename_r (struct _reent *r, const char *oldName, const char *newName)
609 {
610 sd_fat_private_t *dev = sd_fat_get_device_data(oldName);
611 if(!dev) {
612 r->_errno = ENODEV;
613 return -1;
614 }
615
616 OSLockMutex(dev->pMutex);
617
618 char *real_oldpath = sd_fat_real_path(oldName, dev);
619 if(!real_oldpath) {
620 r->_errno = ENOMEM;
621 OSUnlockMutex(dev->pMutex);
622 return -1;
623 }
624 char *real_newpath = sd_fat_real_path(newName, dev);
625 if(!real_newpath) {
626 r->_errno = ENOMEM;
627 free(real_oldpath);
628 OSUnlockMutex(dev->pMutex);
629 return -1;
630 }
631
632 int result = FSRename(dev->pClient, dev->pCmd, real_oldpath, real_newpath, -1);
633
634 free(real_oldpath);
635 free(real_newpath);
636
637 OSUnlockMutex(dev->pMutex);
638
639 if(result < 0) {
640 r->_errno = result;
641 return -1;
642 }
643
644 return 0;
645
646 }
647
sd_fat_mkdir_r(struct _reent * r,const char * path,int mode)648 static int sd_fat_mkdir_r (struct _reent *r, const char *path, int mode)
649 {
650 sd_fat_private_t *dev = sd_fat_get_device_data(path);
651 if(!dev) {
652 r->_errno = ENODEV;
653 return -1;
654 }
655
656 OSLockMutex(dev->pMutex);
657
658 char *real_path = sd_fat_real_path(path, dev);
659 if(!real_path) {
660 r->_errno = ENOMEM;
661 OSUnlockMutex(dev->pMutex);
662 return -1;
663 }
664
665 int result = FSMakeDir(dev->pClient, dev->pCmd, real_path, -1);
666
667 free(real_path);
668
669 OSUnlockMutex(dev->pMutex);
670
671 if(result < 0) {
672 r->_errno = result;
673 return -1;
674 }
675
676 return 0;
677 }
678
sd_fat_statvfs_r(struct _reent * r,const char * path,struct statvfs * buf)679 static int sd_fat_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf)
680 {
681 sd_fat_private_t *dev = sd_fat_get_device_data(path);
682 if(!dev) {
683 r->_errno = ENODEV;
684 return -1;
685 }
686
687 OSLockMutex(dev->pMutex);
688
689 /* Zero out the stat buffer */
690 memset(buf, 0, sizeof(struct statvfs));
691
692 char *real_path = sd_fat_real_path(path, dev);
693 if(!real_path) {
694 r->_errno = ENOMEM;
695 OSUnlockMutex(dev->pMutex);
696 return -1;
697 }
698
699 u64 size;
700
701 int result = FSGetFreeSpaceSize(dev->pClient, dev->pCmd, real_path, &size, -1);
702
703 free(real_path);
704
705 if(result < 0) {
706 r->_errno = result;
707 OSUnlockMutex(dev->pMutex);
708 return -1;
709 }
710
711 /* File system block size */
712 buf->f_bsize = 512;
713
714 /* Fundamental file system block size */
715 buf->f_frsize = 512;
716
717 /* Total number of blocks on file system in units of f_frsize */
718 buf->f_blocks = size >> 9; /* this is unknown */
719
720 /* Free blocks available for all and for non-privileged processes */
721 buf->f_bfree = buf->f_bavail = size >> 9;
722
723 /* Number of inodes at this point in time */
724 buf->f_files = 0xffffffff;
725
726 /* Free inodes available for all and for non-privileged processes */
727 buf->f_ffree = 0xffffffff;
728
729 /* File system id */
730 buf->f_fsid = (int)dev;
731
732 /* Bit mask of f_flag values. */
733 buf->f_flag = 0;
734
735 /* Maximum length of filenames */
736 buf->f_namemax = 255;
737
738 OSUnlockMutex(dev->pMutex);
739
740 return 0;
741 }
742
sd_fat_diropen_r(struct _reent * r,DIR_ITER * dirState,const char * path)743 static DIR_ITER *sd_fat_diropen_r (struct _reent *r, DIR_ITER *dirState, const char *path)
744 {
745 sd_fat_private_t *dev = sd_fat_get_device_data(path);
746 if(!dev) {
747 r->_errno = ENODEV;
748 return NULL;
749 }
750
751 sd_fat_dir_entry_t *dirIter = (sd_fat_dir_entry_t *)dirState->dirStruct;
752
753 OSLockMutex(dev->pMutex);
754
755 char *real_path = sd_fat_real_path(path, dev);
756 if(!real_path) {
757 r->_errno = ENOMEM;
758 OSUnlockMutex(dev->pMutex);
759 return NULL;
760 }
761
762 int dirHandle;
763
764 int result = FSOpenDir(dev->pClient, dev->pCmd, real_path, (FSDirectoryHandle*)&dirHandle, -1);
765
766 free(real_path);
767
768 OSUnlockMutex(dev->pMutex);
769
770 if(result < 0)
771 {
772 r->_errno = result;
773 return NULL;
774 }
775
776 dirIter->dev = dev;
777 dirIter->dirHandle = dirHandle;
778
779 return dirState;
780 }
781
sd_fat_dirclose_r(struct _reent * r,DIR_ITER * dirState)782 static int sd_fat_dirclose_r (struct _reent *r, DIR_ITER *dirState)
783 {
784 sd_fat_dir_entry_t *dirIter = (sd_fat_dir_entry_t *)dirState->dirStruct;
785 if(!dirIter->dev) {
786 r->_errno = ENODEV;
787 return -1;
788 }
789
790 OSLockMutex(dirIter->dev->pMutex);
791
792 int result = FSCloseDir(dirIter->dev->pClient, dirIter->dev->pCmd, dirIter->dirHandle, -1);
793
794 OSUnlockMutex(dirIter->dev->pMutex);
795
796 if(result < 0)
797 {
798 r->_errno = result;
799 return -1;
800 }
801 return 0;
802 }
803
sd_fat_dirreset_r(struct _reent * r,DIR_ITER * dirState)804 static int sd_fat_dirreset_r (struct _reent *r, DIR_ITER *dirState)
805 {
806 sd_fat_dir_entry_t *dirIter = (sd_fat_dir_entry_t *)dirState->dirStruct;
807 if(!dirIter->dev) {
808 r->_errno = ENODEV;
809 return -1;
810 }
811
812 OSLockMutex(dirIter->dev->pMutex);
813
814 int result = FSRewindDir(dirIter->dev->pClient, dirIter->dev->pCmd, dirIter->dirHandle, -1);
815
816 OSUnlockMutex(dirIter->dev->pMutex);
817
818 if(result < 0)
819 {
820 r->_errno = result;
821 return -1;
822 }
823 return 0;
824 }
825
sd_fat_dirnext_r(struct _reent * r,DIR_ITER * dirState,char * filename,struct stat * st)826 static int sd_fat_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *st)
827 {
828 sd_fat_dir_entry_t *dirIter = (sd_fat_dir_entry_t *)dirState->dirStruct;
829 if(!dirIter->dev) {
830 r->_errno = ENODEV;
831 return -1;
832 }
833
834 OSLockMutex(dirIter->dev->pMutex);
835
836 FSDirEntry * dir_entry = malloc(sizeof(FSDirEntry));
837
838 int result = FSReadDir(dirIter->dev->pClient, dirIter->dev->pCmd, dirIter->dirHandle, (FSDirectoryEntry*)dir_entry, -1);
839 if(result < 0)
840 {
841 free(dir_entry);
842 r->_errno = result;
843 OSUnlockMutex(dirIter->dev->pMutex);
844 return -1;
845 }
846
847 /* Fetch the current entry */
848 strcpy(filename, dir_entry->name);
849
850 if(st)
851 {
852 memset(st, 0, sizeof(struct stat));
853 st->st_mode = (dir_entry->stat.flag & 0x80000000) ? S_IFDIR : S_IFREG;
854 st->st_nlink = 1;
855 st->st_size = dir_entry->stat.size;
856 st->st_blocks = (dir_entry->stat.size + 511) >> 9;
857 st->st_dev = dir_entry->stat.ent_id;
858 st->st_uid = dir_entry->stat.owner_id;
859 st->st_gid = dir_entry->stat.group_id;
860 st->st_ino = dir_entry->stat.ent_id;
861 st->st_atime = dir_entry->stat.mtime;
862 st->st_ctime = dir_entry->stat.ctime;
863 st->st_mtime = dir_entry->stat.mtime;
864 }
865
866 free(dir_entry);
867 OSUnlockMutex(dirIter->dev->pMutex);
868 return 0;
869 }
870
871 /* NTFS device driver devoptab */
872 static const devoptab_t devops_sd_fat = {
873 NULL, /* Device name */
874 sizeof (sd_fat_file_state_t),
875 sd_fat_open_r,
876 sd_fat_close_r,
877 sd_fat_write_r,
878 sd_fat_read_r,
879 sd_fat_seek_r,
880 sd_fat_fstat_r,
881 sd_fat_stat_r,
882 sd_fat_link_r,
883 sd_fat_unlink_r,
884 sd_fat_chdir_r,
885 sd_fat_rename_r,
886 sd_fat_mkdir_r,
887 sizeof (sd_fat_dir_entry_t),
888 sd_fat_diropen_r,
889 sd_fat_dirreset_r,
890 sd_fat_dirnext_r,
891 sd_fat_dirclose_r,
892 sd_fat_statvfs_r,
893 sd_fat_ftruncate_r,
894 sd_fat_fsync_r,
895 NULL, /* sd_fat_chmod_r */
896 NULL, /* sd_fat_fchmod_r */
897 NULL /* Device data */
898 };
899
sd_fat_add_device(const char * name,const char * mount_path,void * pClient,void * pCmd)900 static int sd_fat_add_device (const char *name, const char *mount_path, void *pClient, void *pCmd)
901 {
902 devoptab_t *dev = NULL;
903 char *devname = NULL;
904 char *devpath = NULL;
905 int i;
906
907 /* Sanity check */
908 if (!name) {
909 errno = EINVAL;
910 return -1;
911 }
912
913 /* Allocate a devoptab for this device */
914 dev = (devoptab_t *) malloc(sizeof(devoptab_t) + strlen(name) + 1);
915 if (!dev) {
916 errno = ENOMEM;
917 return -1;
918 }
919
920 /* Use the space allocated at the end of the devoptab for storing the device name */
921 devname = (char*)(dev + 1);
922 strcpy(devname, name);
923
924 /* create private data */
925 sd_fat_private_t *priv = (sd_fat_private_t *)malloc(sizeof(sd_fat_private_t) + strlen(mount_path) + 1);
926 if(!priv) {
927 free(dev);
928 errno = ENOMEM;
929 return -1;
930 }
931
932 devpath = (char*)(priv+1);
933 strcpy(devpath, mount_path);
934
935 /* setup private data */
936 priv->mount_path = devpath;
937 priv->pClient = pClient;
938 priv->pCmd = pCmd;
939 priv->pMutex = malloc(sizeof(OSMutex));
940
941 if(!priv->pMutex) {
942 free(dev);
943 free(priv);
944 errno = ENOMEM;
945 return -1;
946 }
947
948 OSInitMutex(priv->pMutex);
949
950 /* Setup the devoptab */
951 memcpy(dev, &devops_sd_fat, sizeof(devoptab_t));
952 dev->name = devname;
953 dev->deviceData = priv;
954
955 /* Add the device to the devoptab table (if there is a free slot) */
956 for (i = 3; i < STD_MAX; i++) {
957 if (devoptab_list[i] == devoptab_list[0]) {
958 devoptab_list[i] = dev;
959 return 0;
960 }
961 }
962
963 /* failure, free all memory */
964 free(priv);
965 free(dev);
966
967 /* If we reach here then there are no free slots in the devoptab table for this device */
968 errno = EADDRNOTAVAIL;
969 return -1;
970 }
971
sd_fat_remove_device(const char * path,void ** pClient,void ** pCmd,char ** mountPath)972 static int sd_fat_remove_device (const char *path, void **pClient, void **pCmd, char **mountPath)
973 {
974 const devoptab_t *devoptab = NULL;
975 char name[128] = {0};
976 int i;
977
978 /* Get the device name from the path */
979 strncpy(name, path, 127);
980 strtok(name, ":/");
981
982 /* Find and remove the specified device from the devoptab table */
983 /* NOTE: We do this manually due to a 'bug' in RemoveDevice */
984 /* which ignores names with suffixes and causes names */
985 /* like "ntfs" and "ntfs1" to be seen as equals */
986 for (i = 3; i < STD_MAX; i++) {
987 devoptab = devoptab_list[i];
988 if (devoptab && devoptab->name) {
989 if (strcmp(name, devoptab->name) == 0) {
990 devoptab_list[i] = devoptab_list[0];
991
992 if(devoptab->deviceData)
993 {
994 sd_fat_private_t *priv = (sd_fat_private_t *)devoptab->deviceData;
995 *pClient = priv->pClient;
996 *pCmd = priv->pCmd;
997 *mountPath = (char*) malloc(strlen(priv->mount_path)+1);
998 if(*mountPath)
999 strcpy(*mountPath, priv->mount_path);
1000 if(priv->pMutex)
1001 free(priv->pMutex);
1002 free(devoptab->deviceData);
1003 }
1004
1005 free((devoptab_t*)devoptab);
1006 return 0;
1007 }
1008 }
1009 }
1010
1011 return -1;
1012 }
1013
mount_sd_fat(const char * path)1014 int mount_sd_fat(const char *path)
1015 {
1016 int result = -1;
1017
1018 /* get command and client */
1019 void* pClient = malloc(sizeof(FSClient));
1020 void* pCmd = malloc(sizeof(FSCmdBlock));
1021
1022 if(!pClient || !pCmd) {
1023 /* just in case free if not 0 */
1024 if(pClient)
1025 free(pClient);
1026 if(pCmd)
1027 free(pCmd);
1028 return -2;
1029 }
1030
1031 FSInit();
1032 FSInitCmdBlock(pCmd);
1033 FSAddClient(pClient, -1);
1034
1035 char *mountPath = NULL;
1036
1037 if(MountFS(pClient, pCmd, &mountPath) == 0) {
1038 result = sd_fat_add_device(path, mountPath, pClient, pCmd);
1039 free(mountPath);
1040 }
1041
1042 return result;
1043 }
1044
unmount_sd_fat(const char * path)1045 int unmount_sd_fat(const char *path)
1046 {
1047 void *pClient = 0;
1048 void *pCmd = 0;
1049 char *mountPath = 0;
1050
1051 int result = sd_fat_remove_device(path, &pClient, &pCmd, &mountPath);
1052 if(result == 0)
1053 {
1054 UmountFS(pClient, pCmd, mountPath);
1055 FSDelClient(pClient, -1);
1056 free(pClient);
1057 free(pCmd);
1058 free(mountPath);
1059 /* FSShutdown(); */
1060 }
1061 return result;
1062 }
1063