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