Permalink
Newer
Older
100644 315 lines (278 sloc) 10.4 KB
1
// Copyright (C) 2012 Henner Zeller <h.zeller@acm.org>
2
//
3
// This program is free software; you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation; either version 3 of the License, or
6
// (at your option) any later version.
7
//
8
// This program is distributed in the hope that it will be useful,
9
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
// GNU General Public License for more details.
12
//
13
// You should have received a copy of the GNU General Public License
14
// along with this program. If not, see <http://www.gnu.org/licenses/>.
15
16
// Use latest version.
17
#define FUSE_USE_VERSION 26
18
#include <fuse.h>
20
#include <dirent.h>
21
#include <errno.h>
@hzeller
Sep 16, 2012
22
#include <fcntl.h>
@hzeller
Sep 16, 2012
24
#include <stdio.h>
@hzeller
Sep 16, 2012
26
#include <string.h>
27
#include <sys/stat.h>
28
#include <sys/time.h>
29
#include <syslog.h>
@hzeller
Sep 16, 2012
30
#include <unistd.h>
32
#include "folve-filesystem.h"
35
36
// Compilation unit variables to communicate with the fuse callbacks.
37
static struct FolveRuntime {
38
FolveRuntime() : fs(NULL), mount_point(NULL),
39
status_port(-1), refresh_time(10), parameter_error(false) {}
43
int refresh_time;
47
static char *concat_path(char *buf, const char *a, const char *b) {
48
strcpy(buf, a);
49
strcat(buf, b);
50
return buf;
51
}
52
53
// Given a relative path from the root of the mounted file-system, get the
54
// original file from the source filesystem.
55
static const char *assemble_orig_path(char *buf, const char *path) {
56
return concat_path(buf, folve_rt.fs->underlying_dir().c_str(), path);
59
// Essentially lstat(). Just forward to the original filesystem (this
60
// will by lying: our convolved files are of different size...)
61
static int folve_getattr(const char *path, struct stat *stbuf) {
62
// If this is a currently open filename, we might be able to output a better
63
// estimate.
64
int result = folve_rt.fs->StatByFilename(path, stbuf);
65
if (result != 0) {
66
char path_buf[PATH_MAX];
67
result = lstat(assemble_orig_path(path_buf, path), stbuf);
68
if (result == -1)
69
return -errno;
70
}
71
// Whatever write mode was there before: now things are readonly.
72
stbuf->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
76
// readdir(). Just forward to original filesystem.
77
static int folve_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
78
off_t offset, struct fuse_file_info *fi) {
79
DIR *dp;
80
struct dirent *de;
81
char path_buf[PATH_MAX];
82
83
dp = opendir(assemble_orig_path(path_buf, path));
84
if (dp == NULL)
85
return -errno;
86
87
while ((de = readdir(dp)) != NULL) {
88
struct stat st;
89
memset(&st, 0, sizeof(st));
90
st.st_ino = de->d_ino;
91
st.st_mode = de->d_type << 12;
92
const char *entry_name = de->d_name;
93
if (filler(buf, entry_name, &st, 0))
94
break;
95
}
96
97
closedir(dp);
98
return 0;
99
}
100
101
// readlink(): forward to original filesystem.
102
static int folve_readlink(const char *path, char *buf, size_t size) {
103
char path_buf[PATH_MAX];
104
const int result = readlink(assemble_orig_path(path_buf, path),
105
buf, size - 1);
106
if (result == -1)
107
return -errno;
108
109
buf[result] = '\0';
110
return 0;
111
}
112
113
static int folve_open(const char *path, struct fuse_file_info *fi) {
114
// We want to be allowed to only return part of the requested data in read().
115
// That way, we can separate reading the ID3-tags from
116
// decoding of the music stream - that way indexing should be fast.
117
// Setting the flag 'direct_io' allows us to return partial results.
118
fi->direct_io = 1;
119
120
// The file-handle has the neat property to be 64 bit - so we can actually
@hzeller
Sep 16, 2012
121
// stuff a pointer to our file handler object in there :)
122
// (Yay, someone was thinking while developing that API).
123
FileHandler *handler = folve_rt.fs->GetOrCreateHandler(path);
124
if (handler == NULL)
125
return -errno;
126
fi->fh = (uint64_t) handler;
127
return 0;
130
static int folve_read(const char *path, char *buf, size_t size, off_t offset,
131
struct fuse_file_info *fi) {
132
return reinterpret_cast<FileHandler *>(fi->fh)->Read(buf, size, offset);
135
static int folve_release(const char *path, struct fuse_file_info *fi) {
136
folve_rt.fs->Close(path, reinterpret_cast<FileHandler *>(fi->fh));
140
static int folve_fgetattr(const char *path, struct stat *result,
141
struct fuse_file_info *fi) {
142
return reinterpret_cast<FileHandler *>(fi->fh)->Stat(result);
145
static void *folve_init(struct fuse_conn_info *conn) {
146
const int ident_len = 20;
147
char *ident = (char*) malloc(ident_len); // openlog() keeps reference. Leaks.
148
snprintf(ident, ident_len, "folve[%d]", getpid());
149
openlog(ident, LOG_CONS|LOG_PERROR, LOG_USER);
150
syslog(LOG_INFO, "Version " FOLVE_VERSION " started. "
151
"Serving '%s' on mount point '%s'",
152
folve_rt.fs->underlying_dir().c_str(), folve_rt.mount_point);
153
if (folve::IsDebugLogEnabled()) {
154
syslog(LOG_INFO, "Debug logging enabled (-D)");
155
}
157
if (folve_rt.status_port > 0) {
158
// Need to start status server after we're daemonized.
159
StatusServer *status_server = new StatusServer(folve_rt.fs);
160
if (status_server->Start(folve_rt.status_port)) {
161
syslog(LOG_INFO, "HTTP status server on port %d; refresh=%d",
162
folve_rt.status_port, folve_rt.refresh_time);
163
status_server->set_meta_refresh(folve_rt.refresh_time);
164
} else {
165
syslog(LOG_ERR, "Couldn't start HTTP server on port %d\n",
169
170
// Some sanity checks.
171
if (folve_rt.fs->config_dirs().size() == 1) {
172
syslog(LOG_NOTICE, "No filter configuration directories given. "
173
"Any files will be just passed through verbatim.");
174
}
175
if (folve_rt.fs->config_dirs().size() > 2 && folve_rt.status_port < 0) {
176
syslog(LOG_WARNING, "Multiple filter configurations given, but no HTTP "
177
"status port. You only can switch filters via the HTTP interface; "
178
"add -p <port>");
179
}
183
static void folve_destroy(void *) {
184
syslog(LOG_INFO, "Exiting.");
185
}
186
@hzeller
Sep 16, 2012
187
static int usage(const char *prg) {
188
printf("usage: %s [options] <original-dir> <mount-point>\n", prg);
189
printf("Options: (in sequence of usefulness)\n"
190
"\t-c <cfg-dir> : Convolver configuration directory.\n"
191
"\t You can supply this option multiple times:\n"
192
"\t Select on the HTTP status page.\n"
193
"\t-p <port> : Port to run the HTTP status server on.\n"
194
"\t-r <refresh> : Seconds between refresh of status page;\n"
195
"\t Default is %d seconds; switch off with -1.\n"
196
"\t-g : Gapless convolving alphabetically adjacent files.\n"
197
"\t-D : Moderate volume Folve debug messages to syslog.\n"
198
"\t-f : Operate in foreground; useful for debugging.\n"
199
"\t-o <mnt-opt> : other generic mount parameters passed to fuse.\n"
200
"\t-d : High volume fuse debug log. Implies -f.\n",
201
folve_rt.refresh_time);
205
struct FolveConfig {
206
FolveConfig() : base_dir(NULL), config_dir(NULL), port(-1) {}
207
const char *base_dir;
208
const char *mount_point;
209
const char *config_dir;
210
int port;
211
};
212
213
enum {
214
FOLVE_OPT_PORT = 42,
215
FOLVE_OPT_REFRESH_TIME,
217
FOLVE_OPT_DEBUG,
219
};
220
221
int FolveOptionHandling(void *data, const char *arg, int key,
222
struct fuse_args *outargs) {
223
char realpath_buf[PATH_MAX]; // running as daemon, need absolute names.
224
FolveRuntime *rt = (FolveRuntime*) data;
225
switch (key) {
226
case FUSE_OPT_KEY_NONOPT:
227
// First non-opt: our underlying dir.
228
if (rt->fs->underlying_dir().empty()) {
229
const char *base_dir = realpath(arg, realpath_buf);
230
if (base_dir != NULL) {
231
rt->fs->set_underlying_dir(base_dir);
232
} else {
233
fprintf(stderr, "Invalid base path '%s': %s\n",
234
arg, strerror(errno));
235
rt->parameter_error = true;
236
}
237
return 0; // we consumed this.
239
rt->mount_point = strdup(arg); // remmber as FYI
240
return 1; // .. but leave it to fuse
241
}
242
case FOLVE_OPT_PORT:
243
rt->status_port = atoi(arg + 2); // strip "-p"
245
case FOLVE_OPT_REFRESH_TIME:
246
rt->refresh_time = atoi(arg + 2); // strip "-r"
247
return 0;
248
case FOLVE_OPT_CONFIG: {
249
const char *config_dir = realpath(arg + 2, realpath_buf); // strip "-c"
250
if (config_dir != NULL) {
251
rt->fs->add_config_dir(config_dir);
252
} else {
253
fprintf(stderr, "Invalid config dir '%s': %s\n",
254
arg + 2, strerror(errno));
255
rt->parameter_error = true;
256
}
258
}
259
case FOLVE_OPT_DEBUG:
260
// rt->fs->set_debug_ui_enabled(true); // Disabled in status-server.
261
folve::EnableDebugLog(true);
262
return 0;
263
case FOLVE_OPT_GAPLESS:
264
rt->fs->set_gapless_processing(true);
265
return 0;
266
}
267
return 1;
268
}
269
270
int main(int argc, char *argv[]) {
271
const char *progname = argv[0];
273
return usage(progname);
276
folve_rt.fs = new FolveFilesystem();
277
278
static struct fuse_opt folve_options[] = {
279
FUSE_OPT_KEY("-p ", FOLVE_OPT_PORT),
280
FUSE_OPT_KEY("-r ", FOLVE_OPT_REFRESH_TIME),
281
FUSE_OPT_KEY("-c ", FOLVE_OPT_CONFIG),
282
FUSE_OPT_KEY("-D", FOLVE_OPT_DEBUG),
283
FUSE_OPT_KEY("-g", FOLVE_OPT_GAPLESS),
284
FUSE_OPT_END
285
};
286
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
287
fuse_opt_parse(&args, &folve_rt, folve_options, FolveOptionHandling);
289
if (folve_rt.parameter_error || !folve_rt.fs->CheckInitialized()) {
290
return usage(progname);
291
}
292
293
struct fuse_operations folve_operations;
294
memset(&folve_operations, 0, sizeof(folve_operations));
296
// Start/stop. Will write to syslog and start auxiliary http service.
297
folve_operations.init = folve_init;
298
folve_operations.destroy = folve_destroy;
299
300
// Basic operations to make navigation work.
301
folve_operations.readdir = folve_readdir;
302
folve_operations.readlink = folve_readlink;
303
304
// open() and close() file.
305
folve_operations.open = folve_open;
306
folve_operations.release = folve_release;
307
308
// Actual workhorse: reading a file and returning predicted file-size
309
folve_operations.read = folve_read;
310
folve_operations.fgetattr = folve_fgetattr;
311
folve_operations.getattr = folve_getattr;
313
return fuse_main(args.argc, args.argv, &folve_operations, NULL);