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