Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 260 lines (234 sloc) 8.779 kb
4d4d90b Henner Zeller o More legalese
authored
1 // Folve - A fuse filesystem that convolves audio files on-the-fly.
2 //
e9c07dd Henner Zeller o add documentation
authored
3 // Copyright (C) 2012 Henner Zeller <h.zeller@acm.org>
cca212e Henner Zeller o document compile error with old version of fuse.
authored
4 //
e9c07dd Henner Zeller o add documentation
authored
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17
a9d3e53 Henner Zeller o Found a project name: "Folve". Some renamings because of that.
authored
18 #include "folve-filesystem.h"
19
31096bb Henner Zeller o remove option -c and add -C: give a base directory for all filters.
authored
20 #include <assert.h>
08bbf28 Henner Zeller o First shot at gapless. Works pretty well for my test-case.
authored
21 #include <dirent.h>
e9c07dd Henner Zeller o add documentation
authored
22 #include <errno.h>
15b305a Henner Zeller o Build infrastructure to re-use file handler objects. Files seem to
authored
23 #include <fcntl.h>
24e7411 Henner Zeller o Move SndFileHandler as ConvolveFileHandler out of folve-filesystem.cc
authored
24 #include <limits.h>
e9c07dd Henner Zeller o add documentation
authored
25 #include <stdio.h>
24e7411 Henner Zeller o Move SndFileHandler as ConvolveFileHandler out of folve-filesystem.cc
authored
26 #include <stdlib.h>
e9c07dd Henner Zeller o add documentation
authored
27 #include <string.h>
28 #include <strings.h>
9a8ea56 Henner Zeller o add infrastructure to provide Stat() output of currently open
authored
29 #include <sys/stat.h>
30 #include <sys/types.h>
5830c5a Henner Zeller o Switch logging to syslog()
authored
31 #include <syslog.h>
e9c07dd Henner Zeller o add documentation
authored
32 #include <unistd.h>
7c7bda6 Henner Zeller o attempt to create a sndfile for every file, if that fails, fall
authored
33
9a8ea56 Henner Zeller o add infrastructure to provide Stat() output of currently open
authored
34 #include <map>
a9d3e53 Henner Zeller o Found a project name: "Folve". Some renamings because of that.
authored
35 #include <string>
6c6dedb Henner Zeller o Some readme tweaks and readability improvements.
authored
36 #include <zita-convolver.h>
e9c07dd Henner Zeller o add documentation
authored
37
5ec3966 Henner Zeller o Only have one pre-buffer thread to only use at most one more core
authored
38 #include "buffer-thread.h"
24e7411 Henner Zeller o Move SndFileHandler as ConvolveFileHandler out of folve-filesystem.cc
authored
39 #include "convolve-file-handler.h"
a9d3e53 Henner Zeller o Found a project name: "Folve". Some renamings because of that.
authored
40 #include "file-handler-cache.h"
41 #include "file-handler.h"
5282bc2 Henner Zeller o Move PassThrougHandler to its won file.
authored
42 #include "pass-through-handler.h"
8268c37 Henner Zeller o Bubble up error messages in status server.
authored
43 #include "util.h"
e9c07dd Henner Zeller o add documentation
authored
44
8c7e5c1 Henner Zeller o Only if -D is given, the debug toggle is active in the UI.
authored
45 FolveFilesystem::FolveFilesystem()
24e7411 Henner Zeller o Move SndFileHandler as ConvolveFileHandler out of folve-filesystem.cc
authored
46 : gapless_processing_(false), pre_buffer_size_(-1), open_file_cache_(4),
47 processor_pool_(3), buffer_thread_(NULL),
145e419 Henner Zeller o add include for off_t
authored
48 total_file_openings_(0), total_file_reopen_(0),
49 // oversize factor of 1.25 seems to be a good initial size.
50 file_oversize_factor_(1.25) {
8c7e5c1 Henner Zeller o Only if -D is given, the debug toggle is active in the UI.
authored
51 }
ba7fde8 Henner Zeller o implement size estimate. If the user stat() or fstat() the file,
authored
52
5ec3966 Henner Zeller o Only have one pre-buffer thread to only use at most one more core
authored
53 void FolveFilesystem::RequestPrebuffer(ConversionBuffer *buffer) {
54 if (pre_buffer_size_ <= 0) return;
55 if (buffer_thread_ == NULL) {
56 buffer_thread_ = new BufferThread(pre_buffer_size_);
57 buffer_thread_->Start();
58 }
59 buffer_thread_->EnqueueWork(buffer);
60 }
61
62 void FolveFilesystem::QuitBuffering(ConversionBuffer *buffer) {
63 if (buffer_thread_ != NULL) buffer_thread_->Forget(buffer);
64 }
65
08bbf28 Henner Zeller o First shot at gapless. Works pretty well for my test-case.
authored
66 FileHandler *FolveFilesystem::CreateFromDescriptor(
67 int filedes,
31096bb Henner Zeller o remove option -c and add -C: give a base directory for all filters.
authored
68 const std::string &config_dir,
08bbf28 Henner Zeller o First shot at gapless. Works pretty well for my test-case.
authored
69 const char *fs_path,
70 const std::string &underlying_file) {
8268c37 Henner Zeller o Bubble up error messages in status server.
authored
71 HandlerStats file_info;
72 file_info.filename = fs_path;
31096bb Henner Zeller o remove option -c and add -C: give a base directory for all filters.
authored
73 file_info.filter_dir = config_dir;
74 if (!config_dir.empty()) {
75 const std::string full_config_path = base_config_dir_ + "/" + config_dir;
24e7411 Henner Zeller o Move SndFileHandler as ConvolveFileHandler out of folve-filesystem.cc
authored
76 FileHandler *handler = ConvolveFileHandler::Create(this, filedes, fs_path,
77 underlying_file,
78 config_dir,
79 full_config_path,
80 &file_info);
81 if (handler != NULL) return handler;
ee675fe Henner Zeller o Provide a way to select between different filter diretories.
authored
82 }
8b18938 Henner Zeller o some comment changes. Ready to do some flac handling.
authored
83 // Every other file-type is just passed through as is.
31096bb Henner Zeller o remove option -c and add -C: give a base directory for all filters.
authored
84 return new PassThroughHandler(filedes, config_dir, file_info);
ee675fe Henner Zeller o Provide a way to select between different filter diretories.
authored
85 }
86
31096bb Henner Zeller o remove option -c and add -C: give a base directory for all filters.
authored
87 std::string FolveFilesystem::CacheKey(const std::string &config_path,
88 const char *fs_path) {
89 return config_path + fs_path;
9a8ea56 Henner Zeller o add infrastructure to provide Stat() output of currently open
authored
90 }
91
08bbf28 Henner Zeller o First shot at gapless. Works pretty well for my test-case.
authored
92 FileHandler *FolveFilesystem::GetOrCreateHandler(const char *fs_path) {
31096bb Henner Zeller o remove option -c and add -C: give a base directory for all filters.
authored
93 const std::string config_path = current_config_subdir_;
94 const std::string cache_key = CacheKey(config_path, fs_path);
08bbf28 Henner Zeller o First shot at gapless. Works pretty well for my test-case.
authored
95 const std::string underlying_file = underlying_dir() + fs_path;
ee675fe Henner Zeller o Provide a way to select between different filter diretories.
authored
96 FileHandler *handler = open_file_cache_.FindAndPin(cache_key);
15b305a Henner Zeller o Build infrastructure to re-use file handler objects. Files seem to
authored
97 if (handler == NULL) {
08bbf28 Henner Zeller o First shot at gapless. Works pretty well for my test-case.
authored
98 int filedes = open(underlying_file.c_str(), O_RDONLY);
680f590 Henner Zeller o report opening error.
authored
99 if (filedes < 0)
100 return NULL;
b5c1e96 Henner Zeller o more useful output on status server.
authored
101 ++total_file_openings_;
31096bb Henner Zeller o remove option -c and add -C: give a base directory for all filters.
authored
102 handler = CreateFromDescriptor(filedes, config_path,
08bbf28 Henner Zeller o First shot at gapless. Works pretty well for my test-case.
authored
103 fs_path, underlying_file);
ee675fe Henner Zeller o Provide a way to select between different filter diretories.
authored
104 handler = open_file_cache_.InsertPinned(cache_key, handler);
b5c1e96 Henner Zeller o more useful output on status server.
authored
105 } else {
106 ++total_file_reopen_;
15b305a Henner Zeller o Build infrastructure to re-use file handler objects. Files seem to
authored
107 }
108 return handler;
e9c07dd Henner Zeller o add documentation
authored
109 }
110
a9d3e53 Henner Zeller o Found a project name: "Folve". Some renamings because of that.
authored
111 int FolveFilesystem::StatByFilename(const char *fs_path, struct stat *st) {
31096bb Henner Zeller o remove option -c and add -C: give a base directory for all filters.
authored
112 const std::string cache_key = CacheKey(current_config_subdir_, fs_path);
ee675fe Henner Zeller o Provide a way to select between different filter diretories.
authored
113 FileHandler *handler = open_file_cache_.FindAndPin(cache_key);
15b305a Henner Zeller o Build infrastructure to re-use file handler objects. Files seem to
authored
114 if (handler == 0)
9a8ea56 Henner Zeller o add infrastructure to provide Stat() output of currently open
authored
115 return -1;
15b305a Henner Zeller o Build infrastructure to re-use file handler objects. Files seem to
authored
116 ssize_t result = handler->Stat(st);
ee675fe Henner Zeller o Provide a way to select between different filter diretories.
authored
117 open_file_cache_.Unpin(cache_key);
15b305a Henner Zeller o Build infrastructure to re-use file handler objects. Files seem to
authored
118 return result;
9a8ea56 Henner Zeller o add infrastructure to provide Stat() output of currently open
authored
119 }
120
ee675fe Henner Zeller o Provide a way to select between different filter diretories.
authored
121 void FolveFilesystem::Close(const char *fs_path, const FileHandler *handler) {
31096bb Henner Zeller o remove option -c and add -C: give a base directory for all filters.
authored
122 assert(handler != NULL);
123 const std::string cache_key = CacheKey(handler->filter_dir(), fs_path);
ee675fe Henner Zeller o Provide a way to select between different filter diretories.
authored
124 open_file_cache_.Unpin(cache_key);
125 }
126
a90d922 Henner Zeller o Separate sanitizing of config subdirectories.
authored
127 static bool IsDirectory(const std::string &path) {
128 if (path.empty()) return false;
129 struct stat st;
130 if (stat(path.c_str(), &st) != 0)
131 return false;
132 return (st.st_mode & S_IFMT) == S_IFDIR;
133 }
134
08bbf28 Henner Zeller o First shot at gapless. Works pretty well for my test-case.
authored
135 bool FolveFilesystem::ListDirectory(const std::string &fs_dir,
ac40d85 Henner Zeller o Make sure we match the alphabetically next file with
authored
136 const std::string &suffix,
08bbf28 Henner Zeller o First shot at gapless. Works pretty well for my test-case.
authored
137 std::set<std::string> *files) {
138 const std::string real_dir = underlying_dir() + fs_dir;
139 DIR *dp = opendir(real_dir.c_str());
140 if (dp == NULL) return false;
141 struct dirent *dent;
142 while ((dent = readdir(dp)) != NULL) {
ac40d85 Henner Zeller o Make sure we match the alphabetically next file with
authored
143 if (!folve::HasSuffix(dent->d_name, suffix))
144 continue;
08bbf28 Henner Zeller o First shot at gapless. Works pretty well for my test-case.
authored
145 files->insert(fs_dir + dent->d_name);
146 }
147 closedir(dp);
148 return true;
149 }
150
a90d922 Henner Zeller o Separate sanitizing of config subdirectories.
authored
151 bool FolveFilesystem::SanitizeConfigSubdir(std::string *subdir_path) const {
152 if (base_config_dir_.length() + 1 + subdir_path->length() > PATH_MAX)
153 return false; // uh, someone wants to buffer overflow us ?
154 const std::string to_verify_path = base_config_dir_ + "/" + *subdir_path;
155 char all_path[PATH_MAX];
156 // This will as well eat symbolic links that break out, though one could
157 // argue that that would be sane. We could think of some light
158 // canonicalization that only removes ./ and ../
159 const char *verified = realpath(to_verify_path.c_str(), all_path);
160 if (verified == NULL) { // bogus directory.
161 return false;
162 }
163 if (strncmp(verified, base_config_dir_.c_str(),
164 base_config_dir_.length()) != 0) {
165 // Attempt to break out with ../-tricks.
166 return false;
167 }
168 if (!IsDirectory(verified))
169 return false;
170
171 // Derive from sanitized dir. So someone can write lowpass/../highpass
172 // or '.' for empty filter. Or ./highpass. And all work.
173 *subdir_path = ((strlen(verified) == base_config_dir_.length())
174 ? "" // chose subdir '.'
175 : verified + base_config_dir_.length() + 1 /*slash*/);
176 return true;
177 }
178
c113afd Henner Zeller o Adapt curl, wget documentation
authored
179 bool FolveFilesystem::SwitchCurrentConfigDir(const std::string &subdir_in) {
180 std::string subdir = subdir_in;
a90d922 Henner Zeller o Separate sanitizing of config subdirectories.
authored
181 if (!subdir.empty() && !SanitizeConfigSubdir(&subdir)) {
182 syslog(LOG_INFO, "Invalid config switch attempt to '%s'",
183 subdir_in.c_str());
184 return false;
31096bb Henner Zeller o remove option -c and add -C: give a base directory for all filters.
authored
185 }
186 if (subdir != current_config_subdir_) {
187 current_config_subdir_ = subdir;
188 if (subdir.empty()) {
5830c5a Henner Zeller o Switch logging to syslog()
authored
189 syslog(LOG_INFO, "Switching to pass-through mode.");
190 } else {
31096bb Henner Zeller o remove option -c and add -C: give a base directory for all filters.
authored
191 syslog(LOG_INFO, "Switching config directory to '%s'", subdir.c_str());
5830c5a Henner Zeller o Switch logging to syslog()
authored
192 }
31096bb Henner Zeller o remove option -c and add -C: give a base directory for all filters.
authored
193 return true;
5830c5a Henner Zeller o Switch logging to syslog()
authored
194 }
31096bb Henner Zeller o remove option -c and add -C: give a base directory for all filters.
authored
195 return false;
e9c07dd Henner Zeller o add documentation
authored
196 }
fd65cee Henner Zeller o add basic zita filter.
authored
197
ee675fe Henner Zeller o Provide a way to select between different filter diretories.
authored
198 bool FolveFilesystem::CheckInitialized() {
199 if (underlying_dir().empty()) {
200 fprintf(stderr, "Don't know the underlying directory to read from.\n");
201 return false;
202 }
31096bb Henner Zeller o remove option -c and add -C: give a base directory for all filters.
authored
203
ee675fe Henner Zeller o Provide a way to select between different filter diretories.
authored
204 if (!IsDirectory(underlying_dir())) {
205 fprintf(stderr, "<underlying-dir>: '%s' not a directory.\n",
206 underlying_dir().c_str());
207 return false;
208 }
209
31096bb Henner Zeller o remove option -c and add -C: give a base directory for all filters.
authored
210 if (base_config_dir_.empty() || !IsDirectory(base_config_dir_)) {
211 fprintf(stderr, "<config-dir>: '%s' not a directory.\n",
212 base_config_dir_.c_str());
213 return false;
ee675fe Henner Zeller o Provide a way to select between different filter diretories.
authored
214 }
31096bb Henner Zeller o remove option -c and add -C: give a base directory for all filters.
authored
215
a90d922 Henner Zeller o Separate sanitizing of config subdirectories.
authored
216 return true;
217 }
218
219 void FolveFilesystem::SetupInitialConfig() {
220 std::set<std::string> available_dirs = ListConfigDirs(true);
221 // Some sanity checks.
222 if (available_dirs.size() == 1) {
223 syslog(LOG_NOTICE, "No filter configuration directories given. "
224 "Any files will be just passed through verbatim.");
225 }
226 if (available_dirs.size() > 1) {
ee675fe Henner Zeller o Provide a way to select between different filter diretories.
authored
227 // By default, lets set the index to the first filter the user provided.
a90d922 Henner Zeller o Separate sanitizing of config subdirectories.
authored
228 SwitchCurrentConfigDir(*++available_dirs.begin());
ee675fe Henner Zeller o Provide a way to select between different filter diretories.
authored
229 }
fd65cee Henner Zeller o add basic zita filter.
authored
230 }
31096bb Henner Zeller o remove option -c and add -C: give a base directory for all filters.
authored
231
232 const std::set<std::string> FolveFilesystem::GetAvailableConfigDirs() const {
a90d922 Henner Zeller o Separate sanitizing of config subdirectories.
authored
233 return ListConfigDirs(false);
234 }
235
236 const std::set<std::string> FolveFilesystem::ListConfigDirs(bool warn_invalid)
237 const {
31096bb Henner Zeller o remove option -c and add -C: give a base directory for all filters.
authored
238 std::set<std::string> result;
239 result.insert(""); // empty directory: pass-through.
240 DIR *dp = opendir(base_config_dir_.c_str());
241 if (dp == NULL) return result;
242 struct dirent *dent;
243 while ((dent = readdir(dp)) != NULL) {
a90d922 Henner Zeller o Separate sanitizing of config subdirectories.
authored
244 std::string subdir = dent->d_name;
245 if (subdir == "." || subdir == "..")
31096bb Henner Zeller o remove option -c and add -C: give a base directory for all filters.
authored
246 continue;
a90d922 Henner Zeller o Separate sanitizing of config subdirectories.
authored
247 if (!SanitizeConfigSubdir(&subdir)) {
248 if (warn_invalid) {
249 syslog(LOG_INFO, "Note: '%s' ignored in config directory; not a "
250 "directory or pointing outside base directory.", dent->d_name);
251 }
31096bb Henner Zeller o remove option -c and add -C: give a base directory for all filters.
authored
252 continue;
a90d922 Henner Zeller o Separate sanitizing of config subdirectories.
authored
253 }
254 result.insert(subdir);
31096bb Henner Zeller o remove option -c and add -C: give a base directory for all filters.
authored
255 }
256 closedir(dp);
257 return result;
258 }
259
Something went wrong with that request. Please try again.