1 #include "QFat.h"
2
3 #include <QBuffer>
4
operator <<(QDataStream & ds,const FatTocEntry & toc)5 QDataStream &operator<<(QDataStream &ds, const FatTocEntry &toc)
6 {
7 ds << toc.flags;
8 ds << toc.creationTimestamp;
9 ds << toc.modificationTimestamp;
10 ds << toc.size;
11 ds << toc.startCluster;
12 ds << toc.name;
13
14 return ds;
15 }
16
operator >>(QDataStream & ds,FatTocEntry & toc)17 QDataStream &operator>>(QDataStream &ds, FatTocEntry &toc)
18 {
19 ds >> toc.flags;
20 ds >> toc.creationTimestamp;
21 ds >> toc.modificationTimestamp;
22 ds >> toc.size;
23 ds >> toc.startCluster;
24 ds >> toc.name;
25
26 return ds;
27 }
28
QFat(const QString & filename,quint16 clusterCount,quint16 clusterSize)29 QFat::QFat(const QString& filename, quint16 clusterCount, quint16 clusterSize)
30 : m_filename(filename)
31 , m_clusterCount(clusterCount)
32 , m_clusterSize(clusterSize)
33 , m_fatFile(0)
34 {
35 }
36
open()37 FatError QFat::open()
38 {
39 FatError ret;
40
41 m_fatFile = new QFile(m_filename);
42 if (m_fatFile->exists()) {
43 if (!m_fatFile->open(QIODevice::ReadWrite))
44 return FatSysError;
45
46 char magic[4]; // "QFAT"
47 m_fatFile->read((char*)&magic, 4);
48 if (magic[0] != 'Q' || magic[1] != 'F' || magic[2] != 'A' || magic[3] != 'T')
49 return FatNotFatFile;
50
51 m_ds.setDevice(m_fatFile);
52 m_ds >> m_clusterCount;
53 m_ds >> m_clusterSize;
54
55 FAT_FAT_TYPE val;
56 m_fat.reserve(m_clusterCount);
57 for (int i=0; i<m_clusterCount; ++i) {
58 m_ds >> val;
59 m_fat << val;
60 }
61
62 m_startOfData = sizeof(FatHeader) + (m_clusterCount * sizeof(FAT_FAT_TYPE));
63 m_rootToc = readTocData(0);
64 } else {
65 m_fat.reserve(m_clusterCount);
66 for (int i=0; i<m_clusterCount; ++i)
67 m_fat.append(0);
68
69 m_startOfData = sizeof(FatHeader) + (m_clusterCount * sizeof(FAT_FAT_TYPE));
70 }
71
72 return FatNoError;
73 }
74
isOpen()75 bool QFat::isOpen()
76 {
77 if (!m_fatFile)
78 return false;
79
80 return true;
81 }
82
writeFat()83 void QFat::writeFat()
84 {
85 m_fatFile->seek(0);
86
87 char magic[5] = "QFAT";
88 m_fatFile->write(magic, 4);
89 m_ds.setDevice(m_fatFile);
90 m_ds << m_clusterCount;
91 m_ds << m_clusterSize;
92
93 for (int i=0; i<m_clusterCount; ++i)
94 m_ds << m_fat[i];
95 }
96
close()97 void QFat::close()
98 {
99 if (m_fatFile) {
100 if (m_fatFile->isOpen()) {
101 writeFat();
102 m_fatFile->close();
103 }
104 delete m_fatFile;
105 m_fatFile = NULL;
106 }
107 }
108
checkAndCreate(FAT_FAT_TYPE clusterNum)109 FatError QFat::checkAndCreate(FAT_FAT_TYPE clusterNum)
110 {
111 FatError ret;
112
113 if (!m_fatFile)
114 return FatNotOpen;
115
116 if (!m_fatFile->isOpen()) {
117
118 if (!m_fatFile->open(QIODevice::ReadWrite))
119 return FatSysError;
120
121 if (!m_fatFile->resize(m_startOfData + m_clusterSize))
122 return FatOutOfSpace;
123
124 char magic[5] = "QFAT";
125 m_fatFile->write(magic, 4);
126 m_ds.setDevice(m_fatFile);
127 m_ds << m_clusterCount;
128 m_ds << m_clusterSize;
129
130 for (int i=0; i<m_clusterCount; ++i)
131 m_ds << m_fat[i];
132
133 writeTocData(m_rootToc, 0);
134 }
135
136 if (m_fatFile->size() < m_startOfData + (clusterNum+1 * m_clusterSize)) {
137 if (!m_fatFile->resize(m_startOfData + (clusterNum+1 * m_clusterSize)))
138 return FatOutOfSpace;
139 }
140 return FatNoError;
141 }
142
findFreeCluster()143 FAT_FAT_TYPE QFat::findFreeCluster()
144 {
145 FAT_FAT_TYPE freeCluster = 1;
146 for (;freeCluster < m_clusterCount; ++freeCluster)
147 if (!m_fat[freeCluster])
148 return freeCluster;
149
150 return m_clusterCount;
151 }
152
eraseData(FAT_FAT_TYPE clusterNum)153 void QFat::eraseData(FAT_FAT_TYPE clusterNum)
154 {
155 if (clusterNum == FAT_FAT_TYPE_NOVALUE)
156 return;
157
158 FAT_FAT_TYPE cluster = clusterNum;
159 do {
160 FAT_FAT_TYPE nextCluster = m_fat[cluster];
161 m_fat[cluster] = 0;
162 cluster = nextCluster;
163 } while (cluster && cluster != FAT_FAT_TYPE_NOVALUE);
164 }
165
readData(FAT_FAT_TYPE clusterNum,qint32 maxSize)166 QByteArray QFat::readData(FAT_FAT_TYPE clusterNum, qint32 maxSize)
167 {
168 QByteArray data;
169 if (clusterNum == FAT_FAT_TYPE_NOVALUE)
170 return data;
171
172 char readBuffer[m_clusterSize];
173 qint32 alreadyRead = 0;
174 qint32 justRead;
175 quint16 nextCluster = clusterNum;
176 m_fatFile->seek(m_startOfData + (nextCluster * m_clusterSize));
177 do {
178 if (maxSize == -1 || alreadyRead+m_clusterSize < maxSize)
179 justRead = m_fatFile->read(readBuffer, m_clusterSize);
180 else
181 justRead = m_fatFile->read(readBuffer, maxSize-alreadyRead);
182 alreadyRead += justRead;
183 data.append(readBuffer, justRead);
184
185 FAT_FAT_TYPE oldCluster = nextCluster;
186 nextCluster = m_fat[nextCluster];
187 if (nextCluster != FAT_FAT_TYPE_NOVALUE && nextCluster != oldCluster+1)
188 m_fatFile->seek(m_startOfData + (nextCluster * m_clusterSize));
189 } while (nextCluster && nextCluster != FAT_FAT_TYPE_NOVALUE);
190
191 return data;
192 }
193
writeData(const QByteArray & data,FAT_FAT_TYPE reqCluster)194 FAT_FAT_TYPE QFat::writeData(const QByteArray& data, FAT_FAT_TYPE reqCluster)
195 {
196 FAT_FAT_TYPE startCluster;
197 if (reqCluster == FAT_FAT_TYPE_NOVALUE)
198 startCluster = findFreeCluster();
199 else
200 startCluster = reqCluster;
201 if (startCluster == m_clusterCount)
202 return m_clusterCount;
203
204 quint64 written;
205 quint32 idx = 0;
206 FAT_FAT_TYPE cluster = startCluster;
207 if (checkAndCreate(cluster) != FatNoError)
208 return m_clusterCount;
209 m_fatFile->seek(m_startOfData + (cluster * m_clusterSize));
210 forever {
211 written = m_fatFile->write(data.data() + idx, ((idx + m_clusterSize) < data.size() ? m_clusterSize : data.size() - idx));
212 if (written == m_clusterSize) {
213 idx += written;
214 m_fat[cluster] = 0xff;
215 FAT_FAT_TYPE nextCluster = findFreeCluster();
216 if (nextCluster == m_clusterCount)
217 return m_clusterCount;
218 if (checkAndCreate(nextCluster) != FatNoError)
219 return m_clusterCount;
220 m_fat[cluster] = nextCluster;
221 if (nextCluster != cluster+1)
222 m_fatFile->seek(m_startOfData + (nextCluster * m_clusterSize));
223 cluster = nextCluster;
224 } else {
225 m_fat[cluster] = FAT_FAT_TYPE_NOVALUE;
226 break;
227 }
228 }
229
230 return startCluster;
231 }
232
readTocData(quint16 clusterNum)233 FatTocEntries QFat::readTocData(quint16 clusterNum)
234 {
235 QByteArray a = readData(clusterNum);
236 QDataStream ds(a);
237 FatTocEntries tocs;
238
239 ds >> tocs;
240 return tocs;
241 }
242
writeTocData(const FatTocEntries & toc,FAT_FAT_TYPE cluster)243 FAT_FAT_TYPE QFat::writeTocData(const FatTocEntries &toc, FAT_FAT_TYPE cluster)
244 {
245 QByteArray a;
246 QDataStream ds(&a, QIODevice::WriteOnly);
247 ds << toc;
248
249 return writeData(a, cluster);
250 }
251
setCurrentTocs(const QString & path)252 FatError QFat::setCurrentTocs(const QString &path)
253 {
254 if (path.isEmpty()) {
255 m_curTocs = m_rootToc;
256 m_curTocsPath = "/";
257 m_curTocsCluster = 0;
258 return FatNoError;
259 }
260 if (path == m_curTocsPath && !m_curTocsPath.isEmpty())
261 return FatNoError;
262
263 FatTocEntries curTocs = m_rootToc;
264 FAT_FAT_TYPE curTocCluster = 0;
265
266 QStringList levels = path.split("/", QString::SkipEmptyParts);
267
268 bool found = false;
269 for (int i=0; i<levels.size(); ++i) {
270 for (int j=0; j<curTocs.size(); ++j) {
271 if (curTocs[j].name == levels[i]) {
272 if (curTocs[j].flags & FLAG_FOLDER) {
273 curTocCluster = curTocs[j].startCluster;
274 curTocs = readTocData(curTocCluster);
275 found = true;
276 break;
277 } else {
278 break;
279 }
280 }
281 }
282 if (!found)
283 break;
284 }
285 if (found) {
286 m_curTocs = curTocs;
287 m_curTocsCluster = curTocCluster;
288 m_curTocsPath = path;
289 return FatNoError;
290 } else {
291 m_curTocsPath.clear();
292 return FatDirNotFound;
293 }
294 }
295
getToc(const QString & filename,FatTocEntry & toc)296 FatError QFat::getToc(const QString &filename, FatTocEntry& toc)
297 {
298 FatTocEntry emptyToc;
299 if (filename.isEmpty())
300 return FatFileNotFound;
301
302 QStringList levels = filename.split("/", QString::SkipEmptyParts);
303 QString name = levels.takeLast();
304 QString path = levels.join("/");
305
306 return getToc(path, name, toc);
307 }
308
getToc(const QString & path,const QString & name,FatTocEntry & toc)309 FatError QFat::getToc(const QString &path, const QString& name, FatTocEntry& toc)
310 {
311 FatError ret = setCurrentTocs(path);
312 if (ret != FatNoError) {
313 return ret;
314 }
315
316 for (int j=0; j<m_curTocs.size(); ++j) {
317 if (m_curTocs[j].name == name) {
318 toc = m_curTocs[j];
319 return FatNoError;
320 }
321 }
322 return FatFileNotFound;
323 }
324
getTocEntries(const QString & reqpath,FatTocEntries & tocs)325 FatError QFat::getTocEntries(const QString &reqpath, FatTocEntries& tocs)
326 {
327 FatTocEntry toc;
328
329 if (reqpath.isEmpty() || reqpath == "/") {
330 tocs = m_rootToc;
331 } else {
332 QStringList levels = reqpath.split("/", QString::SkipEmptyParts);
333 QString name = levels.takeLast();
334 QString path = levels.join("/");
335
336 FatError ret;
337 ret = getToc(path, name, toc);
338 if (ret != FatNoError)
339 return FatDirNotFound;
340
341 tocs = readTocData(toc.startCluster);
342 }
343
344 return FatNoError;
345 }
346
347
addToc(const QString & filename,const FatTocEntry & toc)348 FatError QFat::addToc(const QString &filename, const FatTocEntry &toc)
349 {
350 QStringList levels = filename.split("/", QString::SkipEmptyParts);
351 QString name = levels.takeLast();
352 QString path = levels.join("/");
353 return addToc(path, name, toc);
354 }
355
addToc(const QString & path,const QString & name,const FatTocEntry & toc)356 FatError QFat::addToc(const QString &path, const QString& name, const FatTocEntry &toc)
357 {
358 FatError ret = setCurrentTocs(path);
359 if (ret != FatNoError)
360 return ret;
361
362 for (int j=0; j<m_curTocs.size(); ++j) {
363 if (m_curTocs[j].name == name) {
364 m_curTocs.removeAt(j);
365 }
366 }
367
368 m_curTocs.push_back(toc);
369 eraseData(m_curTocsCluster);
370 FAT_FAT_TYPE clnum = writeTocData(m_curTocs, m_curTocsCluster);
371 if (clnum == m_clusterCount)
372 return FatOutOfSpace;
373 if (m_curTocsCluster == 0)
374 m_rootToc = m_curTocs;
375
376 return FatNoError;
377 }
378
deleteToc(const QString & filename)379 FatError QFat::deleteToc(const QString &filename)
380 {
381 QStringList levels = filename.split("/", QString::SkipEmptyParts);
382 QString name = levels.takeLast();
383 QString path = levels.join("/");
384 return deleteToc(path, name);
385 }
386
deleteToc(const QString & path,const QString & name)387 FatError QFat::deleteToc(const QString &path, const QString& name)
388 {
389 FatError ret = setCurrentTocs(path);
390 if (ret != FatNoError)
391 return ret;
392
393 for (int j=0; j<m_curTocs.size(); ++j) {
394 if (m_curTocs[j].name == name) {
395 m_curTocs.removeAt(j);
396 eraseData(m_curTocsCluster);
397 FAT_FAT_TYPE clnum = writeTocData(m_curTocs, m_curTocsCluster);
398 if (clnum == m_clusterCount)
399 return FatOutOfSpace;
400
401 if (m_curTocsCluster == 0)
402 m_rootToc = m_curTocs;
403
404 return FatNoError;
405 }
406 }
407 return FatFileNotFound;
408 }
409
makeDir(const QString & reqpath)410 FatError QFat::makeDir(const QString &reqpath)
411 {
412 FatError ret;
413
414 FatTocEntry toc;
415 FatTocEntries emptyToc;
416 ret = getToc(reqpath, toc);
417 if (ret == FatNoError)
418 return FatDirAlreadyExists;
419
420 QStringList levels = reqpath.split("/", QString::SkipEmptyParts);
421 QString name = levels.takeLast();
422 QString path = levels.join("/");
423
424 FAT_FAT_TYPE clnum = writeTocData(emptyToc, FAT_FAT_TYPE_NOVALUE);
425 if (clnum == m_clusterCount)
426 return FatOutOfSpace;
427
428 toc.flags = FLAG_FOLDER;
429 QDateTime dt = QDateTime::currentDateTime();
430 toc.creationTimestamp = dt.toTime_t();
431 toc.modificationTimestamp = dt.toTime_t();
432 toc.size = 0;
433 toc.name = name;
434 toc.startCluster = clnum;
435
436 if((ret = addToc(path, name, toc)) != FatNoError) {
437 eraseData(clnum);
438 return ret;
439 }
440 return FatNoError;
441 }
442
makeDirRecursive(const QString & reqpath)443 FatError QFat::makeDirRecursive(const QString &reqpath)
444 {
445 FatError ret;
446
447 FatTocEntry toc;
448 ret = getToc(reqpath, toc);
449 if (ret == FatNoError)
450 return FatDirAlreadyExists;
451
452 QString partPath;
453 int i = 0;
454 QStringList levels = reqpath.split("/", QString::SkipEmptyParts);
455
456 while (i<levels.size()) {
457 partPath = levels[0];
458 for (int j=1; j<=i; ++j)
459 partPath += "/" + levels[j];
460 ret = makeDir(partPath);
461 if (ret != FatNoError && ret != FatDirAlreadyExists)
462 return ret;
463 ++i;
464 }
465
466 return FatNoError;
467 }
468
removeDir(const QString & reqpath)469 FatError QFat::removeDir(const QString& reqpath)
470 {
471 FatError ret;
472 FatTocEntry toc;
473
474 ret = setCurrentTocs(reqpath);
475 if (ret != FatNoError)
476 return ret;
477 if (m_curTocs.size())
478 return FatDirNotEmpty;
479
480 ret = getToc(reqpath, toc);
481 if (ret != FatNoError)
482 return ret;
483
484 eraseData(toc.startCluster);
485 ret = deleteToc(reqpath);
486 if (ret != FatNoError)
487 return ret;
488
489 return FatNoError;
490 }
491
removeDirRecursive(const QString & reqpath)492 FatError QFat::removeDirRecursive(const QString& reqpath)
493 {
494 FatError ret;
495
496 ret = setCurrentTocs(reqpath);
497 if (ret != FatNoError)
498 return ret;
499 if (m_curTocs.size())
500 return FatDirNotEmpty;
501
502 QString partPath = reqpath;
503 while (!partPath.isEmpty()) {
504 ret = removeDir(partPath);
505 if (ret != FatNoError)
506 return ret;
507
508 QStringList levels = partPath.split("/", QString::SkipEmptyParts);
509 levels.takeLast();
510 partPath = levels.join("/");
511 }
512
513 return FatNoError;
514 }
515
removeFile(const QString & reqpath)516 FatError QFat::removeFile(const QString& reqpath)
517 {
518 FatError ret;
519 FatTocEntry toc;
520
521 ret = getToc(reqpath, toc);
522 if (ret != FatNoError)
523 return ret;
524
525 eraseData(toc.startCluster);
526 ret = deleteToc(reqpath);
527 if (ret != FatNoError)
528 return ret;
529
530 return FatNoError;
531 }
532
statusToc(const QString & path,int indent)533 QString QFat::statusToc(const QString& path, int indent)
534 {
535 QString ret;
536 QString fill;
537 fill.fill(' ', indent*2);
538
539 FatTocEntries curToc;
540 getTocEntries(path, curToc);
541
542 for (int i=0; i<curToc.size(); ++i) {
543 if (curToc[i].flags & FLAG_FILE) {
544 ret += QString("%3(%4) %1\t%2\n").arg(curToc[i].name).arg(curToc[i].size).arg(fill).arg(curToc[i].startCluster, sizeof(FAT_FAT_TYPE)*2, 16);
545 } else if (curToc[i].flags & FLAG_FOLDER) {
546 ret += QString("%3(%4) %1/\n").arg(curToc[i].name).arg(fill).arg(curToc[i].startCluster, sizeof(FAT_FAT_TYPE)*2, 16);
547 ret += statusToc(path + curToc[i].name + "/", indent + 1);
548 }
549 }
550
551 return ret;
552 }
553
status()554 QString QFat::status()
555 {
556 QString ret;
557
558 int countFat = 0;
559 int col = 0;
560 ret = QString("FAT in use:\n ");
561 for (int i=0; i<m_fat.size(); ++i) {
562 if (m_fat[i]) {
563 ret += QString("(%1)%2").arg(i, sizeof(FAT_FAT_TYPE)*2, 16).arg(m_fat[i], sizeof(FAT_FAT_TYPE)*2, 16);
564 if (col<3) {
565 ++col;
566 ret += " ";
567 } else {
568 col = 0;
569 ret += "\n ";
570 }
571 countFat++;
572 }
573 }
574 if (col)
575 ret += "\n";
576 ret += QString(" Count = %1\n").arg(countFat);
577 ret += QString(" Fat Size: %1\n").arg(m_clusterCount*sizeof(FAT_FAT_TYPE));
578 ret += "\n";
579 ret += "TOC:\n";
580 ret += statusToc("/", 1);
581 ret += "\n" + QString("FatFile Size: %1\n").arg(m_fatFile->size());
582
583
584 return ret;
585 }
586