annotate data/fileio/BZipFileDevice.cpp @ 1381:ce08318aad83

Fixes to usage of fdopen, avoiding double-close in particular
author Chris Cannam
date Tue, 21 Feb 2017 21:08:14 +0000
parents b3cb0edc25cd
children f204f2fcb15e
rev   line source
Chris@148 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@148 2
Chris@148 3 /*
Chris@148 4 Sonic Visualiser
Chris@148 5 An audio file viewer and annotation editor.
Chris@148 6 Centre for Digital Music, Queen Mary, University of London.
Chris@148 7 This file copyright 2006 Chris Cannam.
Chris@148 8
Chris@148 9 This program is free software; you can redistribute it and/or
Chris@148 10 modify it under the terms of the GNU General Public License as
Chris@148 11 published by the Free Software Foundation; either version 2 of the
Chris@148 12 License, or (at your option) any later version. See the file
Chris@148 13 COPYING included with this distribution for more information.
Chris@148 14 */
Chris@148 15
Chris@148 16 #include "BZipFileDevice.h"
Chris@148 17
Chris@148 18 #include <bzlib.h>
Chris@148 19
Chris@148 20 #include <iostream>
Chris@148 21
Chris@843 22 #include "base/Debug.h"
Chris@843 23
Chris@1381 24 #ifdef _MSC_VER
Chris@1381 25 #include <io.h>
Chris@1381 26 #endif
Chris@1381 27
Chris@148 28 BZipFileDevice::BZipFileDevice(QString fileName) :
Chris@148 29 m_fileName(fileName),
Chris@1348 30 m_qfile(fileName),
Chris@148 31 m_file(0),
Chris@148 32 m_bzFile(0),
Chris@207 33 m_atEnd(true),
Chris@207 34 m_ok(true)
Chris@148 35 {
Chris@148 36 }
Chris@148 37
Chris@148 38 BZipFileDevice::~BZipFileDevice()
Chris@148 39 {
Chris@690 40 // SVDEBUG << "BZipFileDevice::~BZipFileDevice(" << m_fileName << ")" << endl;
Chris@148 41 if (m_bzFile) close();
Chris@148 42 }
Chris@148 43
Chris@148 44 bool
Chris@207 45 BZipFileDevice::isOK() const
Chris@207 46 {
Chris@207 47 return m_ok;
Chris@207 48 }
Chris@207 49
Chris@207 50 bool
Chris@148 51 BZipFileDevice::open(OpenMode mode)
Chris@148 52 {
Chris@203 53 setErrorString("");
Chris@203 54
Chris@148 55 if (m_bzFile) {
Chris@148 56 setErrorString(tr("File is already open"));
Chris@148 57 return false;
Chris@148 58 }
Chris@148 59
Chris@148 60 if (mode & Append) {
Chris@148 61 setErrorString(tr("Append mode not supported"));
Chris@207 62 m_ok = false;
Chris@148 63 return false;
Chris@148 64 }
Chris@148 65
Chris@148 66 if ((mode & (ReadOnly | WriteOnly)) == 0) {
Chris@148 67 setErrorString(tr("File access mode not specified"));
Chris@207 68 m_ok = false;
Chris@148 69 return false;
Chris@148 70 }
Chris@148 71
Chris@148 72 if ((mode & ReadOnly) && (mode & WriteOnly)) {
Chris@148 73 setErrorString(tr("Read and write modes both specified"));
Chris@207 74 m_ok = false;
Chris@148 75 return false;
Chris@148 76 }
Chris@148 77
Chris@1381 78 // This is all going to be a bit silly.
Chris@1381 79 //
Chris@1381 80 // We open the file with QFile so as not to have to worry about locale
Chris@1381 81 // support ourselves (especially on Windows). Then we get a fd from
Chris@1381 82 // QFile and "convert" it to a FILE* using fdopen because that is what
Chris@1381 83 // the bz2 library needs for reading and writing an already-open file.
Chris@1381 84 //
Chris@1381 85 // fdopen takes over the fd it is given, and will close it when fclose
Chris@1381 86 // is called. (We must call fclose, because it's needed to avoid
Chris@1381 87 // leaking the file stream structure.)
Chris@1381 88 //
Chris@1381 89 // But QFile will also close its fd, either when we call QFile::close
Chris@1381 90 // or on destruction -- there doesn't seem to be a way to avoid that
Chris@1381 91 // for a file that QFile opened.
Chris@1381 92 //
Chris@1381 93 // So we have to add an extra dup() in to the fdopen to avoid a double
Chris@1381 94 // close.
Chris@1381 95 //
Chris@1381 96 // Note that bz2 will *not* fclose the FILE* it was passed, so we
Chris@1381 97 // don't have a problem with calling both bzWriteClose and fclose.
Chris@1381 98
Chris@148 99 if (mode & WriteOnly) {
Chris@148 100
Chris@1348 101 if (!m_qfile.open(QIODevice::WriteOnly)) {
Chris@1348 102 setErrorString(tr("Failed to open file for writing"));
Chris@1348 103 m_ok = false;
Chris@1348 104 return false;
Chris@1348 105 }
Chris@1348 106
Chris@1381 107 m_file = fdopen(dup(m_qfile.handle()), "wb");
Chris@148 108 if (!m_file) {
Chris@1348 109 setErrorString(tr("Failed to open file handle for writing"));
Chris@1348 110 m_qfile.close();
Chris@207 111 m_ok = false;
Chris@148 112 return false;
Chris@148 113 }
Chris@148 114
Chris@148 115 int bzError = BZ_OK;
Chris@148 116 m_bzFile = BZ2_bzWriteOpen(&bzError, m_file, 9, 0, 0);
Chris@148 117
Chris@148 118 if (!m_bzFile) {
Chris@148 119 fclose(m_file);
Chris@148 120 m_file = 0;
Chris@1348 121 m_qfile.close();
Chris@148 122 setErrorString(tr("Failed to open bzip2 stream for writing"));
Chris@207 123 m_ok = false;
Chris@148 124 return false;
Chris@148 125 }
Chris@148 126
Chris@843 127 // cerr << "BZipFileDevice: opened \"" << m_fileName << "\" for writing" << endl;
Chris@148 128
Chris@148 129 setErrorString(QString());
Chris@148 130 setOpenMode(mode);
Chris@148 131 return true;
Chris@148 132 }
Chris@148 133
Chris@148 134 if (mode & ReadOnly) {
Chris@148 135
Chris@1348 136 if (!m_qfile.open(QIODevice::ReadOnly)) {
Chris@1348 137 setErrorString(tr("Failed to open file for reading"));
Chris@1348 138 m_ok = false;
Chris@1348 139 return false;
Chris@1348 140 }
Chris@1348 141
Chris@1381 142 m_file = fdopen(dup(m_qfile.handle()), "rb");
Chris@148 143 if (!m_file) {
Chris@1348 144 setErrorString(tr("Failed to open file handle for reading"));
Chris@207 145 m_ok = false;
Chris@148 146 return false;
Chris@148 147 }
Chris@148 148
Chris@148 149 int bzError = BZ_OK;
Chris@148 150 m_bzFile = BZ2_bzReadOpen(&bzError, m_file, 0, 0, NULL, 0);
Chris@148 151
Chris@148 152 if (!m_bzFile) {
Chris@148 153 fclose(m_file);
Chris@148 154 m_file = 0;
Chris@1348 155 m_qfile.close();
Chris@148 156 setErrorString(tr("Failed to open bzip2 stream for reading"));
Chris@207 157 m_ok = false;
Chris@148 158 return false;
Chris@148 159 }
Chris@148 160
Chris@843 161 // cerr << "BZipFileDevice: opened \"" << m_fileName << "\" for reading" << endl;
Chris@148 162
Chris@148 163 m_atEnd = false;
Chris@148 164
Chris@148 165 setErrorString(QString());
Chris@148 166 setOpenMode(mode);
Chris@148 167 return true;
Chris@148 168 }
Chris@148 169
Chris@148 170 setErrorString(tr("Internal error (open for neither read nor write)"));
Chris@207 171 m_ok = false;
Chris@148 172 return false;
Chris@148 173 }
Chris@148 174
Chris@148 175 void
Chris@148 176 BZipFileDevice::close()
Chris@148 177 {
Chris@148 178 if (!m_bzFile) {
Chris@148 179 setErrorString(tr("File not open"));
Chris@207 180 m_ok = false;
Chris@148 181 return;
Chris@148 182 }
Chris@148 183
Chris@148 184 int bzError = BZ_OK;
Chris@148 185
Chris@148 186 if (openMode() & WriteOnly) {
Chris@148 187 unsigned int in = 0, out = 0;
Chris@148 188 BZ2_bzWriteClose(&bzError, m_bzFile, 0, &in, &out);
Chris@843 189 // cerr << "Wrote bzip2 stream (in=" << in << ", out=" << out << ")" << endl;
Chris@1381 190 if (bzError != BZ_OK) {
Chris@1381 191 setErrorString(tr("bzip2 stream write close error"));
Chris@1381 192 }
Chris@148 193 fclose(m_file);
Chris@1348 194 m_qfile.close();
Chris@148 195 m_bzFile = 0;
Chris@148 196 m_file = 0;
Chris@207 197 m_ok = false;
Chris@148 198 return;
Chris@148 199 }
Chris@148 200
Chris@148 201 if (openMode() & ReadOnly) {
Chris@148 202 BZ2_bzReadClose(&bzError, m_bzFile);
Chris@148 203 if (bzError != BZ_OK) {
Chris@148 204 setErrorString(tr("bzip2 stream read close error"));
Chris@148 205 }
Chris@148 206 fclose(m_file);
Chris@1348 207 m_qfile.close();
Chris@148 208 m_bzFile = 0;
Chris@148 209 m_file = 0;
Chris@207 210 m_ok = false;
Chris@148 211 return;
Chris@148 212 }
Chris@148 213
Chris@148 214 setErrorString(tr("Internal error (close for neither read nor write)"));
Chris@148 215 return;
Chris@148 216 }
Chris@148 217
Chris@148 218 qint64
Chris@148 219 BZipFileDevice::readData(char *data, qint64 maxSize)
Chris@148 220 {
Chris@148 221 if (m_atEnd) return 0;
Chris@148 222
Chris@148 223 int bzError = BZ_OK;
Chris@1038 224 int read = BZ2_bzRead(&bzError, m_bzFile, data, int(maxSize));
Chris@148 225
Chris@690 226 // SVDEBUG << "BZipFileDevice::readData: requested " << maxSize << ", read " << read << endl;
Chris@148 227
Chris@148 228 if (bzError != BZ_OK) {
Chris@148 229 if (bzError != BZ_STREAM_END) {
Chris@843 230 cerr << "BZipFileDevice::readData: error condition" << endl;
Chris@148 231 setErrorString(tr("bzip2 stream read error"));
Chris@207 232 m_ok = false;
Chris@148 233 return -1;
Chris@148 234 } else {
Chris@690 235 // SVDEBUG << "BZipFileDevice::readData: reached end of file" << endl;
Chris@148 236 m_atEnd = true;
Chris@148 237 }
Chris@148 238 }
Chris@148 239
Chris@148 240 return read;
Chris@148 241 }
Chris@148 242
Chris@148 243 qint64
Chris@148 244 BZipFileDevice::writeData(const char *data, qint64 maxSize)
Chris@148 245 {
Chris@148 246 int bzError = BZ_OK;
Chris@1038 247 BZ2_bzWrite(&bzError, m_bzFile, (void *)data, int(maxSize));
Chris@148 248
Chris@690 249 // SVDEBUG << "BZipFileDevice::writeData: " << maxSize << " to write" << endl;
Chris@148 250
Chris@148 251 if (bzError != BZ_OK) {
Chris@843 252 cerr << "BZipFileDevice::writeData: error condition" << endl;
Chris@148 253 setErrorString("bzip2 stream write error");
Chris@207 254 m_ok = false;
Chris@148 255 return -1;
Chris@148 256 }
Chris@148 257
Chris@690 258 // SVDEBUG << "BZipFileDevice::writeData: wrote " << maxSize << endl;
Chris@148 259
Chris@148 260 return maxSize;
Chris@148 261 }
Chris@148 262