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