2 * Copyright 2004 Martin Fuchs
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 // Martin Fuchs, 01.02.2004
34 static union DEntry
* link_dir_entries(struct dirent
* dir
, struct Kette
* K
, int cnt
)
36 union DEntry
* Ent
= (union DEntry
*) dir
;
37 struct Kette
* L
= NULL
;
42 AddP(K
, sizeof(struct Kette
));
44 AddP(Ent
, sizeof(union DEntry
));
52 void FATDirectory::read_directory(int scan_flags
)
54 CONTEXT("FATDirectory::read_directory()");
58 union DEntry
* p
= (union DEntry
*) _dir
;
62 /* if (!IS_LNAME(p->E.attr) && p->E.name[0]!=FAT_DEL_CHAR)
63 gesBytes += p->E.size;
66 AddP(p
, sizeof(union DEntry
));
67 } while(++i
<_ents
&& p
->E
.name
[0]);
69 _alloc
= (struct Kette
*) malloc((size_t)((_ents
=i
)+8)*sizeof(struct Kette
));
73 link_dir_entries(_dir
, _alloc
, i
);
75 Entry
* first_entry
= NULL
;
76 int level
= _level
+ 1;
80 WIN32_FIND_DATA w32fd
;
84 TCHAR buffer
[MAX_PATH
];
86 _tcscpy(buffer
, (LPCTSTR
)_path
);
87 LPTSTR pname
= buffer
+ _tcslen(buffer
);
88 int plen
= COUNTOF(buffer
) - _tcslen(buffer
);
93 for(Kette
*p
=_alloc
; p
; p
=p
->Vorw
) {
94 memset(&w32fd
, 0, sizeof(WIN32_FIND_DATA
));
96 DEntry_E
& e
= p
->Ent
->E
;
98 // get file/directory attributes
101 if (attr
.b
& (_A_DELETED
| _A_ILLEGAL
))
102 attr
.b
|= _A_ILLEGAL
;
104 const char* s
= e
.name
;
105 LPTSTR d
= w32fd
.cFileName
;
107 if (!IS_LNAME(attr
.b
) || e
.name
[0]==FAT_DEL_CHAR
) {
108 if (e
.name
[0] == FAT_DEL_CHAR
)
109 w32fd
.dwFileAttributes
|= ATTRIBUTE_ERASED
;
110 else if (IS_LNAME(attr
.b
))
111 w32fd
.dwFileAttributes
|= ATTRIBUTE_LONGNAME
;
112 else if (attr
.a
.directory
)
113 w32fd
.dwFileAttributes
|= FILE_ATTRIBUTE_DIRECTORY
;
114 else if (attr
.a
.volume
)
115 w32fd
.dwFileAttributes
|= ATTRIBUTE_VOLNAME
; //@@ -> in Volume-Name der Root kopieren
118 *d
++ = *s
==FAT_DEL_CHAR
? '?': *s
;
124 while(d
>w32fd
.cFileName
&& d
[-1]==' ')
132 while(d
>w32fd
.cFileName
&& d
[-1]==' ')
135 if (d
>w32fd
.cFileName
&& d
[-1]=='.')
140 s
= (const char*)p
->Ent
->B
; // no change of the pointer, just to avoid overung warnings in code checkers
142 // read long file name
143 TCHAR lname
[] = {s
[1], s
[3], s
[5], s
[7], s
[9], s
[14], s
[16], s
[18], s
[20], s
[22], s
[24], s
[28], s
[30]};
145 long_name
= String(lname
, 13) + long_name
;
148 if (!IS_LNAME(attr
.b
) && !attr
.a
.volume
) {
150 w32fd
.nFileSizeLow
= e
.size
;
152 // convert date/time attribute into FILETIME
153 const filedate
& date
= e
.date
;
154 const filetime
& time
= e
.time
;
158 stime
.wYear
= date
.year
+ 1980;
159 stime
.wMonth
= date
.month
;
160 stime
.wDayOfWeek
= (WORD
)-1;
161 stime
.wDay
= date
.day
;
162 stime
.wHour
= time
.hour
;
163 stime
.wMinute
= time
.min
;
164 stime
.wSecond
= time
.sec2
* 2;
165 stime
.wMilliseconds
= 0;
167 if (SystemTimeToFileTime(&stime
, &ftime
))
168 LocalFileTimeToFileTime(&ftime
, &w32fd
.ftLastWriteTime
);
170 if (!(w32fd
.dwFileAttributes
& ATTRIBUTE_ERASED
)) { //@@
173 if (w32fd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
174 _tcscpy_s(pname
, plen
, w32fd
.cFileName
);
175 entry
= new FATDirectory(_drive
, this, buffer
, e
.fclus
);
177 entry
= new FATEntry(this, e
.fclus
);
179 memcpy(&entry
->_data
, &w32fd
, sizeof(WIN32_FIND_DATA
));
181 if (!long_name
.empty()) {
182 entry
->_content
= _tcsdup(long_name
);
192 entry
->_level
= level
;
207 const void* FATDirectory::get_next_path_component(const void* p
) const
209 LPCTSTR s
= (LPCTSTR
) p
;
211 while(*s
&& *s
!=TEXT('\\') && *s
!=TEXT('/'))
214 while(*s
==TEXT('\\') || *s
==TEXT('/'))
224 Entry
* FATDirectory::find_entry(const void* p
)
226 LPCTSTR name
= (LPCTSTR
)p
;
228 for(Entry
*entry
=_down
; entry
; entry
=entry
->_next
) {
230 LPCTSTR q
= entry
->_data
.cFileName
;
233 if (!*p
|| *p
==TEXT('\\') || *p
==TEXT('/'))
235 } while(tolower(*p
++) == tolower(*q
++));
238 q
= entry
->_data
.cAlternateFileName
;
241 if (!*p
|| *p
==TEXT('\\') || *p
==TEXT('/'))
243 } while(tolower(*p
++) == tolower(*q
++));
250 // get full path of specified directory entry
251 bool FATEntry::get_path(PTSTR path
, size_t path_count
) const
253 return get_path_base ( path
, path_count
, ET_FAT
);
256 ShellPath
FATEntry::create_absolute_pidl() const
258 CONTEXT("WinEntry::create_absolute_pidl()");
260 return (LPCITEMIDLIST
)NULL
;
261 /* prepend root path if the drive is currently actually mounted in the file system -> return working PIDL
262 TCHAR path[MAX_PATH];
264 if (get_path(path, COUNTOF(path)))
265 return ShellPath(path);
272 FATDirectory::FATDirectory(FATDrive
& drive
, LPCTSTR root_path
)
276 _path
= _tcsdup(root_path
);
285 FATDirectory::FATDirectory(FATDrive
& drive
, Entry
* parent
, LPCTSTR path
, unsigned cluster
)
286 : FATEntry(parent
, cluster
),
289 _path
= _tcsdup(path
);
297 FATDirectory::~FATDirectory()
303 bool FATDirectory::read_dir()
308 if (!_drive
._boot_sector
.SectorsPerFAT
) { // FAT32? [boot_sector32->reserved0==0]
309 BootSector32
* boot_sector32
= (BootSector32
*) &_drive
._boot_sector
;
310 DWORD sect
= _drive
._boot_sector
.ReservedSectors
+ _drive
._boot_sector
.NumberFATs
*boot_sector32
->SectorsPerFAT32
; // lese Root-Directory ein
311 int RootEntries
= boot_sector32
->RootSectors
* 32; //@@
313 _secarr
= (struct dirsecz
*)malloc(sizeof(DWORD
) * (_cur_bufs
= (int)((_ents
=RootEntries
)/_drive
._bufents
)));
315 for(i
=0; i
<_cur_bufs
; i
++)
316 _secarr
->s
[i
] = sect
+i
;
318 _dir
= (struct dirent
*)malloc((size_t)(_ents
+16)*sizeof(union DEntry
));
322 if (!(_drive
.read_sector(*_secarr
->s
,(Buffer
*)_dir
,_cur_bufs
)))
325 DWORD sect
= _drive
._boot_sector
.ReservedSectors
+ _drive
._boot_sector
.NumberFATs
*_drive
._boot_sector
.SectorsPerFAT
; // read in root directory
327 _secarr
= (struct dirsecz
*)malloc(sizeof(DWORD
) * (_cur_bufs
= (int)((_ents
=_drive
._boot_sector
.RootEntries
)/_drive
._bufents
)));
329 for(i
=0; i
<_cur_bufs
; i
++)
330 _secarr
->s
[i
] = sect
+i
;
332 _dir
= (struct dirent
*)malloc((size_t)(_ents
+16)*sizeof(union DEntry
));
336 if (!_drive
.read_sector(*_secarr
->s
,(Buffer
*)_dir
,_cur_bufs
))
348 h
= _drive
.read_FAT(h
, ok
);
354 } while (h
<0x0ffffff0 && h
);
356 _secarr
= (struct dirsecz
*) malloc(sizeof(DWORD
) * _cur_bufs
);
361 _ents
= _drive
._bufents
* (size_t)_cur_bufs
* _drive
._SClus
;
363 if ((buf
=(Buffer
*)(_dir
=(struct dirent
*)malloc((size_t) (_ents
+16)*sizeof(union DEntry
)))) == NULL
)
370 if (!_drive
._boot_sector
.SectorsPerFAT
) { // FAT32 ?
371 BootSector32
* boot_sector32
= (BootSector32
*) &_drive
._boot_sector
;
372 //int RootEntries = boot_sector32->RootSectors * 32; //@@
373 //fdatsec = _drive._boot_sector.ReservedSectors + _drive._boot_sector.NumberFATs*boot_sector32->SectorsPerFAT32 + RootEntries*sizeof(DEntry)/_drive._boot_sector.BytesPerSector; // dpb.fdirsec
374 fdatsec
= _drive
._boot_sector
.ReservedSectors
+
375 _drive
._boot_sector
.NumberFATs
*boot_sector32
->SectorsPerFAT32
+ boot_sector32
->RootSectors
;
377 fdatsec
= _drive
._boot_sector
.ReservedSectors
+
378 _drive
._boot_sector
.NumberFATs
*_drive
._boot_sector
.SectorsPerFAT
+
379 _drive
._boot_sector
.RootEntries
*sizeof(DEntry
)/_drive
._boot_sector
.BytesPerSector
; // dpb.fdirsec
381 for(i
=0; i
<_cur_bufs
; i
++) {
382 _secarr
->s
[i
] = fdatsec
+ (DWORD
)_drive
._SClus
*(h
-2);
384 h
= _drive
.read_FAT(h
, ok
);
390 for(i
=0; i
<_cur_bufs
; i
++) {
391 if ((ok
= (_drive
.read_sector(_secarr
->s
[i
], buf
, _drive
._SClus
))) == true)
392 AddP(buf
, _drive
._bufl
*_drive
._SClus
)
394 //@@FPara = _secarr->s[i];
399 buf
->dat
[0] = 0; // Endekennzeichen für Rekurs setzen
407 #pragma warning(disable: 4355)
410 FATDrive::FATDrive(LPCTSTR path
)
411 : FATDirectory(*this, TEXT("\\"))
423 _hDrive
= CreateFile(path
, GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, 0);
425 if (_hDrive
!= INVALID_HANDLE_VALUE
) {
426 _boot_sector
.BytesPerSector
= 512;
428 if (read_sector(0, (Buffer
*)&_boot_sector
, 1)) {
429 _bufl
= _boot_sector
.BytesPerSector
;
430 _SClus
= _boot_sector
.SectorsPerCluster
;
431 _bufents
= _bufl
/ sizeof(union DEntry
);
438 FATDrive::~FATDrive()
440 if (_hDrive
!= INVALID_HANDLE_VALUE
)
441 CloseHandle(_hDrive
);
447 void FATDrive::small_cache()
453 free(_CacheSec
), _CacheSec
= NULL
;
458 _Caches
= CACHE_SIZE_LOW
;
459 _FATCache
= (struct Cache
*) malloc((_Caches
+1) * _drive
._bufl
);
464 void FATDrive::reset_cache() // mark cache as empty
469 _CacheSec
= (DWORD
*) malloc(_Caches
* sizeof(DWORD
));
470 _CacheCnt
= (int*) malloc(_Caches
* sizeof(int));
471 _CacheDty
= (bool*) malloc(_Caches
* sizeof(bool));
473 _CacheSec
= (DWORD
*) realloc(_CacheSec
, _Caches
* sizeof(DWORD
));
474 _CacheCnt
= (int*) realloc(_CacheCnt
, _Caches
* sizeof(int));
475 _CacheDty
= (bool*) realloc(_CacheDty
, _Caches
* sizeof(bool));
478 for(i
=0; i
<_Caches
; i
++)
481 _read_ahead
= (_Caches
+1) / 2;
484 bool FATDrive::read_sector(DWORD sec
, Buffer
* buf
, int len
)
486 sec
+= 63; //@@ jump to first partition
488 if (SetFilePointer(_hDrive
, sec
*_drive
._boot_sector
.BytesPerSector
, 0, 0) == INVALID_SET_FILE_POINTER
)
493 if (!ReadFile(_hDrive
, buf
, len
*_drive
._boot_sector
.BytesPerSector
, &read
, 0))
499 DWORD
FATDrive::read_FAT(DWORD cluster
, bool& ok
) //@@ use exception handling
504 DWORD nclus
= (_boot_sector
.Sectors32
? _boot_sector
.Sectors32
: _boot_sector
.Sectors16
) / _boot_sector
.SectorsPerCluster
; ///@todo cache result
506 if (cluster
> nclus
) {
511 if (nclus
>= 65536) { // FAT32
512 DWORD FATsec
= cluster
/ (_boot_sector
.BytesPerSector
/4);
513 DWORD z
= (cluster
- _boot_sector
.BytesPerSector
/4 * FATsec
)*4;
514 FATsec
+= _boot_sector
.ReservedSectors
;
515 if (!read_cache(FATsec
, &FATBuf
))
517 nClus
= dpeek(&FATBuf
->dat
[z
]);
518 } else if (nclus
>= 4096) { // 16 Bit-FAT
519 DWORD FATsec
= cluster
/ (_boot_sector
.BytesPerSector
/2);
520 DWORD z
= (cluster
- _boot_sector
.BytesPerSector
/2 * FATsec
)*2;
521 FATsec
+= _boot_sector
.ReservedSectors
;
522 if (!read_cache(FATsec
, &FATBuf
))
524 nClus
= wpeek(&FATBuf
->dat
[z
]);
528 } else { // 12 Bit-FAT
529 DWORD FATsec
= cluster
*3 / (_boot_sector
.BytesPerSector
*2);
530 DWORD z
= (cluster
*3 - _boot_sector
.BytesPerSector
*2*FATsec
)/2;
531 FATsec
+= _boot_sector
.ReservedSectors
;
532 if (!read_cache(FATsec
,&FATBuf
))
534 BYTE a
= FATBuf
->dat
[z
++];
536 if (z
>= _boot_sector
.BytesPerSector
)
537 if (!read_cache(FATsec
+1,&FATBuf
))
541 BYTE b
= FATBuf
->dat
[z
];
544 nClus
= (a
>>4) | (b
<<4);
546 nClus
= a
| ((b
& 0xf)<<8);
555 bool FATDrive::read_cache(DWORD sec
, Buffer
** bufptr
)
559 if (_boot_sector
.BytesPerSector
!= BufLen
) // no standard sector size?
560 return read_sector(sec
, *bufptr
=(Buffer
*)&_FATCache
[0], 1);
564 for(i
=0; _CacheSec
[i
]!=sec
&& i
<_Caches
; )
569 *bufptr
= (Buffer
*) &_FATCache
[i
]; // FAT-Sektor schon gepuffert
574 i
= get_cache_buffer();
576 if (_cache_empty
) // von get_cache_buffer() gesetzt
579 anz
= _boot_sector
.SectorsPerFAT
*_boot_sector
.NumberFATs
- sec
;
581 if (anz
> _read_ahead
)
584 for(i
=0; i
<anz
; i
++) {
585 _CacheSec
[i
] = sec
++;
590 _CacheCnt
[0] = _CacheCount
;
592 return read_sector(_CacheSec
[0], *bufptr
=(Buffer
*) &_FATCache
[0], anz
);
597 _CacheCnt
[i
] = _CacheCount
;
599 return read_sector(_CacheSec
[i
]=sec
, *bufptr
=(Buffer
*) &_FATCache
[i
], 1);
603 int FATDrive::get_cache_buffer() // search for free cache buffer
607 for(i
=0; i
<_Caches
; i
++)
611 _cache_empty
= i
==_Caches
? true: false;
613 for(i
=0; _CacheSec
[i
] && i
<_Caches
; )
620 minCnt
= 0; // search for least used buffer
622 for(j
=i
=0; i
<_Caches
; i
++)
623 if (minCnt
< _CacheCnt
[i
]) {
624 minCnt
= _CacheCnt
[i
];
628 /**@todo enable write back
629 if (CacheDty[j]) // Dirty-Flag gesetzt?
630 if (writesec(_CacheSec[j], (Buffer*) &_FATCache[j], 1))
631 FPara = _CacheSec[j], Frag(SecWriteErr);