1 /*
2 
3     This file is part of the KFloppy program, part of the KDE project
4 
5     Copyright (C) 2002 Adriaan de Groot <groot@kde.org>
6     Copyright (C) 2004, 2005 Nicolas GOUTTE <goutte@kde.org>
7     Copyright (C) 2015, 2016 Wolfgang Bauer <wbauer@tmo.at>
8 
9     This program is free software; you can redistribute it and/or modify
10     it under the terms of the GNU General Public License as published by
11     the Free Software Foundation, version 2.
12 
13     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17 
18     You should have received a copy of the GNU General Public License
19     along with this program; if not, write to the Free Software
20     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 
22 */
23 
24 #include "format.h"
25 
26 #include <stdlib.h>
27 #include <unistd.h>
28 
29 #include "qplatformdefs.h"
30 #include <QRegExp>
31 #include <QStandardPaths>
32 #include <QTimer>
33 
34 #include <KLocalizedString>
35 #include <KProcess>
36 
37 static QStringList extPath = QStringList();
38 
findExecutable(const QString & e)39 /* static */ QString findExecutable(const QString &e)
40 {
41     if (extPath.isEmpty()) {
42         QStringList path = QString::fromLocal8Bit(qgetenv("PATH")).split(QStringLiteral(":"));
43         path.append(QStringLiteral("/usr/sbin"));
44         path.append(QStringLiteral("/sbin"));
45         extPath = path;
46     }
47 
48     return QStandardPaths::findExecutable(e, extPath);
49 }
50 
KFAction(QObject * parent)51 KFAction::KFAction(QObject *parent)
52     : QObject(parent)
53 {
54     DEBUGSETUP;
55 }
56 
~KFAction()57 KFAction::~KFAction()
58 {
59     DEBUGSETUP;
60     quit();
61 }
62 
quit()63 /* slot */ void KFAction::quit()
64 {
65     DEBUGSETUP;
66 }
67 
exec()68 /* slot */ void KFAction::exec()
69 {
70     DEBUGSETUP;
71 }
72 
73 class KFActionQueue_p
74 {
75 public:
76     QList<KFAction *> list;
77 };
78 
KFActionQueue(QObject * parent)79 KFActionQueue::KFActionQueue(QObject *parent)
80     : KFAction(parent)
81     , d(new KFActionQueue_p)
82 {
83     DEBUGSETUP;
84 }
85 
~KFActionQueue()86 KFActionQueue::~KFActionQueue()
87 {
88     DEBUGSETUP;
89     qDeleteAll(d->list);
90     d->list.clear();
91     delete d;
92 }
93 
queue(KFAction * p)94 void KFActionQueue::queue(KFAction *p)
95 {
96     DEBUGSETUP;
97 
98     d->list.append(p);
99     DEBUGS(p->objectName());
100 }
101 
exec()102 /* virtual */ void KFActionQueue::exec()
103 {
104     DEBUGSETUP;
105 
106     actionDone(nullptr, true);
107 }
108 
actionDone(KFAction * p,bool success)109 /* slot */ void KFActionQueue::actionDone(KFAction *p, bool success)
110 {
111     DEBUGSETUP;
112 
113     if (p) {
114         if (!d->list.isEmpty() && d->list.first() == p) {
115             d->list.removeFirst();
116             delete p;
117         } else {
118             DEBUGS("Strange pointer received.");
119             Q_EMIT done(this, false);
120             return;
121         }
122     } else {
123         DEBUGS("Starting action queue.");
124     }
125 
126     if (!success) {
127         DEBUGS("Action failed.");
128         Q_EMIT done(this, false);
129         return;
130     }
131 
132     KFAction *next = d->list.isEmpty() ? nullptr : d->list.first();
133     if (!next) {
134         Q_EMIT done(this, true);
135     } else {
136         qCDebug(KFLOPPY_LOG) << "Running action " << next->objectName();
137         QObject::connect(next, &KFAction::done, this, &KFActionQueue::actionDone);
138         // Propagate signals
139         QObject::connect(next, &KFAction::status, this, &KFAction::status);
140         QTimer::singleShot(0, next, &KFAction::exec);
141     }
142 }
143 
144 // Here we have names of devices. The variable
145 // names are basically the linux device names,
146 // replace with whatever your OS needs instead.
147 //
148 //
149 #ifdef ANY_LINUX
150 
151 const char *const fd0H1440[] = {"/dev/fd0u1440", "/dev/floppy/0u1440", "/dev/fd0h1440", "/dev/fd0H1440", "/dev/fd0", nullptr};
152 const char *const fd0D720[] = {"/dev/fd0u720", "/dev/floppy/0u720", "/dev/fd0D720", "/dev/fd0h720", "/dev/fd0", nullptr};
153 const char *const fd0h1200[] = {"/dev/fd0h1200", "/dev/floppy/0h1200", "/dev/fd0", nullptr};
154 const char *const fd0h360[] = {"/dev/fd0u360", "/dev/floppy/0u360", "/dev/fd0h360", "/dev/fd0d360", "/dev/fd0", nullptr};
155 
156 const char *const fd1H1440[] = {"/dev/fd1u1440", "/dev/floppy/1u1440", "/dev/fd1h1440", "/dev/fd1H1440", "/dev/fd1", nullptr};
157 const char *const fd1D720[] = {"/dev/fd1u720", "/dev/floppy/1u720", "/dev/fd1D720", "/dev/fd1h720", "/dev/fd1", nullptr};
158 const char *const fd1h1200[] = {"/dev/fd1h1200", "/dev/floppy/1h1200", "/dev/fd1", nullptr};
159 const char *const fd1h360[] = {"/dev/fd1u360", "/dev/floppy/1u360", "/dev/fd1h360", "/dev/fd1d360", "/dev/fd1", nullptr};
160 
161 const char *const fd0auto[] = {"/dev/fd0", nullptr};
162 const char *const fd1auto[] = {"/dev/fd1", nullptr};
163 
164 #endif
165 
166 #ifdef ANY_BSD
167 const char *const fd0[] = {"/dev/fd0", nullptr};
168 const char *const fd1[] = {"/dev/fd1", nullptr};
169 #endif
170 
171 // Next we have a table of device names and characteristics.
172 // These are ordered according to 2*densityIndex+deviceIndex,
173 // ie. primary (0) 1440K (0) is first, then secondary (1) 1440K is
174 // second, down to secondary (1) 360k (4) in position 3*2+1=7.
175 //
176 //
177 // Note that the data originally contained in KFloppy was
178 // patently false, so most of this is fake. I guess no one ever
179 // formatted a 5.25" floppy.
180 //
181 // The flags field is unused in this implementation.
182 //
183 //
184 const fdinfo fdtable[] = {
185 #ifdef ANY_LINUX
186     // device  drv blks trk flg
187     {fd0H1440, 0, 1440, 80, 0},
188     {fd1H1440, 1, 1440, 80, 0},
189     {fd0D720, 0, 720, 80, 0},
190     {fd1D720, 1, 720, 80, 0},
191     {fd0h1200, 0, 1200, 80, 0},
192     {fd1h1200, 1, 1200, 80, 0},
193     {fd0h360, 0, 360, 40, 0},
194     {fd1h360, 1, 360, 40, 0},
195     {fd0auto, 0, 0, 80, 0},
196     {fd1auto, 1, 0, 80, 0},
197 #endif
198 
199 #ifdef ANY_BSD
200     // Instead of the number of tracks, which is
201     // unneeded, we record the
202     // number of F's printed during an fdformat
203     {fd0, 0, 1440, 40, 0},
204     {fd1, 1, 1440, 40, 0},
205     {fd0, 0, 720, 40, 0},
206     {fd1, 1, 720, 40, 0},
207     {fd0, 0, 1200, 40, 0},
208     {fd1, 1, 1200, 40, 0},
209     {fd0, 0, 360, 40, 0},
210     {fd1, 1, 360, 40, 0},
211 #endif
212     {nullptr, 0, 0, 0, 0}};
213 
FloppyAction(QObject * p)214 FloppyAction::FloppyAction(QObject *p)
215     : KFAction(p)
216     , deviceInfo(nullptr)
217     , theProcess(nullptr)
218 {
219     DEBUGSETUP;
220 }
221 
quit()222 void FloppyAction::quit()
223 {
224     DEBUGSETUP;
225     delete theProcess;
226     theProcess = nullptr;
227 
228     KFAction::quit();
229 }
230 
configureDevice(const QString & newDeviceName)231 bool FloppyAction::configureDevice(const QString &newDeviceName)
232 {
233     deviceInfo = nullptr; // We have not any idea what the device is
234     deviceName = newDeviceName;
235     return true; // No problem!
236 }
237 
configureDevice(int drive,int density)238 bool FloppyAction::configureDevice(int drive, int density)
239 {
240     DEBUGSETUP;
241     const char *devicename = nullptr;
242 
243     deviceInfo = nullptr;
244     deviceName.clear();
245 
246     if ((drive < 0) || (drive > 1)) {
247         Q_EMIT status(i18n("Unexpected drive number %1.", drive), -1);
248         return false;
249     }
250 
251     const fdinfo *deviceinfo = fdtable;
252     for (; deviceinfo && (deviceinfo->devices); deviceinfo++) {
253         if (deviceinfo->blocks != density)
254             continue;
255     }
256     if (!deviceinfo) {
257         Q_EMIT status(i18n("Unexpected density number %1.", density), -1);
258         return false;
259     }
260 
261     deviceinfo = fdtable;
262     for (; deviceinfo && (deviceinfo->devices); deviceinfo++) {
263         if (deviceinfo->blocks != density)
264             continue;
265         if (deviceinfo->drive == drive)
266             break;
267     }
268 
269     if (!deviceinfo || !deviceinfo->devices) {
270         Q_EMIT status(i18n("Cannot find a device for drive %1 and density %2.", drive, density), -1);
271         return false;
272     }
273 
274     for (const char *const *devices = deviceinfo->devices; *devices; devices++) {
275         if (QT_ACCESS(*devices, W_OK) >= 0) {
276             qCDebug(KFLOPPY_LOG) << "Found device " << *devices;
277             devicename = *devices;
278             break;
279         }
280     }
281 
282     if (!devicename) {
283         const QString str = i18n(
284             "Cannot access %1\nMake sure that the device exists and that "
285             "you have write permission to it.",
286             QLatin1String(deviceinfo->devices[0]));
287         Q_EMIT status(str, -1);
288         return false;
289     }
290 
291     deviceName = QLatin1String(devicename);
292     deviceInfo = deviceinfo;
293 
294     return true;
295 }
296 
readStdOut()297 void FloppyAction::readStdOut()
298 {
299     processStdOut(QString::fromUtf8(theProcess->readAllStandardOutput()));
300 }
301 
readStdErr()302 void FloppyAction::readStdErr()
303 {
304     processStdOut(QString::fromUtf8(theProcess->readAllStandardError()));
305 }
306 
processDone(int exitCode,QProcess::ExitStatus exitStatus)307 void FloppyAction::processDone(int exitCode, QProcess::ExitStatus exitStatus)
308 {
309     DEBUGSETUP;
310 
311     if (exitStatus == QProcess::NormalExit) {
312         if (exitCode == 0) {
313             Q_EMIT status(QString(), 100);
314             Q_EMIT done(this, true);
315         } else {
316             Q_EMIT status(i18n("The program %1 terminated with an error.", theProcessName), 100);
317             Q_EMIT done(this, false);
318         }
319     } else {
320         Q_EMIT status(i18n("The program %1 terminated abnormally.", theProcessName), 100);
321         Q_EMIT done(this, false);
322     }
323 }
324 
processStdOut(const QString & s)325 void FloppyAction::processStdOut(const QString &s)
326 {
327     qCDebug(KFLOPPY_LOG) << "stdout:" << s;
328 }
329 
processStdErr(const QString & s)330 void FloppyAction::processStdErr(const QString &s)
331 {
332     processStdOut(s);
333 }
334 
startProcess()335 bool FloppyAction::startProcess()
336 {
337     DEBUGSETUP;
338 
339     connect(theProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(processDone(int, QProcess::ExitStatus)));
340     connect(theProcess, &QProcess::readyReadStandardOutput, this, &FloppyAction::readStdOut);
341     connect(theProcess, &QProcess::readyReadStandardError, this, &FloppyAction::readStdErr);
342 
343     theProcess->setEnv(QStringLiteral("LC_ALL"), QStringLiteral("C")); // We need the untranslated output of the tool
344     theProcess->setOutputChannelMode(KProcess::SeparateChannels);
345     theProcess->start();
346     return (theProcess->exitStatus() == QProcess::NormalExit);
347 }
348 
349 /* static */ QString FDFormat::fdformatName = QString();
350 
FDFormat(QObject * p)351 FDFormat::FDFormat(QObject *p)
352     : FloppyAction(p)
353     , doVerify(true)
354 {
355     DEBUGSETUP;
356     theProcessName = QStringLiteral("fdformat");
357     setObjectName(QStringLiteral("FDFormat"));
358 }
359 
runtimeCheck()360 /* static */ bool FDFormat::runtimeCheck()
361 {
362     fdformatName = findExecutable(QStringLiteral("fdformat"));
363     return (!fdformatName.isEmpty());
364 }
365 
configure(bool v)366 bool FDFormat::configure(bool v)
367 {
368     doVerify = v;
369     return true;
370 }
371 
exec()372 /* virtual */ void FDFormat::exec()
373 {
374     DEBUGSETUP;
375 
376     if (!deviceInfo || deviceName.isEmpty()) {
377         Q_EMIT status(i18n("Internal error: device not correctly defined."), -1);
378         Q_EMIT done(this, false);
379         return;
380     }
381 
382     if (fdformatName.isEmpty()) {
383         Q_EMIT status(i18n("Cannot find fdformat."), -1);
384         Q_EMIT done(this, false);
385         return;
386     }
387 
388     delete theProcess;
389     theProcess = new KProcess;
390 
391     formatTrackCount = 0;
392 
393     *theProcess << fdformatName;
394 
395     // Common to Linux and BSD, others may differ
396     if (!doVerify) {
397         *theProcess << QStringLiteral("-n");
398     }
399 
400 #ifdef ANY_BSD
401     *theProcess << QStringLiteral("-y") << QStringLiteral("-f") << QString::number(deviceInfo->blocks);
402 #elif defined(ANY_LINUX)
403     // No Linux-specific flags
404 #endif
405 
406     // Common to Linux and BSD, others may differ
407     *theProcess << deviceName;
408 
409     if (!startProcess()) {
410         Q_EMIT status(i18n("Could not start fdformat."), -1);
411         Q_EMIT done(this, false);
412     }
413 
414     // Now depend on fdformat running and producing output.
415 }
416 
417 // Parse some output from the fdformat process. Lots of
418 // #ifdefs here to account for variations in the basic
419 // fdformat. Uses gotos to branch to whatever error message we
420 // need, since the messages can be standardized across OSsen.
421 //
422 //
processStdOut(const QString & s)423 void FDFormat::processStdOut(const QString &s)
424 {
425     DEBUGSETUP;
426 
427 #ifdef ANY_BSD
428     if (s[0] == QLatin1Char('F')) {
429         formatTrackCount++;
430         Q_EMIT status(QString(), formatTrackCount * 100 / deviceInfo->tracks);
431     } else if (s[0] == QLatin1Char('E')) {
432         Q_EMIT status(i18n("Error formatting track %1.", formatTrackCount), -1);
433     } else {
434         if (s.contains(QLatin1String("ioctl(FD_FORM)"))) {
435             Q_EMIT status(i18n("Cannot access floppy or floppy drive.\n"
436                                "Please insert a floppy and make sure that you "
437                                "have selected a valid floppy drive."),
438                           -1);
439             return;
440         }
441         if (s.indexOf(QLatin1String("/dev/")) >= 0) {
442             Q_EMIT status(s, -1);
443             return;
444         }
445         DEBUGS(s);
446     }
447 #elif defined(ANY_LINUX)
448     DEBUGS(s);
449     QRegExp regexp(QStringLiteral("([0-9]+)"));
450     if (s.startsWith(QLatin1String("bad data at cyl")) || s.contains(QLatin1String("Problem reading cylinder"))) {
451         if (regexp.indexIn(s) > -1) {
452             const int track = regexp.cap(1).toInt();
453             Q_EMIT status(i18n("Low-level formatting error at track %1.", track), -1);
454         } else {
455             // This error should not happen
456             Q_EMIT status(i18n("Low-level formatting error: %1", s), -1);
457         }
458         return;
459     } else if (s.contains(QLatin1String("ioctl(FDFMTBEG)"))) {
460         Q_EMIT status(i18n("Cannot access floppy or floppy drive.\n"
461                            "Please insert a floppy and make sure that you "
462                            "have selected a valid floppy drive."),
463                       -1);
464         return;
465     } else if (s.contains(QLatin1String("busy"))) // "Device or resource busy"
466     {
467         Q_EMIT status(i18n("Device busy.\nPerhaps you need to unmount the floppy first."), -1);
468         return;
469     }
470     // Be careful to leave "iotcl" as last before checking numbers
471     else if (s.contains(QLatin1String("ioctl"))) {
472         Q_EMIT status(i18n("Low-level format error: %1", s), -1);
473         return;
474     }
475     // Check for numbers at last (as /dev/fd0u1440 has numbers too)
476     else if (regexp.indexIn(s) > -1) {
477         // Normal track number (formatting or verifying)
478         const int p = regexp.cap(1).toInt();
479         if ((p >= 0) && (p < deviceInfo->tracks)) {
480             Q_EMIT status(QString(), p * 100 / deviceInfo->tracks);
481         }
482     }
483 #endif
484     return;
485 }
486 
487 /* static */ QString DDZeroOut::m_ddName = QString();
488 
DDZeroOut(QObject * p)489 DDZeroOut::DDZeroOut(QObject *p)
490     : FloppyAction(p)
491 {
492     qCDebug(KFLOPPY_LOG) << k_funcinfo;
493     theProcessName = QStringLiteral("dd");
494     setObjectName(QStringLiteral("DD"));
495 }
496 
runtimeCheck()497 /* static */ bool DDZeroOut::runtimeCheck()
498 {
499     m_ddName = findExecutable(QStringLiteral("dd"));
500     return (!m_ddName.isEmpty());
501 }
502 
exec()503 /* virtual */ void DDZeroOut::exec()
504 {
505     qCDebug(KFLOPPY_LOG) << k_funcinfo;
506 
507     if (deviceName.isEmpty()) {
508         Q_EMIT status(i18n("Internal error: device not correctly defined."), -1);
509         Q_EMIT done(this, false);
510         return;
511     }
512 
513     if (m_ddName.isEmpty()) {
514         Q_EMIT status(i18n("Cannot find dd."), -1);
515         Q_EMIT done(this, false);
516         return;
517     }
518 
519     delete theProcess;
520     theProcess = new KProcess;
521 
522     *theProcess << m_ddName;
523 
524     *theProcess << QStringLiteral("if=/dev/zero");
525     *theProcess << QStringLiteral("of=") + deviceName;
526 
527     if (!startProcess()) {
528         Q_EMIT status(i18n("Could not start dd."), -1);
529         Q_EMIT done(this, false);
530     }
531 }
532 
processDone(int exitCode,QProcess::ExitStatus exitStatus)533 void DDZeroOut::processDone(int exitCode, QProcess::ExitStatus exitStatus)
534 {
535     Q_UNUSED(exitCode);
536     Q_UNUSED(exitStatus);
537 
538     qCDebug(KFLOPPY_LOG) << k_funcinfo;
539 
540     /**
541      * As we do not give a number of blocks to dd(1), it will stop
542      * with the error "No space left on device"
543      *
544      * ### TODO: really check if the exit is not on an other error and then abort the formatting
545      */
546     Q_EMIT status(QString(), 100);
547     Q_EMIT done(this, true);
548 }
549 
550 /* static */ QString FATFilesystem::newfs_fat = QString();
551 
FATFilesystem(QObject * parent)552 FATFilesystem::FATFilesystem(QObject *parent)
553     : FloppyAction(parent)
554 {
555     DEBUGSETUP;
556     runtimeCheck();
557     theProcessName = newfs_fat;
558     setObjectName(QStringLiteral("FATFilesystem"));
559 }
560 
runtimeCheck()561 /* static */ bool FATFilesystem::runtimeCheck()
562 {
563     DEBUGSETUP;
564 
565 #ifdef ANY_BSD
566     newfs_fat = findExecutable(QStringLiteral("newfs_msdos"));
567 #elif defined(ANY_LINUX)
568     newfs_fat = findExecutable(QStringLiteral("mkdosfs"));
569 #else
570     return false;
571 #endif
572 
573     return !newfs_fat.isEmpty();
574 }
575 
configure(bool v,bool l,const QString & lbl)576 bool FATFilesystem::configure(bool v, bool l, const QString &lbl)
577 {
578     doVerify = v;
579     doLabel = l;
580     if (l)
581         label = lbl.simplified();
582     else
583         label.clear();
584 
585     return true;
586 }
587 
exec()588 void FATFilesystem::exec()
589 {
590     DEBUGSETUP;
591 
592     if (
593 #ifdef ANY_BSD // BSD needs the deviceInfo for the block count
594         !deviceInfo ||
595 #endif
596         deviceName.isEmpty()) {
597         Q_EMIT status(i18n("Internal error: device not correctly defined."), -1);
598         Q_EMIT done(this, false);
599         return;
600     }
601 
602     if (newfs_fat.isEmpty()) {
603         Q_EMIT status(i18n("Cannot find a program to create FAT filesystems."), -1);
604         Q_EMIT done(this, false);
605         return;
606     }
607 
608     delete theProcess;
609     KProcess *p = theProcess = new KProcess;
610 
611     *p << newfs_fat;
612 #ifdef ANY_BSD
613     *p << QStringLiteral("-f") << QString::number(deviceInfo->blocks);
614     if (doLabel) {
615         *p << QStringLiteral("-L") << label;
616     }
617 #else
618 #ifdef ANY_LINUX
619     if (doLabel) {
620         *p << QStringLiteral("-n") << label;
621     }
622     if (doVerify) {
623         *p << QStringLiteral("-c");
624     }
625 #endif
626 #endif
627     *p << deviceName;
628 
629     if (!startProcess()) {
630         Q_EMIT status(i18n("Cannot start FAT format program."), -1);
631         Q_EMIT done(this, false);
632     }
633 }
634 
processStdOut(const QString & s)635 void FATFilesystem::processStdOut(const QString &s)
636 {
637 #ifdef ANY_BSD
638     // ### TODO: do some checks
639 #elif defined(ANY_LINUX)
640     qCDebug(KFLOPPY_LOG) << s;
641     if (s.contains(QLatin1String("mounted"))) // "/dev/fd0 contains a mounted filesystem"
642     {
643         Q_EMIT status(i18n("Floppy is mounted.\nYou need to unmount the floppy first."), -1);
644         return;
645     } else if (s.contains(QLatin1String("busy"))) // "Device or resource busy"
646     {
647         Q_EMIT status(i18n("Device busy.\nPerhaps you need to unmount the floppy first."), -1);
648         return;
649     } else if (s.contains(QLatin1String("denied"))) // "Permission denied"
650     {
651         Q_EMIT status(s, -1);
652         return;
653     }
654 #if 0
655     else if ( s.find( "mkdosfs" ) != -1 ) // DEBUG: get the program header and show it!
656     {
657         Q_EMIT status( s, -1 );
658         return;
659     }
660 #endif
661 #endif
662 }
663 
664 #ifdef ANY_BSD
665 
666 /* static */ QString UFSFilesystem::newfs = QString();
667 
UFSFilesystem(QObject * parent)668 UFSFilesystem::UFSFilesystem(QObject *parent)
669     : FloppyAction(parent)
670 {
671     DEBUGSETUP;
672     runtimeCheck();
673     theProcessName = newfs;
674     setObjectName(QStringLiteral("UFSFilesystem"));
675 }
676 
runtimeCheck()677 /* static */ bool UFSFilesystem::runtimeCheck()
678 {
679     DEBUGSETUP;
680 
681     newfs = findExecutable(QStringLiteral("newfs"));
682 
683     return !newfs.isEmpty();
684 }
685 
exec()686 void UFSFilesystem::exec()
687 {
688     DEBUGSETUP;
689 
690     if (deviceName.isEmpty()) {
691         Q_EMIT status(i18n("Internal error: device not correctly defined."), -1);
692         Q_EMIT done(this, false);
693         return;
694     }
695 
696     if (newfs.isEmpty()) {
697         Q_EMIT status(i18nc("BSD", "Cannot find a program to create UFS filesystems."), -1);
698         Q_EMIT done(this, false);
699         return;
700     }
701 
702     delete theProcess;
703     KProcess *p = theProcess = new KProcess;
704 
705     *p << newfs;
706 
707     // ### TODO: is it still needed? (FreeBSD 5.3's man page says: "For backward compatibility.")
708     if (deviceInfo)
709         *p << QStringLiteral("-T") << QStringLiteral("fd%1").arg(deviceInfo->blocks);
710 
711     *p << deviceName;
712 
713     if (!startProcess()) {
714         Q_EMIT status(i18nc("BSD", "Cannot start UFS format program."), -1);
715         Q_EMIT done(this, false);
716     }
717 }
718 #endif
719 
720 /* static */ QString Ext2Filesystem::newfs = QString();
721 
Ext2Filesystem(QObject * parent)722 Ext2Filesystem::Ext2Filesystem(QObject *parent)
723     : FloppyAction(parent)
724 {
725     DEBUGSETUP;
726     runtimeCheck();
727     theProcessName = QStringLiteral("mke2fs");
728     setObjectName(QStringLiteral("Ext2Filesystem"));
729 }
730 
runtimeCheck()731 /* static */ bool Ext2Filesystem::runtimeCheck()
732 {
733     DEBUGSETUP;
734 
735     newfs = findExecutable(QStringLiteral("mke2fs"));
736 
737     return !newfs.isEmpty();
738 }
739 
configure(bool v,bool l,const QString & lbl)740 bool Ext2Filesystem::configure(bool v, bool l, const QString &lbl)
741 {
742     doVerify = v;
743     doLabel = l;
744     if (l) {
745         label = lbl.trimmed();
746     } else {
747         label.clear();
748     }
749 
750     return true;
751 }
752 
exec()753 void Ext2Filesystem::exec()
754 {
755     DEBUGSETUP;
756 
757     if (
758 #ifdef ANY_BSD // BSD needs the deviceInfo for the block count
759         !deviceInfo ||
760 #endif
761         deviceName.isEmpty()) {
762         Q_EMIT status(i18n("Internal error: device not correctly defined."), -1);
763         Q_EMIT done(this, false);
764         return;
765     }
766 
767     if (newfs.isEmpty()) {
768         Q_EMIT status(i18n("Cannot find a program to create ext2 filesystems."), -1);
769         Q_EMIT done(this, false);
770         return;
771     }
772 
773     delete theProcess;
774     KProcess *p = theProcess = new KProcess;
775 
776     *p << newfs;
777     *p << QStringLiteral("-q");
778     if (doVerify)
779         *p << QStringLiteral("-c");
780     if (doLabel)
781         *p << QStringLiteral("-L") << label;
782 
783     *p << deviceName;
784 
785     if (!startProcess()) {
786         Q_EMIT status(i18n("Cannot start ext2 format program."), -1);
787         Q_EMIT done(this, false);
788     }
789 }
790 
processStdOut(const QString & s)791 void Ext2Filesystem::processStdOut(const QString &s)
792 {
793 #ifdef ANY_BSD
794     // ### TODO: do some checks
795 #elif defined(ANY_LINUX)
796     qCDebug(KFLOPPY_LOG) << s;
797     if (s.contains(QLatin1String("mounted"))) // "/dev/fd0 is mounted; will not make a filesystem here!"
798     {
799         Q_EMIT status(i18n("Floppy is mounted.\nYou need to unmount the floppy first."), -1);
800         return;
801     } else if (s.contains(QLatin1String("busy"))) // "Device or resource busy"
802     {
803         Q_EMIT status(i18n("Device busy.\nPerhaps you need to unmount the floppy first."), -1);
804         return;
805     } else if (s.contains(QLatin1String("denied"))) // "Permission denied"
806     {
807         Q_EMIT status(s, -1);
808         return;
809     }
810 #endif
811 }
812 
813 #ifdef ANY_LINUX
814 /* static */ QString MinixFilesystem::newfs = QString();
815 
MinixFilesystem(QObject * parent)816 MinixFilesystem::MinixFilesystem(QObject *parent)
817     : FloppyAction(parent)
818 {
819     DEBUGSETUP;
820     runtimeCheck();
821     theProcessName = QStringLiteral("mkfs.minix");
822     setObjectName(QStringLiteral("Minix2Filesystem"));
823 }
824 
runtimeCheck()825 /* static */ bool MinixFilesystem::runtimeCheck()
826 {
827     DEBUGSETUP;
828 
829     newfs = findExecutable(QStringLiteral("mkfs.minix"));
830 
831     return !newfs.isEmpty();
832 }
833 
configure(bool v,bool l,const QString & lbl)834 bool MinixFilesystem::configure(bool v, bool l, const QString &lbl)
835 {
836     doVerify = v;
837     doLabel = l;
838     if (l) {
839         label = lbl.trimmed();
840     } else {
841         label.clear();
842     }
843 
844     return true;
845 }
846 
exec()847 void MinixFilesystem::exec()
848 {
849     DEBUGSETUP;
850 
851     if (deviceName.isEmpty()) {
852         Q_EMIT status(i18n("Internal error: device not correctly defined."), -1);
853         Q_EMIT done(this, false);
854         return;
855     }
856 
857     if (newfs.isEmpty()) {
858         Q_EMIT status(i18n("Cannot find a program to create Minix filesystems."), -1);
859         Q_EMIT done(this, false);
860         return;
861     }
862 
863     delete theProcess;
864     KProcess *p = theProcess = new KProcess;
865 
866     *p << newfs;
867 
868     // Labeling is not possible
869     if (doVerify)
870         *p << QStringLiteral("-c");
871 
872     *p << deviceName;
873 
874     if (!startProcess()) {
875         Q_EMIT status(i18n("Cannot start Minix format program."), -1);
876         Q_EMIT done(this, false);
877     }
878 }
879 
processStdOut(const QString & s)880 void MinixFilesystem::processStdOut(const QString &s)
881 {
882     qCDebug(KFLOPPY_LOG) << s;
883     if (s.contains(QLatin1String("mounted"))) // "mkfs.minix: /dev/fd0 is mounted; will not make a filesystem here!"
884     {
885         Q_EMIT status(i18n("Floppy is mounted.\nYou need to unmount the floppy first."), -1);
886         return;
887     } else if (s.contains(QLatin1String("busy"))) // "Device or resource busy"
888     {
889         Q_EMIT status(i18n("Device busy.\nPerhaps you need to unmount the floppy first."), -1);
890         return;
891     } else if (s.contains(QLatin1String("denied"))) // "Permission denied"
892     {
893         Q_EMIT status(s, -1);
894         return;
895     }
896 }
897 
898 #endif
899