fjfs is FUSE module that implements virtual joining of multiple files as one. https://georgi.unixsol.org/programs/fjfs/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

fjfs.c 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. /*
  2. * fjfs
  3. * fjfs is FUSE module that implements virtual joining of multiple files as one.
  4. *
  5. * Copyright (c) 2010-2011 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. #define FUSE_USE_VERSION 26
  26. #define _XOPEN_SOURCE 500 // for "pread"
  27. #define _GNU_SOURCE 1 // for "getline"
  28. #include <stdio.h>
  29. #include <stdlib.h>
  30. #include <error.h>
  31. #include <string.h>
  32. #include <errno.h>
  33. #include <unistd.h>
  34. #include <sys/types.h>
  35. #include <fuse.h>
  36. /* Handle list of files */
  37. struct fileinfo {
  38. int fd;
  39. char *name;
  40. off_t size;
  41. };
  42. struct files {
  43. int num_files;
  44. off_t total_size;
  45. int alloc_files;
  46. struct fileinfo **data;
  47. };
  48. struct files *filelist;
  49. char *filenames;
  50. char *mountpoint;
  51. int mountpoint_created = 0;
  52. static struct files *files_alloc(void) {
  53. struct files *f = calloc(1, sizeof(struct files));
  54. f->alloc_files = 64;
  55. f->data = calloc(f->alloc_files, sizeof(struct fileinfo *));
  56. return f;
  57. }
  58. static void files_free(struct files **pfiles) {
  59. struct files *files = *pfiles;
  60. struct fileinfo *file;
  61. int i;
  62. if (files) {
  63. for (i=0; i<files->num_files; i++) {
  64. file = files->data[i];
  65. close(file->fd);
  66. free(file->name);
  67. free(file);
  68. }
  69. free(files->data);
  70. free(*pfiles);
  71. *pfiles = NULL;
  72. }
  73. }
  74. #if 0
  75. void files_dump(struct files *files) {
  76. int i;
  77. fprintf(stdout,"num_files:%d\n", files->num_files);
  78. fprintf(stdout,"alloc_files:%d\n", files->alloc_files);
  79. fprintf(stdout,"total_sizes:%lld\n", (unsigned long long)files->total_size);
  80. for (i=0; i<files->num_files; i++) {
  81. struct fileinfo *f = files->data[i];
  82. fprintf(stdout,"file[%d]->fd=%d\n", i, f->fd);
  83. fprintf(stdout,"file[%d]->name=%s\n", i, f->name);
  84. fprintf(stdout,"file[%d]->size=%llu\n", i, (unsigned long long)f->size);
  85. }
  86. }
  87. #endif
  88. static int files_add_file(struct files *files, char *filename) {
  89. int ret = 0;
  90. struct stat sb;
  91. if (stat(filename, &sb) != -1) {
  92. struct fileinfo *file;
  93. int fd = open(filename, O_RDONLY);
  94. if (fd == -1) {
  95. fprintf(stderr, "open(%s) error : %s\n", filename, strerror(errno));
  96. return ret;
  97. }
  98. file = calloc(1, sizeof(struct fileinfo));
  99. file->name = strdup(filename);
  100. file->size = sb.st_size;
  101. file->fd = fd;
  102. files->total_size += file->size;
  103. files->data[files->num_files] = file;
  104. files->num_files++;
  105. if (files->num_files >= files->alloc_files-1) {
  106. files->alloc_files *= 2;
  107. files->data = realloc(files->data, files->alloc_files * sizeof(struct fileinfo *));
  108. }
  109. ret = 1;
  110. } else {
  111. fprintf(stderr, "stat(%s) error : %s\n", filename, strerror(errno));
  112. }
  113. return ret;
  114. }
  115. static int files_load_filelist(struct files *files, char *filename) {
  116. size_t len;
  117. ssize_t readed;
  118. int ret = 0;
  119. char *line = NULL;
  120. FILE *file = fopen(filename, "r");
  121. if (!file) {
  122. fprintf(stderr, "Can't open %s : %s\n", filename, strerror(errno));
  123. return ret;
  124. }
  125. while ((readed = getline(&line, &len, file)) != -1) {
  126. line[readed-1] = '\0';
  127. ret += files_add_file(files, line);
  128. }
  129. free(line);
  130. fclose(file);
  131. return ret;
  132. }
  133. static int fuse_getattr(const char *path, struct stat *stbuf) {
  134. if (strcmp(path, "/") != 0)
  135. return -ENOENT;
  136. if (fstat(filelist->data[0]->fd, stbuf) == -1)
  137. return -errno;
  138. stbuf->st_size = filelist->total_size;
  139. return 0;
  140. }
  141. static int fuse_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
  142. (void)fi; // prevent warning
  143. int i;
  144. struct fileinfo *file = filelist->data[0];
  145. ssize_t readen, totreaden = 0;
  146. off_t fileofs = offset, passed = 0;
  147. int filenum = 0;
  148. if (strcmp(path, "/") != 0)
  149. return -ENOENT;
  150. if (offset >= filelist->total_size) // Hmm :)
  151. return 0;
  152. if (offset + size > filelist->total_size) { // Asking for too much
  153. if (filelist->total_size < offset) // Prevent overflow
  154. return 0;
  155. size = filelist->total_size - offset;
  156. }
  157. if (offset > file->size) { // After the first file
  158. // Find the file that corresponds to the required offset
  159. // Can be slow with lots of files, but do it simple for now
  160. for (i=0; i<filelist->num_files; i++) {
  161. struct fileinfo *f = filelist->data[i];
  162. passed += f->size;
  163. if (passed >= offset) {
  164. file = f;
  165. filenum = i;
  166. fileofs = file->size - (passed - offset);
  167. break;
  168. }
  169. }
  170. }
  171. // Read all data
  172. do {
  173. readen = pread(file->fd, buf, size, fileofs);
  174. if (readen == -1) // Error reading
  175. return -errno;
  176. totreaden += readen;
  177. fileofs += readen;
  178. buf += readen;
  179. size -= readen;
  180. if (fileofs >= file->size) {
  181. fileofs = 0;
  182. filenum++;
  183. if (filenum >= filelist->num_files) {
  184. break;
  185. }
  186. file = filelist->data[filenum];
  187. }
  188. } while(size > 0);
  189. return totreaden;
  190. }
  191. static int fjfs_unlink(const char *path) {
  192. if (strcmp(path, "/") != 0)
  193. return -ENOENT;
  194. return unlink(path);
  195. }
  196. static void fjfs_destroy(void *f __attribute__((unused))) {
  197. if (mountpoint_created)
  198. unlink(mountpoint);
  199. }
  200. static struct fuse_operations concatfs_op = {
  201. .getattr = fuse_getattr,
  202. .read = fuse_read,
  203. .unlink = fjfs_unlink,
  204. .destroy = fjfs_destroy,
  205. };
  206. int main(int argc, char *argv[]) {
  207. int ret;
  208. struct stat sb;
  209. if (argc < 3) {
  210. fprintf(stderr, "Usage: %s mount-point-file filelist.txt\n", argv[0]);
  211. exit(EXIT_FAILURE);
  212. }
  213. mountpoint = argv[1];
  214. filenames = argv[2];
  215. if (stat(mountpoint, &sb) == -1) {
  216. FILE *f = fopen(mountpoint, "wb");
  217. if (f) {
  218. mountpoint_created = 1;
  219. fclose(f);
  220. } else {
  221. fprintf(stderr, "Can't create mount point %s : %s\n", mountpoint, strerror(errno));
  222. exit(EXIT_FAILURE);
  223. }
  224. } else {
  225. if (!S_ISREG(sb.st_mode)) {
  226. fprintf(stderr, "%s is not a file!\n", mountpoint);
  227. exit(EXIT_FAILURE);
  228. }
  229. }
  230. filelist = files_alloc();
  231. if (!files_load_filelist(filelist, filenames)) {
  232. fprintf(stderr, "Error no files loaded.\n");
  233. files_free(&filelist);
  234. if (mountpoint_created)
  235. unlink(mountpoint);
  236. exit(EXIT_FAILURE);
  237. }
  238. char *fuse_argv[5];
  239. fuse_argv[0] = argv[0];
  240. fuse_argv[1] = mountpoint;
  241. fuse_argv[2] = "-o";
  242. fuse_argv[3] = "nonempty,allow_other,fsname=fjfs";
  243. fuse_argv[4] = 0;
  244. ret = fuse_main(4, fuse_argv, &concatfs_op, NULL);
  245. if (mountpoint_created)
  246. unlink(mountpoint);
  247. files_free(&filelist);
  248. return ret;
  249. }