|
@@ -0,0 +1,291 @@
|
|
1
|
+/*
|
|
2
|
+# filejoinfs v1.0
|
|
3
|
+# Quick'n'dirty FUSE module implementing joining of different files as one
|
|
4
|
+#
|
|
5
|
+# Copyright (c) 2010 Georgi Chorbadzhiyski (georgi@unixsol.org)
|
|
6
|
+# All rights reserved.
|
|
7
|
+#
|
|
8
|
+# Redistribution and use of this script, with or without modification, is
|
|
9
|
+# permitted provided that the following conditions are met:
|
|
10
|
+#
|
|
11
|
+# 1. Redistributions of this script must retain the above copyright
|
|
12
|
+# notice, this list of conditions and the following disclaimer.
|
|
13
|
+#
|
|
14
|
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
15
|
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
16
|
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
|
17
|
+# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
18
|
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
19
|
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
20
|
+# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
21
|
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
22
|
+# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
23
|
+# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
24
|
+#
|
|
25
|
+# To compile the program run:
|
|
26
|
+# gcc -Wall -Wextra `pkg-config fuse --cflags --libs` filejoinfs.c -lfuse -o filejoinfs
|
|
27
|
+#
|
|
28
|
+# To use it:
|
|
29
|
+# 1. Install fuse module
|
|
30
|
+# sudo modprobe fuse
|
|
31
|
+#
|
|
32
|
+# 2. Create file list
|
|
33
|
+# echo /etc/group >> filelist.txt
|
|
34
|
+# echo /etc/issue >> filelist.txt
|
|
35
|
+# echo /etc/passwd >> filelist.txt
|
|
36
|
+#
|
|
37
|
+# 3. Create an empty file over which the files in the list will be joined
|
|
38
|
+# touch joined.txt
|
|
39
|
+#
|
|
40
|
+# 4. Mount filejoinfs
|
|
41
|
+# ./filejoinfs filelist.txt joined.txt
|
|
42
|
+#
|
|
43
|
+# 5. Check the result with
|
|
44
|
+# cat joined.txt
|
|
45
|
+#
|
|
46
|
+# You will see the contents of files listed in filelist.txt
|
|
47
|
+#
|
|
48
|
+# 6. Unmount the fs
|
|
49
|
+# fusermount -u joined.txt
|
|
50
|
+#
|
|
51
|
+ */
|
|
52
|
+
|
|
53
|
+#define FUSE_USE_VERSION 26
|
|
54
|
+#define _XOPEN_SOURCE 500 // for "pread"
|
|
55
|
+#define _GNU_SOURCE 1 // for "getline"
|
|
56
|
+
|
|
57
|
+#include <stdio.h>
|
|
58
|
+#include <stdlib.h>
|
|
59
|
+#include <error.h>
|
|
60
|
+#include <string.h>
|
|
61
|
+#include <errno.h>
|
|
62
|
+#include <unistd.h>
|
|
63
|
+#include <sys/types.h>
|
|
64
|
+#include <fuse.h>
|
|
65
|
+
|
|
66
|
+/* Handle list of files */
|
|
67
|
+struct fileinfo {
|
|
68
|
+ int fd;
|
|
69
|
+ char *name;
|
|
70
|
+ off_t size;
|
|
71
|
+};
|
|
72
|
+
|
|
73
|
+struct files {
|
|
74
|
+ int num_files;
|
|
75
|
+ off_t total_size;
|
|
76
|
+ int alloc_files;
|
|
77
|
+ struct fileinfo **data;
|
|
78
|
+};
|
|
79
|
+
|
|
80
|
+struct files *filelist;
|
|
81
|
+
|
|
82
|
+struct files *files_alloc() {
|
|
83
|
+ struct files *f = calloc(1, sizeof(struct files));
|
|
84
|
+ f->alloc_files = 64;
|
|
85
|
+ f->data = calloc(f->alloc_files, sizeof(struct fileinfo *));
|
|
86
|
+ return f;
|
|
87
|
+}
|
|
88
|
+
|
|
89
|
+void files_free(struct files **pfiles) {
|
|
90
|
+ struct files *files = *pfiles;
|
|
91
|
+ struct fileinfo *file;
|
|
92
|
+ int i;
|
|
93
|
+ if (files) {
|
|
94
|
+ for (i=0; i<files->num_files; i++) {
|
|
95
|
+ file = files->data[i];
|
|
96
|
+ close(file->fd);
|
|
97
|
+ free(file->name);
|
|
98
|
+ free(file);
|
|
99
|
+ }
|
|
100
|
+ free(files->data);
|
|
101
|
+ free(*pfiles);
|
|
102
|
+ *pfiles = NULL;
|
|
103
|
+ }
|
|
104
|
+}
|
|
105
|
+
|
|
106
|
+void files_dump(struct files *files) {
|
|
107
|
+ int i;
|
|
108
|
+ fprintf(stdout,"num_files:%d\n", files->num_files);
|
|
109
|
+ fprintf(stdout,"alloc_files:%d\n", files->alloc_files);
|
|
110
|
+ fprintf(stdout,"total_sizes:%lld\n", files->total_size);
|
|
111
|
+ for (i=0; i<files->num_files; i++) {
|
|
112
|
+ struct fileinfo *f = files->data[i];
|
|
113
|
+ fprintf(stdout,"file[%d]->fd=%d\n", i, f->fd);
|
|
114
|
+ fprintf(stdout,"file[%d]->name=%s\n", i, f->name);
|
|
115
|
+ fprintf(stdout,"file[%d]->size=%llu\n", i, f->size);
|
|
116
|
+ }
|
|
117
|
+}
|
|
118
|
+
|
|
119
|
+int files_add_file(struct files *files, char *filename) {
|
|
120
|
+ int ret = 0;
|
|
121
|
+ struct stat sb;
|
|
122
|
+ if (stat(filename, &sb) != -1) {
|
|
123
|
+ struct fileinfo *file;
|
|
124
|
+
|
|
125
|
+ int fd = open(filename, O_RDONLY);
|
|
126
|
+ if (fd == -1) {
|
|
127
|
+ fprintf(stderr, "open(%s) error : %s\n", filename, strerror(errno));
|
|
128
|
+ return ret;
|
|
129
|
+ }
|
|
130
|
+
|
|
131
|
+ file = calloc(1, sizeof(struct fileinfo));
|
|
132
|
+ file->name = strdup(filename);
|
|
133
|
+ file->size = sb.st_size;
|
|
134
|
+ file->fd = fd;
|
|
135
|
+
|
|
136
|
+ files->total_size += file->size;
|
|
137
|
+
|
|
138
|
+ files->data[files->num_files] = file;
|
|
139
|
+ files->num_files++;
|
|
140
|
+ if (files->num_files >= files->alloc_files-1) {
|
|
141
|
+ files->alloc_files *= 2;
|
|
142
|
+ files->data = realloc(files->data, files->alloc_files * sizeof(struct fileinfo *));
|
|
143
|
+ }
|
|
144
|
+
|
|
145
|
+ ret = 1;
|
|
146
|
+ } else {
|
|
147
|
+ fprintf(stderr, "stat(%s) error : %s\n", filename, strerror(errno));
|
|
148
|
+ }
|
|
149
|
+ return ret;
|
|
150
|
+}
|
|
151
|
+
|
|
152
|
+int files_load_filelist(struct files *files, char *filename) {
|
|
153
|
+ size_t len;
|
|
154
|
+ ssize_t readed;
|
|
155
|
+ int ret = 0;
|
|
156
|
+ char *line = NULL;
|
|
157
|
+
|
|
158
|
+ FILE *file = fopen(filename, "r");
|
|
159
|
+ if (!file) {
|
|
160
|
+ fprintf(stderr, "Can't open %s : %s\n", filename, strerror(errno));
|
|
161
|
+ return ret;
|
|
162
|
+ }
|
|
163
|
+
|
|
164
|
+ while ((readed = getline(&line, &len, file)) != -1) {
|
|
165
|
+ line[readed-1] = '\0';
|
|
166
|
+ ret += files_add_file(files, line);
|
|
167
|
+ }
|
|
168
|
+ free(line);
|
|
169
|
+ fclose(file);
|
|
170
|
+
|
|
171
|
+ return ret;
|
|
172
|
+}
|
|
173
|
+
|
|
174
|
+static int fuse_getattr(const char *path, struct stat *stbuf) {
|
|
175
|
+ if (strcmp(path, "/") != 0)
|
|
176
|
+ return -ENOENT;
|
|
177
|
+
|
|
178
|
+ if (fstat(filelist->data[0]->fd, stbuf) == -1)
|
|
179
|
+ return -errno;
|
|
180
|
+
|
|
181
|
+ stbuf->st_size = filelist->total_size;
|
|
182
|
+
|
|
183
|
+ return 0;
|
|
184
|
+}
|
|
185
|
+
|
|
186
|
+static int fuse_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
|
|
187
|
+ (void)fi; // prevent warning
|
|
188
|
+ int i;
|
|
189
|
+ struct fileinfo *file = filelist->data[0];
|
|
190
|
+ ssize_t readen, totreaden = 0;
|
|
191
|
+ off_t fileofs = offset, passed = 0;
|
|
192
|
+ int filenum = 0;
|
|
193
|
+
|
|
194
|
+ if (strcmp(path, "/") != 0)
|
|
195
|
+ return -ENOENT;
|
|
196
|
+
|
|
197
|
+ if (offset >= filelist->total_size) // Hmm :)
|
|
198
|
+ return 0;
|
|
199
|
+
|
|
200
|
+ if (offset + size > filelist->total_size) { // Asking for too much
|
|
201
|
+ if (filelist->total_size < offset) // Prevent overflow
|
|
202
|
+ return 0;
|
|
203
|
+ size = filelist->total_size - offset;
|
|
204
|
+ }
|
|
205
|
+
|
|
206
|
+ if (offset > file->size) { // After the first file
|
|
207
|
+ // Find the file that corresponds to the required offset
|
|
208
|
+ // Can be slow with lots of files, but do it simple for now
|
|
209
|
+ for (i=0; i<filelist->num_files; i++) {
|
|
210
|
+ struct fileinfo *f = filelist->data[i];
|
|
211
|
+ passed += f->size;
|
|
212
|
+ if (passed >= offset) {
|
|
213
|
+ file = f;
|
|
214
|
+ filenum = i;
|
|
215
|
+ fileofs = file->size - (passed - offset);
|
|
216
|
+ break;
|
|
217
|
+ }
|
|
218
|
+ }
|
|
219
|
+ }
|
|
220
|
+
|
|
221
|
+ // Read all data
|
|
222
|
+ do {
|
|
223
|
+ readen = pread(file->fd, buf, size, fileofs);
|
|
224
|
+ if (readen == -1) // Error reading
|
|
225
|
+ return -errno;
|
|
226
|
+ totreaden += readen;
|
|
227
|
+ fileofs += readen;
|
|
228
|
+ buf += readen;
|
|
229
|
+ size -= readen;
|
|
230
|
+ if (fileofs >= file->size) {
|
|
231
|
+ fileofs = 0;
|
|
232
|
+ filenum++;
|
|
233
|
+ if (filenum >= filelist->num_files) {
|
|
234
|
+ break;
|
|
235
|
+ }
|
|
236
|
+ file = filelist->data[filenum];
|
|
237
|
+ }
|
|
238
|
+ } while(size > 0);
|
|
239
|
+
|
|
240
|
+ return totreaden;
|
|
241
|
+}
|
|
242
|
+
|
|
243
|
+static struct fuse_operations concatfs_op = {
|
|
244
|
+ .getattr = fuse_getattr,
|
|
245
|
+ .read = fuse_read,
|
|
246
|
+};
|
|
247
|
+
|
|
248
|
+int main(int argc, char *argv[]) {
|
|
249
|
+ int ret;
|
|
250
|
+ char *filenames;
|
|
251
|
+ char *mountpoint_file;
|
|
252
|
+ struct stat sb;
|
|
253
|
+
|
|
254
|
+ if (argc < 3) {
|
|
255
|
+ fprintf(stderr, "Usage: filejoinfs filelist.txt mount-point-file\n");
|
|
256
|
+ exit(EXIT_FAILURE);
|
|
257
|
+ }
|
|
258
|
+
|
|
259
|
+ filenames = argv[1];
|
|
260
|
+ mountpoint_file = argv[2];
|
|
261
|
+
|
|
262
|
+ if (stat(mountpoint_file, &sb) == -1) {
|
|
263
|
+ fprintf(stderr, "Can't mount on %s : %s\n", mountpoint_file, strerror(errno));
|
|
264
|
+ exit(EXIT_FAILURE);
|
|
265
|
+ } else {
|
|
266
|
+ if (!S_ISREG(sb.st_mode)) {
|
|
267
|
+ fprintf(stderr, "%s is not a file!\n", mountpoint_file);
|
|
268
|
+ exit(EXIT_FAILURE);
|
|
269
|
+ }
|
|
270
|
+ }
|
|
271
|
+
|
|
272
|
+ filelist = files_alloc();
|
|
273
|
+ if (!files_load_filelist(filelist, filenames)) {
|
|
274
|
+ fprintf(stderr, "Error no files loaded from %s.\n", argv[1]);
|
|
275
|
+ files_free(&filelist);
|
|
276
|
+ exit(EXIT_FAILURE);
|
|
277
|
+ }
|
|
278
|
+
|
|
279
|
+ char *fuse_argv[5];
|
|
280
|
+ fuse_argv[0] = argv[0];
|
|
281
|
+ fuse_argv[1] = mountpoint_file;
|
|
282
|
+ fuse_argv[2] = "-o";
|
|
283
|
+ fuse_argv[3] = "nonempty,allow_user,direct_io";
|
|
284
|
+ fuse_argv[4] = 0;
|
|
285
|
+
|
|
286
|
+ ret = fuse_main(4, fuse_argv, &concatfs_op, NULL);
|
|
287
|
+
|
|
288
|
+ files_free(&filelist);
|
|
289
|
+
|
|
290
|
+ return ret;
|
|
291
|
+}
|