CompositeStorage.hxx 4.31 KB
Newer Older
1
/*
2
 * Copyright 2003-2016 The Music Player Daemon Project
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
 * 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_COMPOSITE_STORAGE_HXX
#define MPD_COMPOSITE_STORAGE_HXX

#include "check.h"
#include "StorageInterface.hxx"
#include "thread/Mutex.hxx"
#include "Compiler.h"

#include <string>
#include <map>

/**
 * A #Storage implementation that combines multiple other #Storage
 * instances in one virtual tree.  It is used to "mount" new #Storage
 * instances into the storage tree.
 *
 * This class is thread-safe: mounts may be added and removed at any
 * time in any thread.
 */
class CompositeStorage final : public Storage {
	/**
	 * A node in the virtual directory tree.
	 */
	struct Directory {
		/**
45
		 * The #Storage mounted in this virtual directory.  All
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
		 * "leaf" Directory instances must have a #Storage.
		 * Other Directory instances may have one, and child
		 * mounts will be "mixed" in.
		 */
		Storage *storage;

		std::map<std::string, Directory> children;

		Directory():storage(nullptr) {}
		~Directory();

		gcc_pure
		bool IsEmpty() const {
			return storage == nullptr && children.empty();
		}

		gcc_pure
		const Directory *Find(const char *uri) const;

		Directory &Make(const char *uri);

		bool Unmount();
		bool Unmount(const char *uri);

		gcc_pure
		bool MapToRelativeUTF8(std::string &buffer,
				       const char *uri) const;
	};

	struct FindResult {
		const Directory *directory;
		const char *uri;
	};

	/**
	 * Protects the virtual #Directory tree.
	 *
	 * TODO: use readers-writer lock
	 */
	mutable Mutex mutex;

	Directory root;

	mutable std::string relative_buffer;

public:
	CompositeStorage();
	virtual ~CompositeStorage();

95 96 97 98 99 100 101 102 103 104 105
	/**
	 * Get the #Storage at the specified mount point.  Returns
	 * nullptr if the given URI is not a mount point.
	 *
	 * The returned pointer is unprotected.  No other thread is
	 * allowed to unmount the given mount point while the return
	 * value is being used.
	 */
	gcc_pure gcc_nonnull_all
	Storage *GetMount(const char *uri);

106 107 108 109 110 111 112 113 114 115 116 117
	/**
	 * Call the given function for each mounted storage, including
	 * the root storage.  Passes mount point URI and the a const
	 * Storage reference to the function.
	 */
	template<typename T>
	void VisitMounts(T t) const {
		const ScopeLock protect(mutex);
		std::string uri;
		VisitMounts(uri, root, t);
	}

118 119 120 121
	void Mount(const char *uri, Storage *storage);
	bool Unmount(const char *uri);

	/* virtual methods from class Storage */
122
	StorageFileInfo GetInfo(const char *uri, bool follow) override;
123

124
	StorageDirectoryReader *OpenDirectory(const char *uri) override;
125

126
	std::string MapUTF8(const char *uri) const override;
127

128
	AllocatedPath MapFS(const char *uri) const override;
129

130
	const char *MapToRelativeUTF8(const char *uri) const override;
131 132

private:
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
	template<typename T>
	void VisitMounts(std::string &uri, const Directory &directory,
			 T t) const {
		const Storage *const storage = directory.storage;
		if (storage != nullptr)
			t(uri.c_str(), *storage);

		if (!uri.empty())
			uri.push_back('/');

		const size_t uri_length = uri.length();

		for (const auto &i : directory.children) {
			uri.resize(uri_length);
			uri.append(i.first);

			VisitMounts(uri, i.second, t);
		}
	}

153 154 155 156 157 158 159 160
	/**
	 * Follow the given URI path, and find the outermost directory
	 * which is a #Storage mount point.  If there are no mounts,
	 * it returns the root directory (with a nullptr "storage"
	 * attribute, of course).  FindResult::uri contains the
	 * remaining unused part of the URI (may be empty if all of
	 * the URI was used).
	 */
161 162 163 164 165 166 167 168
	gcc_pure
	FindResult FindStorage(const char *uri) const;

	const char *MapToRelativeUTF8(const Directory &directory,
				      const char *uri) const;
};

#endif