diff options
Diffstat (limited to 'src/fs/io')
-rw-r--r-- | src/fs/io/BufferedOutputStream.cxx | 143 | ||||
-rw-r--r-- | src/fs/io/BufferedOutputStream.hxx | 71 | ||||
-rw-r--r-- | src/fs/io/FileOutputStream.cxx | 135 | ||||
-rw-r--r-- | src/fs/io/FileOutputStream.hxx | 68 | ||||
-rw-r--r-- | src/fs/io/GzipOutputStream.cxx | 106 | ||||
-rw-r--r-- | src/fs/io/GzipOutputStream.hxx | 71 | ||||
-rw-r--r-- | src/fs/io/OutputStream.hxx | 38 | ||||
-rw-r--r-- | src/fs/io/StdioOutputStream.hxx | 45 | ||||
-rw-r--r-- | src/fs/io/TextFile.cxx | 81 | ||||
-rw-r--r-- | src/fs/io/TextFile.hxx | 62 |
10 files changed, 820 insertions, 0 deletions
diff --git a/src/fs/io/BufferedOutputStream.cxx b/src/fs/io/BufferedOutputStream.cxx new file mode 100644 index 000000000..088a3e279 --- /dev/null +++ b/src/fs/io/BufferedOutputStream.cxx @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "BufferedOutputStream.hxx" +#include "OutputStream.hxx" + +#include <stdarg.h> +#include <string.h> +#include <stdio.h> + +bool +BufferedOutputStream::AppendToBuffer(const void *data, size_t size) +{ + auto r = buffer.Write(); + if (r.size < size) + return false; + + memcpy(r.data, data, size); + buffer.Append(size); + return true; +} + +bool +BufferedOutputStream::Write(const void *data, size_t size) +{ + if (gcc_unlikely(last_error.IsDefined())) + return false; + + if (AppendToBuffer(data, size)) + return true; + + if (!Flush()) + return false; + + if (AppendToBuffer(data, size)) + return true; + + return os.Write(data, size, last_error); +} + +bool +BufferedOutputStream::Write(const char *p) +{ + return Write(p, strlen(p)); +} + +bool +BufferedOutputStream::Format(const char *fmt, ...) +{ + if (gcc_unlikely(last_error.IsDefined())) + return false; + + auto r = buffer.Write(); + if (r.IsEmpty()) { + if (!Flush()) + return false; + + r = buffer.Write(); + } + + /* format into the buffer */ + va_list ap; + va_start(ap, fmt); + size_t size = vsnprintf(r.data, r.size, fmt, ap); + va_end(ap); + + if (gcc_unlikely(size >= r.size)) { + /* buffer was not large enough; flush it and try + again */ + + if (!Flush()) + return false; + + r = buffer.Write(); + + if (gcc_unlikely(size >= r.size)) { + /* still not enough space: grow the buffer and + try again */ + r.size = size + 1; + r.data = buffer.Write(r.size); + } + + /* format into the new buffer */ + va_start(ap, fmt); + size = vsnprintf(r.data, r.size, fmt, ap); + va_end(ap); + + /* this time, it must fit */ + assert(size < r.size); + } + + buffer.Append(size); + return true; +} + +bool +BufferedOutputStream::Flush() +{ + if (!Check()) + return false; + + auto r = buffer.Read(); + if (r.IsEmpty()) + return true; + + bool success = os.Write(r.data, r.size, last_error); + if (gcc_likely(success)) + buffer.Consume(r.size); + return success; +} + +bool +BufferedOutputStream::Flush(Error &error) +{ + if (!Check(error)) + return false; + + auto r = buffer.Read(); + if (r.IsEmpty()) + return true; + + bool success = os.Write(r.data, r.size, error); + if (gcc_likely(success)) + buffer.Consume(r.size); + return success; +} diff --git a/src/fs/io/BufferedOutputStream.hxx b/src/fs/io/BufferedOutputStream.hxx new file mode 100644 index 000000000..f2de758a2 --- /dev/null +++ b/src/fs/io/BufferedOutputStream.hxx @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_BUFFERED_OUTPUT_STREAM_HXX +#define MPD_BUFFERED_OUTPUT_STREAM_HXX + +#include "check.h" +#include "Compiler.h" +#include "util/DynamicFifoBuffer.hxx" +#include "util/Error.hxx" + +#include <stddef.h> + +class OutputStream; +class Error; + +class BufferedOutputStream { + OutputStream &os; + + DynamicFifoBuffer<char> buffer; + + Error last_error; + +public: + BufferedOutputStream(OutputStream &_os) + :os(_os), buffer(32768) {} + + bool Write(const void *data, size_t size); + bool Write(const char *p); + + gcc_printf(2,3) + bool Format(const char *fmt, ...); + + gcc_pure + bool Check() const { + return !last_error.IsDefined(); + } + + bool Check(Error &error) const { + if (last_error.IsDefined()) { + error.Set(last_error); + return false; + } else + return true; + } + + bool Flush(); + + bool Flush(Error &error); + +private: + bool AppendToBuffer(const void *data, size_t size); +}; + +#endif diff --git a/src/fs/io/FileOutputStream.cxx b/src/fs/io/FileOutputStream.cxx new file mode 100644 index 000000000..dc4456d1f --- /dev/null +++ b/src/fs/io/FileOutputStream.cxx @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "FileOutputStream.hxx" +#include "fs/FileSystem.hxx" +#include "system/fd_util.h" +#include "util/Error.hxx" + +#ifdef WIN32 + +FileOutputStream::FileOutputStream(Path _path, Error &error) + :path(_path), + handle(CreateFile(path.c_str(), GENERIC_WRITE, 0, nullptr, + TRUNCATE_EXISTING, + FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH, + nullptr)) +{ + if (handle == INVALID_HANDLE_VALUE) + error.FormatLastError("Failed to create %s", path.c_str()); +} + +bool +FileOutputStream::Write(const void *data, size_t size, Error &error) +{ + assert(IsDefined()); + + DWORD nbytes; + if (!WriteFile(handle, data, size, &nbytes, nullptr)) { + error.FormatLastError("Failed to write to %s", path.c_str()); + return false; + } + + if (size_t(nbytes) != size) { + error.FormatLastError(ERROR_DISK_FULL, + "Failed to write to %s", path.c_str()); + return false; + } + + return true; +} + +bool +FileOutputStream::Commit(gcc_unused Error &error) +{ + assert(IsDefined()); + + CloseHandle(handle); + return true; +} + +void +FileOutputStream::Cancel() +{ + assert(IsDefined()); + + CloseHandle(handle); + RemoveFile(path); +} + +#else + +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> + +FileOutputStream::FileOutputStream(Path _path, Error &error) + :path(_path), + fd(open_cloexec(path.c_str(), + O_WRONLY|O_CREAT|O_TRUNC, + 0666)) +{ + if (fd < 0) + error.FormatErrno("Failed to create %s", path.c_str()); +} + +bool +FileOutputStream::Write(const void *data, size_t size, Error &error) +{ + assert(IsDefined()); + + ssize_t nbytes = write(fd, data, size); + if (nbytes < 0) { + error.FormatErrno("Failed to write to %s", path.c_str()); + return false; + } else if ((size_t)nbytes < size) { + error.FormatErrno(ENOSPC, + "Failed to write to %s", path.c_str()); + return false; + } + + return true; +} + +bool +FileOutputStream::Commit(Error &error) +{ + assert(IsDefined()); + + bool success = close(fd) == 0; + fd = -1; + if (!success) + error.FormatErrno("Failed to commit %s", path.c_str()); + + return success; +} + +void +FileOutputStream::Cancel() +{ + assert(IsDefined()); + + close(fd); + fd = -1; + + RemoveFile(path); +} + +#endif diff --git a/src/fs/io/FileOutputStream.hxx b/src/fs/io/FileOutputStream.hxx new file mode 100644 index 000000000..68174ec83 --- /dev/null +++ b/src/fs/io/FileOutputStream.hxx @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_FILE_OUTPUT_STREAM_HXX +#define MPD_FILE_OUTPUT_STREAM_HXX + +#include "check.h" +#include "OutputStream.hxx" +#include "fs/AllocatedPath.hxx" + +#include <assert.h> + +#ifdef WIN32 +#include <windows.h> +#endif + +class Path; + +class FileOutputStream final : public OutputStream { + AllocatedPath path; + +#ifdef WIN32 + HANDLE handle; +#else + int fd; +#endif + +public: + FileOutputStream(Path _path, Error &error); + + ~FileOutputStream() { + if (IsDefined()) + Cancel(); + } + + + bool IsDefined() const { +#ifdef WIN32 + return handle != INVALID_HANDLE_VALUE; +#else + return fd >= 0; +#endif + } + + bool Commit(Error &error); + void Cancel(); + + /* virtual methods from class OutputStream */ + bool Write(const void *data, size_t size, Error &error) override; +}; + +#endif diff --git a/src/fs/io/GzipOutputStream.cxx b/src/fs/io/GzipOutputStream.cxx new file mode 100644 index 000000000..d05645d9e --- /dev/null +++ b/src/fs/io/GzipOutputStream.cxx @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "GzipOutputStream.hxx" +#include "util/Error.hxx" +#include "util/Domain.hxx" + +const Domain zlib_domain("zlib"); + +GzipOutputStream::GzipOutputStream(OutputStream &_next, Error &error) + :next(_next) +{ + z.next_in = nullptr; + z.avail_in = 0; + z.zalloc = Z_NULL; + z.zfree = Z_NULL; + z.opaque = Z_NULL; + + constexpr int windowBits = 15; + constexpr int gzip_encoding = 16; + + int result = deflateInit2(&z, Z_DEFAULT_COMPRESSION, Z_DEFLATED, + windowBits | gzip_encoding, + 8, Z_DEFAULT_STRATEGY); + if (result != Z_OK) { + z.opaque = this; + error.Set(zlib_domain, result, zError(result)); + } +} + +GzipOutputStream::~GzipOutputStream() +{ + if (IsDefined()) + deflateEnd(&z); +} + +bool +GzipOutputStream::Flush(Error &error) +{ + /* no more input */ + z.next_in = nullptr; + z.avail_in = 0; + + while (true) { + Bytef output[4096]; + z.next_out = output; + z.avail_out = sizeof(output); + + int result = deflate(&z, Z_FINISH); + if (z.next_out > output && + !next.Write(output, z.next_out - output, error)) + return false; + + if (result == Z_STREAM_END) + return true; + else if (result != Z_OK) { + error.Set(zlib_domain, result, zError(result)); + return false; + } + } +} + +bool +GzipOutputStream::Write(const void *_data, size_t size, Error &error) +{ + /* zlib's API requires non-const input pointer */ + void *data = const_cast<void *>(_data); + + z.next_in = reinterpret_cast<Bytef *>(data); + z.avail_in = size; + + while (z.avail_in > 0) { + Bytef output[4096]; + z.next_out = output; + z.avail_out = sizeof(output); + + int result = deflate(&z, Z_NO_FLUSH); + if (result != Z_OK) { + error.Set(zlib_domain, result, zError(result)); + return false; + } + + if (z.next_out > output && + !next.Write(output, z.next_out - output, error)) + return false; + } + + return true; +} diff --git a/src/fs/io/GzipOutputStream.hxx b/src/fs/io/GzipOutputStream.hxx new file mode 100644 index 000000000..4f6d8b357 --- /dev/null +++ b/src/fs/io/GzipOutputStream.hxx @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_GZIP_OUTPUT_STREAM_HXX +#define MPD_GZIP_OUTPUT_STREAM_HXX + +#include "check.h" +#include "OutputStream.hxx" + +#include <assert.h> +#include <zlib.h> + +class Error; +class Domain; + +extern const Domain zlib_domain; + +/** + * A filter that compresses data written to it using zlib, forwarding + * compressed data in the "gzip" format. + * + * Don't forget to call Flush() before destructing this object. + */ +class GzipOutputStream final : public OutputStream { + OutputStream &next; + + z_stream z; + +public: + /** + * Construct the filter. Call IsDefined() to check whether + * the constructor has succeeded. If not, #error will hold + * information about the failure. + */ + GzipOutputStream(OutputStream &_next, Error &error); + ~GzipOutputStream(); + + /** + * Check whether the constructor has succeeded. + */ + bool IsDefined() const { + return z.opaque == nullptr; + } + + /** + * Finish the file and write all data remaining in zlib's + * output buffer. + */ + bool Flush(Error &error); + + /* virtual methods from class OutputStream */ + bool Write(const void *data, size_t size, Error &error) override; +}; + +#endif diff --git a/src/fs/io/OutputStream.hxx b/src/fs/io/OutputStream.hxx new file mode 100644 index 000000000..71311c71f --- /dev/null +++ b/src/fs/io/OutputStream.hxx @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_OUTPUT_STREAM_HXX +#define MPD_OUTPUT_STREAM_HXX + +#include "check.h" +#include "Compiler.h" + +#include <stddef.h> + +class Error; + +class OutputStream { +public: + OutputStream() = default; + OutputStream(const OutputStream &) = delete; + + virtual bool Write(const void *data, size_t size, Error &error) = 0; +}; + +#endif diff --git a/src/fs/io/StdioOutputStream.hxx b/src/fs/io/StdioOutputStream.hxx new file mode 100644 index 000000000..e00db922f --- /dev/null +++ b/src/fs/io/StdioOutputStream.hxx @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_STDIO_OUTPUT_STREAM_HXX +#define MPD_STDIO_OUTPUT_STREAM_HXX + +#include "check.h" +#include "OutputStream.hxx" +#include "fs/AllocatedPath.hxx" + +#include <stdio.h> + +class StdioOutputStream final : public OutputStream { + FILE *const file; + +public: + StdioOutputStream(FILE *_file):file(_file) {} + + /* virtual methods from class OutputStream */ + bool Write(const void *data, size_t size, + gcc_unused Error &error) override { + fwrite(data, 1, size, file); + + /* this class is debug-only and ignores errors */ + return true; + } +}; + +#endif diff --git a/src/fs/io/TextFile.cxx b/src/fs/io/TextFile.cxx new file mode 100644 index 000000000..b1a92b9cc --- /dev/null +++ b/src/fs/io/TextFile.cxx @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "TextFile.hxx" +#include "util/Alloc.hxx" +#include "fs/Path.hxx" +#include "fs/FileSystem.hxx" + +#include <assert.h> +#include <string.h> +#include <stdlib.h> + +TextFile::TextFile(Path path_fs) + :file(FOpen(path_fs, FOpenMode::ReadText)), + buffer((char *)xalloc(step)), capacity(step), length(0) {} + +TextFile::~TextFile() +{ + free(buffer); + + if (file != nullptr) + fclose(file); +} + +char * +TextFile::ReadLine() +{ + assert(file != nullptr); + + while (true) { + if (length >= capacity) { + if (capacity >= max_length) + /* too large already - bail out */ + return nullptr; + + capacity <<= 1; + char *new_buffer = (char *)realloc(buffer, capacity); + if (new_buffer == nullptr) + /* out of memory - bail out */ + return nullptr; + } + + char *p = fgets(buffer + length, capacity - length, file); + if (p == nullptr) { + if (length == 0 || ferror(file)) + return nullptr; + break; + } + + length += strlen(buffer + length); + if (buffer[length - 1] == '\n') + break; + } + + /* remove the newline characters */ + if (buffer[length - 1] == '\n') + --length; + if (buffer[length - 1] == '\r') + --length; + + buffer[length] = 0; + length = 0; + return buffer; +} diff --git a/src/fs/io/TextFile.hxx b/src/fs/io/TextFile.hxx new file mode 100644 index 000000000..e3a712a88 --- /dev/null +++ b/src/fs/io/TextFile.hxx @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_TEXT_FILE_HXX +#define MPD_TEXT_FILE_HXX + +#include "Compiler.h" + +#include <stdio.h> +#include <stddef.h> + +class Path; + +class TextFile { + static constexpr size_t max_length = 512 * 1024; + static constexpr size_t step = 1024; + + FILE *const file; + + char *buffer; + size_t capacity, length; + +public: + TextFile(Path path_fs); + + TextFile(const TextFile &other) = delete; + + ~TextFile(); + + bool HasFailed() const { + return gcc_unlikely(file == nullptr); + } + + /** + * Reads a line from the input file, and strips trailing + * space. There is a reasonable maximum line length, only to + * prevent denial of service. + * + * @param file the source file, opened in text mode + * @param buffer an allocator for the buffer + * @return a pointer to the line, or nullptr on end-of-file or error + */ + char *ReadLine(); +}; + +#endif |