Sync to trunk r65566.
[reactos.git] / base / shell / explorer-old / shell / fatfs.cpp
1 /*
2 * Copyright 2004 Martin Fuchs
3 *
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.
8 *
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.
13 *
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
17 */
18
19
20 //
21 // Explorer clone
22 //
23 // fatfs.cpp
24 //
25 // Martin Fuchs, 01.02.2004
26 //
27
28
29 #include <precomp.h>
30
31 #include "fatfs.h"
32
33 #ifdef _DEBUG
34
35 static union DEntry* link_dir_entries(struct dirent* dir, struct Kette* K, int cnt)
36 {
37 union DEntry* Ent = (union DEntry*) dir;
38 struct Kette* L = NULL;
39
40 for(; cnt; cnt--) {
41 K->Rueck = L;
42 (L=K)->Ent = Ent;
43 AddP(K, sizeof(struct Kette));
44 L->Vorw = K;
45 AddP(Ent, sizeof(union DEntry));
46 }
47
48 L->Vorw = NULL;
49
50 return Ent;
51 }
52
53 void FATDirectory::read_directory(int scan_flags)
54 {
55 CONTEXT("FATDirectory::read_directory()");
56
57 read_dir();
58
59 union DEntry* p = (union DEntry*) _dir;
60 int i = 0;
61
62 do {
63 /* if (!IS_LNAME(p->E.attr) && p->E.name[0]!=FAT_DEL_CHAR)
64 gesBytes += p->E.size;
65 */
66
67 AddP(p, sizeof(union DEntry));
68 } while(++i<_ents && p->E.name[0]);
69
70 _alloc = (struct Kette*) malloc((size_t)((_ents=i)+8)*sizeof(struct Kette));
71 if (!_alloc)
72 return;
73
74 link_dir_entries(_dir, _alloc, i);
75
76 Entry* first_entry = NULL;
77 int level = _level + 1;
78
79 Entry* last = NULL;
80
81 WIN32_FIND_DATA w32fd;
82 FAT_attribute attr;
83 String long_name;
84
85 TCHAR buffer[MAX_PATH];
86
87 _tcscpy(buffer, (LPCTSTR)_path);
88 LPTSTR pname = buffer + _tcslen(buffer);
89 int plen = COUNTOF(buffer) - _tcslen(buffer);
90
91 *pname++ = '\\';
92 --plen;
93
94 for(Kette*p=_alloc; p; p=p->Vorw) {
95 memset(&w32fd, 0, sizeof(WIN32_FIND_DATA));
96
97 DEntry_E& e = p->Ent->E;
98
99 // get file/directory attributes
100 attr.b = e.attr;
101
102 if (attr.b & (_A_DELETED | _A_ILLEGAL))
103 attr.b |= _A_ILLEGAL;
104
105 const char* s = e.name;
106 LPTSTR d = w32fd.cFileName;
107
108 if (!IS_LNAME(attr.b) || e.name[0]==FAT_DEL_CHAR) {
109 if (e.name[0] == FAT_DEL_CHAR)
110 w32fd.dwFileAttributes |= ATTRIBUTE_ERASED;
111 else if (IS_LNAME(attr.b))
112 w32fd.dwFileAttributes |= ATTRIBUTE_LONGNAME;
113 else if (attr.a.directory)
114 w32fd.dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
115 else if (attr.a.volume)
116 w32fd.dwFileAttributes |= ATTRIBUTE_VOLNAME; //@@ -> in Volume-Name der Root kopieren
117
118 // get file name
119 *d++ = *s==FAT_DEL_CHAR? '?': *s;
120 ++s;
121
122 for(i=0; i<7; ++i)
123 *d++ = *s++;
124
125 while(d>w32fd.cFileName && d[-1]==' ')
126 --d;
127
128 *d++ = '.';
129
130 for(; i<10; ++i)
131 *d++ = *s++;
132
133 while(d>w32fd.cFileName && d[-1]==' ')
134 --d;
135
136 if (d>w32fd.cFileName && d[-1]=='.')
137 --d;
138
139 *d = '\0';
140 } else {
141 s = (const char*)p->Ent->B; // no change of the pointer, just to avoid overung warnings in code checkers
142
143 // read long file name
144 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
146 long_name = String(lname, 13) + long_name;
147 }
148
149 if (!IS_LNAME(attr.b) && !attr.a.volume) {
150 // get file size
151 w32fd.nFileSizeLow = e.size;
152
153 // convert date/time attribute into FILETIME
154 const filedate& date = e.date;
155 const filetime& time = e.time;
156 SYSTEMTIME stime;
157 FILETIME ftime;
158
159 stime.wYear = date.year + 1980;
160 stime.wMonth = date.month;
161 stime.wDayOfWeek = (WORD)-1;
162 stime.wDay = date.day;
163 stime.wHour = time.hour;
164 stime.wMinute = time.min;
165 stime.wSecond = time.sec2 * 2;
166 stime.wMilliseconds = 0;
167
168 if (SystemTimeToFileTime(&stime, &ftime))
169 LocalFileTimeToFileTime(&ftime, &w32fd.ftLastWriteTime);
170
171 if (!(w32fd.dwFileAttributes & ATTRIBUTE_ERASED)) { //@@
172 Entry* entry;
173
174 if (w32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
175 _tcscpy_s(pname, plen, w32fd.cFileName);
176 entry = new FATDirectory(_drive, this, buffer, e.fclus);
177 } else
178 entry = new FATEntry(this, e.fclus);
179
180 memcpy(&entry->_data, &w32fd, sizeof(WIN32_FIND_DATA));
181
182 if (!long_name.empty()) {
183 entry->_content = _tcsdup(long_name);
184 long_name.erase();
185 }
186
187 if (!first_entry)
188 first_entry = entry;
189
190 if (last)
191 last->_next = entry;
192
193 entry->_level = level;
194
195 last = entry;
196 }
197 }
198 }
199
200 if (last)
201 last->_next = NULL;
202
203 _down = first_entry;
204 _scanned = true;
205 }
206
207
208 const void* FATDirectory::get_next_path_component(const void* p) const
209 {
210 LPCTSTR s = (LPCTSTR) p;
211
212 while(*s && *s!=TEXT('\\') && *s!=TEXT('/'))
213 ++s;
214
215 while(*s==TEXT('\\') || *s==TEXT('/'))
216 ++s;
217
218 if (!*s)
219 return NULL;
220
221 return s;
222 }
223
224
225 Entry* FATDirectory::find_entry(const void* p)
226 {
227 LPCTSTR name = (LPCTSTR)p;
228
229 for(Entry*entry=_down; entry; entry=entry->_next) {
230 LPCTSTR p = name;
231 LPCTSTR q = entry->_data.cFileName;
232
233 do {
234 if (!*p || *p==TEXT('\\') || *p==TEXT('/'))
235 return entry;
236 } while(tolower(*p++) == tolower(*q++));
237
238 p = name;
239 q = entry->_data.cAlternateFileName;
240
241 do {
242 if (!*p || *p==TEXT('\\') || *p==TEXT('/'))
243 return entry;
244 } while(tolower(*p++) == tolower(*q++));
245 }
246
247 return NULL;
248 }
249
250
251 // get full path of specified directory entry
252 bool FATEntry::get_path(PTSTR path, size_t path_count) const
253 {
254 return get_path_base ( path, path_count, ET_FAT );
255 }
256
257 ShellPath FATEntry::create_absolute_pidl() const
258 {
259 CONTEXT("WinEntry::create_absolute_pidl()");
260
261 return (LPCITEMIDLIST)NULL;
262 /* prepend root path if the drive is currently actually mounted in the file system -> return working PIDL
263 TCHAR path[MAX_PATH];
264
265 if (get_path(path, COUNTOF(path)))
266 return ShellPath(path);
267
268 return ShellPath();
269 */
270 }
271
272
273 FATDirectory::FATDirectory(FATDrive& drive, LPCTSTR root_path)
274 : FATEntry(),
275 _drive(drive)
276 {
277 _path = _tcsdup(root_path);
278
279 _secarr = NULL;
280 _cur_bufs = 0;
281 _ents = 0;
282 _dir = NULL;
283 _cluster = 0;
284 }
285
286 FATDirectory::FATDirectory(FATDrive& drive, Entry* parent, LPCTSTR path, unsigned cluster)
287 : FATEntry(parent, cluster),
288 _drive(drive)
289 {
290 _path = _tcsdup(path);
291
292 _secarr = NULL;
293 _cur_bufs = 0;
294 _ents = 0;
295 _dir = NULL;
296 }
297
298 FATDirectory::~FATDirectory()
299 {
300 free(_path);
301 _path = NULL;
302 }
303
304 bool FATDirectory::read_dir()
305 {
306 int i;
307
308 if (_cluster == 0) {
309 if (!_drive._boot_sector.SectorsPerFAT) { // FAT32? [boot_sector32->reserved0==0]
310 BootSector32* boot_sector32 = (BootSector32*) &_drive._boot_sector;
311 DWORD sect = _drive._boot_sector.ReservedSectors + _drive._boot_sector.NumberFATs*boot_sector32->SectorsPerFAT32; // lese Root-Directory ein
312 int RootEntries = boot_sector32->RootSectors * 32; //@@
313
314 _secarr = (struct dirsecz*)malloc(sizeof(DWORD) * (_cur_bufs = (int)((_ents=RootEntries)/_drive._bufents)));
315
316 for(i=0; i<_cur_bufs; i++)
317 _secarr->s[i] = sect+i;
318
319 _dir = (struct dirent*)malloc((size_t)(_ents+16)*sizeof(union DEntry));
320 if (!_dir)
321 return false;
322
323 if (!(_drive.read_sector(*_secarr->s,(Buffer*)_dir,_cur_bufs)))
324 return false;
325 } else {
326 DWORD sect = _drive._boot_sector.ReservedSectors + _drive._boot_sector.NumberFATs*_drive._boot_sector.SectorsPerFAT; // read in root directory
327
328 _secarr = (struct dirsecz*)malloc(sizeof(DWORD) * (_cur_bufs = (int)((_ents=_drive._boot_sector.RootEntries)/_drive._bufents)));
329
330 for(i=0; i<_cur_bufs; i++)
331 _secarr->s[i] = sect+i;
332
333 _dir = (struct dirent*)malloc((size_t)(_ents+16)*sizeof(union DEntry));
334 if (!_dir)
335 return false;
336
337 if (!_drive.read_sector(*_secarr->s,(Buffer*)_dir,_cur_bufs))
338 return false;
339 }
340 } else {
341 Buffer* buf;
342 bool ok;
343
344 DWORD h = _cluster;
345
346 _cur_bufs = 0;
347
348 do {
349 h = _drive.read_FAT(h, ok);
350
351 if (!ok)
352 return false;
353
354 _cur_bufs++;
355 } while (h<0x0ffffff0 && h);
356
357 _secarr = (struct dirsecz*) malloc(sizeof(DWORD) * _cur_bufs);
358
359 if (!_secarr)
360 return false;
361
362 _ents = _drive._bufents * (size_t)_cur_bufs * _drive._SClus;
363
364 if ((buf=(Buffer*)(_dir=(struct dirent*)malloc((size_t) (_ents+16)*sizeof(union DEntry)))) == NULL)
365 return false;
366
367 h = _cluster;
368
369 DWORD fdatsec;
370
371 if (!_drive._boot_sector.SectorsPerFAT) { // FAT32 ?
372 BootSector32* boot_sector32 = (BootSector32*) &_drive._boot_sector;
373 //int RootEntries = boot_sector32->RootSectors * 32; //@@
374 //fdatsec = _drive._boot_sector.ReservedSectors + _drive._boot_sector.NumberFATs*boot_sector32->SectorsPerFAT32 + RootEntries*sizeof(DEntry)/_drive._boot_sector.BytesPerSector; // dpb.fdirsec
375 fdatsec = _drive._boot_sector.ReservedSectors +
376 _drive._boot_sector.NumberFATs*boot_sector32->SectorsPerFAT32 + boot_sector32->RootSectors;
377 } else
378 fdatsec = _drive._boot_sector.ReservedSectors +
379 _drive._boot_sector.NumberFATs*_drive._boot_sector.SectorsPerFAT +
380 _drive._boot_sector.RootEntries*sizeof(DEntry)/_drive._boot_sector.BytesPerSector; // dpb.fdirsec
381
382 for(i=0; i<_cur_bufs; i++) {
383 _secarr->s[i] = fdatsec + (DWORD)_drive._SClus*(h-2);
384
385 h = _drive.read_FAT(h, ok);
386
387 if (!ok)
388 return false;
389 }
390
391 for(i=0; i<_cur_bufs; i++) {
392 if ((ok = (_drive.read_sector(_secarr->s[i], buf, _drive._SClus))) == true)
393 AddP(buf, _drive._bufl*_drive._SClus)
394 else {
395 //@@FPara = _secarr->s[i];
396 return false;
397 }
398 }
399
400 buf->dat[0] = 0; // Endekennzeichen für Rekurs setzen
401 }
402
403 return true;
404 }
405
406
407 #ifdef _MSC_VER
408 #pragma warning(disable: 4355)
409 #endif
410
411 FATDrive::FATDrive(LPCTSTR path)
412 : FATDirectory(*this, TEXT("\\"))
413 {
414 _bufl = 0;
415 _bufents = 0;
416 _SClus = 0;
417 _FATCache = NULL;
418 _CacheCount = 0;
419 _CacheSec = NULL;
420 _CacheCnt = NULL;
421 _CacheDty = NULL;
422 _Caches = 0;
423
424 _hDrive = CreateFile(path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
425
426 if (_hDrive != INVALID_HANDLE_VALUE) {
427 _boot_sector.BytesPerSector = 512;
428
429 if (read_sector(0, (Buffer*)&_boot_sector, 1)) {
430 _bufl = _boot_sector.BytesPerSector;
431 _SClus = _boot_sector.SectorsPerCluster;
432 _bufents = _bufl / sizeof(union DEntry);
433 }
434
435 small_cache();
436 }
437 }
438
439 FATDrive::~FATDrive()
440 {
441 if (_hDrive != INVALID_HANDLE_VALUE)
442 CloseHandle(_hDrive);
443
444 free(_path);
445 _path = NULL;
446 }
447
448 void FATDrive::small_cache()
449 {
450 if (_FATCache)
451 free(_FATCache);
452
453 if (_CacheSec) {
454 free(_CacheSec), _CacheSec = NULL;
455 free(_CacheCnt);
456 free(_CacheDty);
457 }
458
459 _Caches = CACHE_SIZE_LOW;
460 _FATCache = (struct Cache *) malloc((_Caches+1) * _drive._bufl);
461
462 reset_cache();
463 }
464
465 void FATDrive::reset_cache() // mark cache as empty
466 {
467 int i;
468
469 if (!_CacheSec) {
470 _CacheSec = (DWORD*) malloc(_Caches * sizeof(DWORD));
471 _CacheCnt = (int*) malloc(_Caches * sizeof(int));
472 _CacheDty = (bool*) malloc(_Caches * sizeof(bool));
473 } else {
474 _CacheSec = (DWORD*) realloc(_CacheSec, _Caches * sizeof(DWORD));
475 _CacheCnt = (int*) realloc(_CacheCnt, _Caches * sizeof(int));
476 _CacheDty = (bool*) realloc(_CacheDty, _Caches * sizeof(bool));
477 }
478
479 for(i=0; i<_Caches; i++)
480 _CacheSec[i] = 0;
481
482 _read_ahead = (_Caches+1) / 2;
483 }
484
485 bool FATDrive::read_sector(DWORD sec, Buffer* buf, int len)
486 {
487 sec += 63; //@@ jump to first partition
488
489 if (SetFilePointer(_hDrive, sec*_drive._boot_sector.BytesPerSector, 0, 0) == INVALID_SET_FILE_POINTER)
490 return false;
491
492 DWORD read;
493
494 if (!ReadFile(_hDrive, buf, len*_drive._boot_sector.BytesPerSector, &read, 0))
495 return false;
496
497 return true;
498 }
499
500 DWORD FATDrive::read_FAT(DWORD cluster, bool& ok) //@@ use exception handling
501 {
502 DWORD nClus;
503 Buffer* FATBuf;
504
505 DWORD nclus = (_boot_sector.Sectors32? _boot_sector.Sectors32: _boot_sector.Sectors16) / _boot_sector.SectorsPerCluster; ///@todo cache result
506
507 if (cluster > nclus) {
508 ok = false;
509 return (DWORD)-1;
510 }
511
512 if (nclus >= 65536) { // FAT32
513 DWORD FATsec = cluster / (_boot_sector.BytesPerSector/4);
514 DWORD z = (cluster - _boot_sector.BytesPerSector/4 * FATsec)*4;
515 FATsec += _boot_sector.ReservedSectors;
516 if (!read_cache(FATsec, &FATBuf))
517 ok = false;
518 nClus = dpeek(&FATBuf->dat[z]);
519 } else if (nclus >= 4096) { // 16 Bit-FAT
520 DWORD FATsec = cluster / (_boot_sector.BytesPerSector/2);
521 DWORD z = (cluster - _boot_sector.BytesPerSector/2 * FATsec)*2;
522 FATsec += _boot_sector.ReservedSectors;
523 if (!read_cache(FATsec, &FATBuf))
524 ok = false;
525 nClus = wpeek(&FATBuf->dat[z]);
526
527 if (nClus >= 0xfff0)
528 nClus |= 0x0fff0000;
529 } else { // 12 Bit-FAT
530 DWORD FATsec = cluster*3 / (_boot_sector.BytesPerSector*2);
531 DWORD z = (cluster*3 - _boot_sector.BytesPerSector*2*FATsec)/2;
532 FATsec += _boot_sector.ReservedSectors;
533 if (!read_cache(FATsec,&FATBuf))
534 ok = false;
535 BYTE a = FATBuf->dat[z++];
536
537 if (z >= _boot_sector.BytesPerSector)
538 if (!read_cache(FATsec+1,&FATBuf))
539 ok = false;
540 z = 0;
541
542 BYTE b = FATBuf->dat[z];
543
544 if (cluster & 1)
545 nClus = (a>>4) | (b<<4);
546 else
547 nClus = a | ((b & 0xf)<<8);
548
549 if (nClus >= 0xff0)
550 nClus |= 0x0ffff000;
551 }
552
553 return nClus;
554 }
555
556 bool FATDrive::read_cache(DWORD sec, Buffer** bufptr)
557 {
558 int i, C, anz;
559
560 if (_boot_sector.BytesPerSector != BufLen) // no standard sector size?
561 return read_sector(sec, *bufptr=(Buffer*)&_FATCache[0], 1);
562
563 _CacheCount++;
564
565 for(i=0; _CacheSec[i]!=sec && i<_Caches; )
566 ++i;
567
568 if (i < _Caches)
569 {
570 *bufptr = (Buffer*) &_FATCache[i]; // FAT-Sektor schon gepuffert
571 _CacheCnt[i]++;
572 return true;
573 }
574
575 i = get_cache_buffer();
576
577 if (_cache_empty) // von get_cache_buffer() gesetzt
578 {
579 C = _CacheCount-1;
580 anz = _boot_sector.SectorsPerFAT*_boot_sector.NumberFATs - sec;
581
582 if (anz > _read_ahead)
583 anz = _read_ahead;
584
585 for(i=0; i<anz; i++) {
586 _CacheSec[i] = sec++;
587 _CacheCnt[i] = C;
588 _CacheDty[i] = 0;
589 }
590
591 _CacheCnt[0] = _CacheCount;
592
593 return read_sector(_CacheSec[0], *bufptr=(Buffer*) &_FATCache[0], anz);
594 }
595 else
596 {
597 _CacheDty[i] = 0;
598 _CacheCnt[i] = _CacheCount;
599
600 return read_sector(_CacheSec[i]=sec, *bufptr=(Buffer*) &_FATCache[i], 1);
601 }
602 }
603
604 int FATDrive::get_cache_buffer() // search for free cache buffer
605 {
606 int i, j, minCnt;
607
608 for(i=0; i<_Caches; i++)
609 if (_CacheSec[i])
610 break;
611
612 _cache_empty = i==_Caches? true: false;
613
614 for(i=0; _CacheSec[i] && i<_Caches; )
615 ++i;
616
617 if (i < _Caches)
618 j = i;
619 else
620 {
621 minCnt = 0; // search for least used buffer
622
623 for(j=i=0; i<_Caches; i++)
624 if (minCnt < _CacheCnt[i]) {
625 minCnt = _CacheCnt[i];
626 j = i;
627 }
628
629 /**@todo enable write back
630 if (CacheDty[j]) // Dirty-Flag gesetzt?
631 if (writesec(_CacheSec[j], (Buffer*) &_FATCache[j], 1))
632 FPara = _CacheSec[j], Frag(SecWriteErr);
633 */
634 }
635
636 return j;
637 }
638
639 #endif // _DEBUG