1 //---------------------------------------------------------------------------
2 //File name: fs_driver.c
3 //---------------------------------------------------------------------------
5 * fat_driver.c - USB Mass storage driver for PS2
7 * (C) 2004, Marek Olejnik (ole00@post.cz)
8 * (C) 2004 Hermes (support for sector sizes from 512 to 4096 bytes)
9 * (C) 2004 raipsu (fs_dopen, fs_dclose, fs_dread, fs_getstat implementation)
11 * FAT filesystem layer
13 * See the file LICENSE included with this distribution for licensing terms.
15 //---------------------------------------------------------------------------
19 #include <io_common.h>
32 #define malloc(a) AllocSysMemory(0,(a), NULL)
33 #define free(a) FreeSysMemory((a))
36 #include "usbhd_common.h"
37 #include "fat_driver.h"
38 #include "fat_write.h"
41 //#define DEBUG //comment out this line when not debugging
43 #include "mass_debug.h"
44 #include "mass_stor.h"
46 #define IOCTL_CKFREE 0xBABEC0DE //dlanor: Ioctl request code => Check free space
47 #define IOCTL_RENAME 0xFEEDC0DE //dlanor: Ioctl request code => Rename
48 #define IOCTL_GETCLUSTER 0xBEEFC0DE //jimmikaelkael: Ioctl request code => get file start cluster
49 #define IOCTL_GETDEVSECTORSIZE 0xDEADC0DE //jimmikaelkael: Ioctl request code => get mass storage device sector size
50 #define IOCTL_CHECKCHAIN 0xCAFEC0DE //polo: Ioctl request code => Check cluster chain
52 #define FLUSH_SECTORS fat_flushSectors
54 typedef struct _fs_rec
{
56 //This flag is always 1 for a file, and always 0 for a folder (different typedef)
57 //Routines that handle both must test it, and then typecast the privdata pointer
58 //to the type that is appropriate for the given case. (see also fs_dir typedef)
60 int mode
; //file open mode
61 unsigned int sfnSector
; //short filename sector - write support
62 int sfnOffset
; //short filename offset - write support
63 int sizeChange
; //flag
67 typedef struct _fs_dir
{
69 //This flag is always 1 for a file, and always 0 for a folder (different typedef)
70 //Routines that handle both must test it, and then typecast the privdata pointer
71 //to the type that is appropriate for the given case. (see also fs_rec typedef)
74 fat_dir_list fatdlist
;
78 fs_rec fsRec
[MAX_FILES
]; //file info record
81 static iop_device_t fs_driver
;
82 static iop_device_ops_t fs_functarray
;
85 void fillStat(fio_stat_t
*stat
, fat_dir
*fatdir
)
87 stat
->mode
= FIO_SO_IROTH
| FIO_SO_IXOTH
;
88 if (fatdir
->attr
& FAT_ATTR_DIRECTORY
) {
89 stat
->mode
|= FIO_SO_IFDIR
;
91 stat
->mode
|= FIO_SO_IFREG
;
93 if (!(fatdir
->attr
& FAT_ATTR_READONLY
)) {
94 stat
->mode
|= FIO_SO_IWOTH
;
97 stat
->size
= fatdir
->size
;
99 //set created Date: Day, Month, Year
100 stat
->ctime
[4] = fatdir
->cdate
[0];
101 stat
->ctime
[5] = fatdir
->cdate
[1];
102 stat
->ctime
[6] = fatdir
->cdate
[2];
103 stat
->ctime
[7] = fatdir
->cdate
[3];
105 //set created Time: Hours, Minutes, Seconds
106 stat
->ctime
[3] = fatdir
->ctime
[0];
107 stat
->ctime
[2] = fatdir
->ctime
[1];
108 stat
->ctime
[1] = fatdir
->ctime
[2];
110 //set accessed Date: Day, Month, Year
111 stat
->atime
[4] = fatdir
->adate
[0];
112 stat
->atime
[5] = fatdir
->adate
[1];
113 stat
->atime
[6] = fatdir
->adate
[2];
114 stat
->atime
[7] = fatdir
->adate
[3];
116 //set modified Date: Day, Month, Year
117 stat
->mtime
[4] = fatdir
->mdate
[0];
118 stat
->mtime
[5] = fatdir
->mdate
[1];
119 stat
->mtime
[6] = fatdir
->mdate
[2];
120 stat
->mtime
[7] = fatdir
->mdate
[3];
122 //set modified Time: Hours, Minutes, Seconds
123 stat
->mtime
[3] = fatdir
->mtime
[0];
124 stat
->mtime
[2] = fatdir
->mtime
[1];
125 stat
->mtime
[1] = fatdir
->mtime
[2];
128 /*************************************************************************************/
129 /* File IO functions */
130 /*************************************************************************************/
137 //---------------------------------------------------------------------------
138 fs_rec
* fs_findFreeFileSlot(void) {
140 for (i
= 0; i
< MAX_FILES
; i
++) {
141 if (fsRec
[i
].file_flag
== -1) {
149 //---------------------------------------------------------------------------
150 fs_rec
* fs_findFileSlotByName(const char* name
) {
152 for (i
= 0; i
< MAX_FILES
; i
++) {
153 if (fsRec
[i
].file_flag
>= 0 && strEqual(fsRec
[i
].fatdir
.name
, (unsigned char*) name
) == 0) {
160 //---------------------------------------------------------------------------
161 static int _lock_sema_id
= 0;
163 //---------------------------------------------------------------------------
164 int _fs_init_lock(void)
173 if((_lock_sema_id
= CreateSema(&sp
)) < 0) { return(-1); }
179 //---------------------------------------------------------------------------
183 if(WaitSema(_lock_sema_id
) != _lock_sema_id
) { return(-1); }
188 //---------------------------------------------------------------------------
192 SignalSema(_lock_sema_id
);
197 //---------------------------------------------------------------------------
201 for (i
= 0; i
< MAX_FILES
; i
++) { fsRec
[i
].file_flag
= -1; }
203 if(_lock_sema_id
>= 0) { DeleteSema(_lock_sema_id
); }
208 //---------------------------------------------------------------------------
211 //---------------------------------------------------------------------------
217 //---------------------------------------------------------------------------
218 int fs_init(iop_device_t
*driver
)
220 if(fs_inited
) { return(1); }
228 //---------------------------------------------------------------------------
229 int fs_open(iop_file_t
* fd
, const char *name
, int mode
) {
233 unsigned int cluster
;
237 XPRINTF("USBHDFSD: fs_open called: %s mode=%X \n", name
, mode
) ;
239 fatd
= fat_getData(fd
->unit
);
240 if (fatd
== NULL
) { _fs_unlock(); return -ENODEV
; }
242 #ifndef WRITE_SUPPORT
244 if (mode
!= 0 && mode
!= O_RDONLY
) { //correct O_RDONLY number?
245 XPRINTF("USBHDFSD: mode (%d) != O_RDONLY (%d) \n", mode
, O_RDONLY
);
254 //check if the file is already open
255 rec2
= fs_findFileSlotByName(name
);
257 if ((mode
& O_WRONLY
) || //current file is opened for write
258 (rec2
->mode
& O_WRONLY
) ) {//other file is opened for write
265 //check if the slot is free
266 rec
= fs_findFreeFileSlot();
267 if (rec
== NULL
) { _fs_unlock(); return -EMFILE
; }
270 if((mode
& O_WRONLY
)) { //dlanor: corrected bad test condition
271 cluster
= 0; //start from root
274 if (mode
& O_CREAT
) {
275 XPRINTF("USBHDFSD: FAT I: O_CREAT detected!\n");
281 ret
= fat_createFile(fatd
, name
, 0, escapeNotExist
, &cluster
, &rec
->sfnSector
, &rec
->sfnOffset
);
286 //the file already exist but mode is set to truncate
287 if (ret
== 2 && (mode
& O_TRUNC
)) {
288 XPRINTF("USBHDFSD: FAT I: O_TRUNC detected!\n");
289 fat_truncateFile(fatd
, cluster
, rec
->sfnSector
, rec
->sfnOffset
);
295 cluster
= 0; //allways start from root
296 XPRINTF("USBHDFSD: Calling fat_getFileStartCluster from fs_open\n");
297 ret
= fat_getFileStartCluster(fatd
, name
, &cluster
, &rec
->fatdir
);
302 if ((rec
->fatdir
.attr
& FAT_ATTR_DIRECTORY
) == FAT_ATTR_DIRECTORY
) {
303 // Can't open a directory with fioOpen
314 if ((mode
& O_APPEND
) && (mode
& O_WRONLY
)) {
315 XPRINTF("USBHDFSD: FAT I: O_APPEND detected!\n");
316 rec
->filePos
= rec
->fatdir
.size
;
320 //store the slot to user parameters
327 //---------------------------------------------------------------------------
328 int fs_close(iop_file_t
* fd
) {
330 fs_rec
* rec
= (fs_rec
*)fd
->privdata
;
340 fatd
= fat_getData(fd
->unit
);
341 if (fatd
== NULL
) { _fs_unlock(); return -ENODEV
; }
344 if ((rec
->mode
& O_WRONLY
)) {
345 //update direntry size and time
346 if (rec
->sizeChange
) {
347 fat_updateSfn(fatd
, rec
->fatdir
.size
, rec
->sfnSector
, rec
->sfnOffset
);
358 //---------------------------------------------------------------------------
359 int fs_lseek(iop_file_t
* fd
, unsigned long offset
, int whence
) {
361 fs_rec
* rec
= (fs_rec
*)fd
->privdata
;
368 fatd
= fat_getData(fd
->unit
);
369 if (fatd
== NULL
) { _fs_unlock(); return -ENODEV
; }
373 rec
->filePos
= offset
;
376 rec
->filePos
+= offset
;
379 rec
->filePos
= rec
->fatdir
.size
+ offset
;
385 if (rec
->filePos
< 0) {
388 if (rec
->filePos
> rec
->fatdir
.size
) {
389 rec
->filePos
= rec
->fatdir
.size
;
396 //---------------------------------------------------------------------------
397 int fs_write(iop_file_t
* fd
, void * buffer
, int size
)
401 fs_rec
* rec
= (fs_rec
*)fd
->privdata
;
403 int updateClusterIndices
= 0;
410 fatd
= fat_getData(fd
->unit
);
411 if (fatd
== NULL
) { _fs_unlock(); return -ENODEV
; }
413 if (rec
->file_flag
!= 1) {
418 if (!(rec
->mode
& O_WRONLY
)) {
423 if (size
<= 0) { _fs_unlock(); return 0; }
425 result
= fat_writeFile(fatd
, &rec
->fatdir
, &updateClusterIndices
, rec
->filePos
, (unsigned char*) buffer
, size
);
426 if (result
> 0) { //write succesful
427 rec
->filePos
+= result
;
428 if (rec
->filePos
> rec
->fatdir
.size
) {
429 rec
->fatdir
.size
= rec
->filePos
;
431 //if new clusters allocated - then update file cluster indices
432 if (updateClusterIndices
) {
433 fat_setFatDirChain(fatd
, &rec
->fatdir
);
445 //---------------------------------------------------------------------------
446 int fs_read(iop_file_t
* fd
, void * buffer
, int size
) {
448 fs_rec
* rec
= (fs_rec
*)fd
->privdata
;
456 fatd
= fat_getData(fd
->unit
);
457 if (fatd
== NULL
) { _fs_unlock(); return -ENODEV
; }
459 if (rec
->file_flag
!= 1) {
464 if (!(rec
->mode
& O_RDONLY
)) {
474 if ((rec
->filePos
+size
) > rec
->fatdir
.size
) {
475 size
= rec
->fatdir
.size
- rec
->filePos
;
478 result
= fat_readFile(fatd
, &rec
->fatdir
, rec
->filePos
, (unsigned char*) buffer
, size
);
479 if (result
> 0) { //read succesful
480 rec
->filePos
+= result
;
488 //---------------------------------------------------------------------------
489 int getNameSignature(const char *name
) {
494 for (i
=0; name
[i
] != 0 ; i
++) ret
+= (name
[i
]*i
/2 + name
[i
]);
498 //---------------------------------------------------------------------------
504 iop_sys_clock_t clock
;
507 GetSystemTime(&clock
);
508 SysClock2USec(&clock
, &sec
, &usec
);
509 return (sec
*1000) + (usec
/1000);
513 //---------------------------------------------------------------------------
514 int fs_remove (iop_file_t
*fd
, const char *name
) {
522 fatd
= fat_getData(fd
->unit
);
526 removalResult
= result
;
531 rec
= fs_findFileSlotByName(name
);
532 //store filename signature and time of removal
533 nameSignature
= getNameSignature(name
);
534 removalTime
= getMillis();
536 //file is opened - can't delete the file
539 removalResult
= result
;
544 result
= fat_deleteFile(fatd
, name
, 0);
546 removalTime
= getMillis(); //update removal time
547 removalResult
= result
;
556 //---------------------------------------------------------------------------
557 int fs_mkdir (iop_file_t
*fd
, const char *name
) {
562 unsigned int sfnSector
;
563 unsigned int cluster
;
568 fatd
= fat_getData(fd
->unit
);
569 if (fatd
== NULL
) { _fs_unlock(); return -ENODEV
; }
571 XPRINTF("USBHDFSD: fs_mkdir: name=%s \n",name
);
572 //workaround for bug that invokes fioMkdir right after fioRemove
573 sig
= getNameSignature(name
);
574 millis
= getMillis();
575 if (sig
== nameSignature
&& (millis
- removalTime
) < 1000) {
577 return removalResult
; //return the original return code from fs_remove
580 ret
= fat_createFile(fatd
, name
, 1, 0, &cluster
, &sfnSector
, &sfnOffset
);
582 //directory of the same name already exist
595 //---------------------------------------------------------------------------
596 // NOTE! you MUST call fioRmdir with device number in the name
597 // or else this fs_rmdir function is never called.
598 // example: fioRmdir("mass:/dir1"); // <-- doesn't work (bug?)
599 // fioRmdir("mass0:/dir1"); // <-- works fine
600 int fs_rmdir (iop_file_t
*fd
, const char *name
) {
607 fatd
= fat_getData(fd
->unit
);
608 if (fatd
== NULL
) { _fs_unlock(); return -ENODEV
; }
610 ret
= fat_deleteFile(fatd
, name
, 1);
619 //---------------------------------------------------------------------------
620 int fs_dopen (iop_file_t
*fd
, const char *name
)
628 XPRINTF("USBHDFSD: fs_dopen called: unit %d name %s\n", fd
->unit
, name
);
630 fatd
= fat_getData(fd
->unit
);
631 if (fatd
== NULL
) { _fs_unlock(); return -ENODEV
; }
633 if( ((name
[0] == '/') && (name
[1] == '\0'))
634 ||((name
[0] == '/') && (name
[1] == '.') && (name
[2] == '\0')))
640 fd
->privdata
= (void*)malloc(sizeof(fs_dir
));
641 memset(fd
->privdata
, 0, sizeof(fs_dir
)); //NB: also implies "file_flag = 0;"
642 rec
= (fs_dir
*) fd
->privdata
;
644 rec
->status
= fat_getFirstDirentry(fatd
, (char*)name
, &rec
->fatdlist
, &rec
->fatdir
);
646 // root directory may have no entries, nothing else may.
647 if(rec
->status
== 0 && !is_root
)
648 rec
->status
= -EFAULT
;
657 //---------------------------------------------------------------------------
658 int fs_dclose (iop_file_t
*fd
)
660 if (fd
->privdata
== 0)
664 XPRINTF("USBHDFSD: fs_dclose called: unit %d\n", fd
->unit
);
671 //---------------------------------------------------------------------------
672 int fs_dread (iop_file_t
*fd
, fio_dirent_t
*buffer
)
676 fs_dir
* rec
= (fs_dir
*) fd
->privdata
;
683 XPRINTF("USBHDFSD: fs_dread called: unit %d\n", fd
->unit
);
685 fatd
= fat_getData(fd
->unit
);
686 if (fatd
== NULL
) { _fs_unlock(); return -ENODEV
; }
688 while (rec
->status
> 0
689 && (rec
->fatdir
.attr
& FAT_ATTR_VOLUME_LABEL
690 || ((rec
->fatdir
.attr
& (FAT_ATTR_HIDDEN
| FAT_ATTR_SYSTEM
)) == (FAT_ATTR_HIDDEN
| FAT_ATTR_SYSTEM
))))
691 rec
->status
= fat_getNextDirentry(fatd
, &rec
->fatdlist
, &rec
->fatdir
);
694 if (rec
->status
>= 0)
696 memset(buffer
, 0, sizeof(fio_dirent_t
));
697 fillStat(&buffer
->stat
, &rec
->fatdir
);
698 strcpy(buffer
->name
, (const char*) rec
->fatdir
.name
);
702 rec
->status
= fat_getNextDirentry(fatd
, &rec
->fatdlist
, &rec
->fatdir
);
708 //---------------------------------------------------------------------------
709 int fs_getstat(iop_file_t
*fd
, const char *name
, fio_stat_t
*stat
)
713 unsigned int cluster
= 0;
718 XPRINTF("USBHDFSD: fs_getstat called: unit %d name %s\n", fd
->unit
, name
);
720 fatd
= fat_getData(fd
->unit
);
721 if (fatd
== NULL
) { _fs_unlock(); return -ENODEV
; }
723 XPRINTF("USBHDFSD: Calling fat_getFileStartCluster from fs_getstat\n");
724 ret
= fat_getFileStartCluster(fatd
, name
, &cluster
, &fatdir
);
730 memset(stat
, 0, sizeof(fio_stat_t
));
731 fillStat(stat
, &fatdir
);
737 //---------------------------------------------------------------------------
738 int fs_chstat (iop_file_t
*fd
, const char *name
, fio_stat_t
*stat
, unsigned int a
)
743 //---------------------------------------------------------------------------
744 int fs_deinit (iop_device_t
*fd
)
749 //---------------------------------------------------------------------------
750 int fs_format (iop_file_t
*fd
)
755 //---------------------------------------------------------------------------
756 unsigned int fs_getFileSector(iop_file_t
*fd
, char *name
)
760 unsigned int cluster
= 0;
763 fatd
= fat_getData(fd
->unit
);
767 ret
= fat_getFileStartCluster(fatd
, name
, &cluster
, &fatDir
);
771 return fat_cluster2sector(&fatd
->partBpb
, cluster
);
774 //---------------------------------------------------------------------------
775 int fs_checkClusterChain(iop_file_t
*fd
, char *name
)
781 unsigned int cluster
= 0;
783 fatd
= fat_getData(fd
->unit
);
787 rec
= fs_findFreeFileSlot();
791 ret
= fat_getFileStartCluster(fatd
, name
, &cluster
, &rec
->fatdir
);
799 // Check cluster chain (write operation can cause dammage if file is fragmented)
800 while (chain_end
== 0) {
801 chain_size
= fat_getClusterChain(fatd
, cluster
, fatd
->cbuf
, MAX_DIR_CLUSTER
, 1);
803 if(fatd
->cbuf
[0] != fatd
->cbuf
[chain_size
-1] - (chain_size
-1))
806 if (chain_size
== MAX_DIR_CLUSTER
) {
807 cluster
= fatd
->cbuf
[chain_size
- 1];
816 //---------------------------------------------------------------------------
817 int fs_ioctl(iop_file_t
*fd
, unsigned long request
, void *data
)
820 fs_dir
* rec
= (fs_dir
*) fd
->privdata
;
828 fatd
= fat_getData(fd
->unit
);
829 if (fatd
== NULL
) { _fs_unlock(); return -ENODEV
; }
832 case IOCTL_CKFREE
: //Request to calculate free space (ignore file/folder selected)
835 case IOCTL_RENAME
: //Request to rename opened file/folder
838 case IOCTL_GETCLUSTER
:
839 ret
= fs_getFileSector(fd
, (char *)data
);
841 case IOCTL_GETDEVSECTORSIZE
:
842 ret
= mass_stor_sectorsize(fatd
->dev
);
844 case IOCTL_CHECKCHAIN
:
845 ret
= fs_checkClusterChain(fd
, (char *)data
);
856 /* init file system driver */
859 fs_driver
.name
= "mass";
860 fs_driver
.type
= IOP_DT_FS
;
861 fs_driver
.version
= 2;
862 fs_driver
.desc
= "Usb mass storage driver";
863 fs_driver
.ops
= &fs_functarray
;
865 fs_functarray
.init
= fs_init
;
866 fs_functarray
.deinit
= fs_deinit
;
867 fs_functarray
.format
= fs_format
;
868 fs_functarray
.open
= fs_open
;
869 fs_functarray
.close
= fs_close
;
870 fs_functarray
.read
= fs_read
;
871 fs_functarray
.write
= fs_write
;
872 fs_functarray
.lseek
= fs_lseek
;
873 fs_functarray
.ioctl
= fs_ioctl
;
874 fs_functarray
.remove
= fs_remove
;
875 fs_functarray
.mkdir
= fs_mkdir
;
876 fs_functarray
.rmdir
= fs_rmdir
;
877 fs_functarray
.dopen
= fs_dopen
;
878 fs_functarray
.dclose
= fs_dclose
;
879 fs_functarray
.dread
= fs_dread
;
880 fs_functarray
.getstat
= fs_getstat
;
881 fs_functarray
.chstat
= fs_chstat
;
886 // should return an error code if AddDrv fails...
891 //---------------------------------------------------------------------------
892 //End of file: fs_driver.c
893 //---------------------------------------------------------------------------