main.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. #include "src/fs.h"
  2. #include "src/gpgme.h"
  3. #include "src/str.h"
  4. // http://libfuse.github.io/doxygen/globals.html
  5. #define FUSE_USE_VERSION 31
  6. #include <fuse.h>
  7. // https://www.gnupg.org/documentation/manuals/gpgme/Function-and-Data-Index.html
  8. #include <gpgme.h>
  9. // posix
  10. #include <dirent.h>
  11. #include <sys/stat.h>
  12. #include <sys/types.h>
  13. #include <unistd.h>
  14. #include <errno.h>
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include <string.h>
  18. #define FUSE_PATH_BUF_LEN 256
  19. static char cache_dir[] = "/tmp/rgpgfs-cache-XXXXXX";
  20. static const size_t CACHE_PATH_BUF_LEN = sizeof(cache_dir) + FUSE_PATH_BUF_LEN;
  21. static const char ENC_SUFFIX[] = ".gpg";
  22. static gpgme_ctx_t gpgme_ctx;
  23. static gpgme_key_t gpgme_recip_key;
  24. static struct { char *recipient_name; } rgpgfs_config;
  25. static struct fuse_opt rgpgfs_opts[] = {{"--recipient %s", 0, 0},
  26. {"--recipient=%s", 0, 0},
  27. {"-r %s", 0, 0},
  28. {"recipient=%s", 0, 0},
  29. FUSE_OPT_END};
  30. static int rgpgfs_encrypt(const char *source_path, char *cache_path) {
  31. // fprintf(stderr, "rgpgfs_encrypt('%s', %p)\n", source_path, cache_path);
  32. size_t source_path_len = strnlen(source_path, FUSE_PATH_BUF_LEN);
  33. if (source_path_len >= FUSE_PATH_BUF_LEN) {
  34. errno = ENAMETOOLONG;
  35. perror("rgpgfs_encrypt");
  36. return 1;
  37. }
  38. strcpy(cache_path, cache_dir);
  39. strcat(cache_path, source_path);
  40. struct stat source_stat;
  41. if (lstat(source_path, &source_stat)) {
  42. perror("rgpgfs_encrypt: failed to stat source file");
  43. return 1;
  44. }
  45. struct stat cache_stat;
  46. if (lstat(cache_path, &cache_stat) ||
  47. source_stat.st_mtim.tv_sec > cache_stat.st_mtim.tv_sec) {
  48. if (rgpgfs_fs_mkdirs(cache_path)) {
  49. perror("rgpgfs_encrypt: failed to create dirs");
  50. return 1;
  51. }
  52. // list of recipients may implicitly include the default recipient
  53. gpgme_key_t recip_keys[] = {gpgme_recip_key, NULL};
  54. if (rgpgfs_gpgme_encrypt_file_to_file(gpgme_ctx, recip_keys, source_path,
  55. cache_path)) {
  56. fprintf(stderr, "%s: failed to create encrypted cache of %s\n", __func__,
  57. source_path);
  58. return 1;
  59. }
  60. printf("encrypted %s\n", source_path);
  61. }
  62. return 0;
  63. }
  64. static int rgpgfs_getattr(const char *mount_path, struct stat *statbuf,
  65. struct fuse_file_info *fi) {
  66. if (!lstat(mount_path, statbuf)) {
  67. if (S_ISREG(statbuf->st_mode)) {
  68. fprintf(stderr, "%s: tried to access path without %s suffix: %s\n",
  69. __func__, ENC_SUFFIX, mount_path);
  70. return -ENOENT; // missing ENC_SUFFIX
  71. }
  72. return 0;
  73. }
  74. char source_path[FUSE_PATH_BUF_LEN];
  75. if (rgpgfs_strncpy_without_suffix(source_path, mount_path, ENC_SUFFIX,
  76. FUSE_PATH_BUF_LEN - 1)) {
  77. return -ENOENT;
  78. }
  79. char cache_path[CACHE_PATH_BUF_LEN];
  80. if (rgpgfs_encrypt(source_path, cache_path))
  81. return -errno;
  82. if (lstat(cache_path, statbuf))
  83. return -errno;
  84. return 0;
  85. }
  86. static int rgpgfs_access(const char *mount_path, int mask) {
  87. struct stat statbuf;
  88. if (!lstat(mount_path, &statbuf)) {
  89. if (S_ISREG(statbuf.st_mode)) {
  90. fprintf(stderr, "%s: tried to access path without %s suffix: %s\n",
  91. __func__, ENC_SUFFIX, mount_path);
  92. return -ENOENT;
  93. }
  94. if (access(mount_path, mask)) {
  95. return -errno;
  96. }
  97. return 0;
  98. }
  99. char source_path[FUSE_PATH_BUF_LEN];
  100. if (rgpgfs_strncpy_without_suffix(source_path, mount_path, ENC_SUFFIX,
  101. FUSE_PATH_BUF_LEN - 1)) {
  102. fprintf(stderr, "%s: invalid suffix in path: %s\n", __func__, mount_path);
  103. return -ENOENT;
  104. }
  105. if (access(source_path, mask)) {
  106. return -errno;
  107. }
  108. return 0;
  109. }
  110. static int rgpgfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
  111. off_t offset, struct fuse_file_info *fi,
  112. enum fuse_readdir_flags flags) {
  113. DIR *dirp = opendir(path);
  114. if (dirp == NULL) {
  115. return -errno;
  116. }
  117. errno = 0;
  118. struct dirent *entp;
  119. const size_t dirent_name_max_len = sizeof(entp->d_name);
  120. while ((entp = readdir(dirp)) != NULL) {
  121. struct stat statbf;
  122. memset(&statbf, 0, sizeof(statbf));
  123. statbf.st_ino = entp->d_ino;
  124. statbf.st_mode = entp->d_type << 12;
  125. if (S_ISREG(statbf.st_mode)) {
  126. if (strnlen(entp->d_name, dirent_name_max_len) + strlen(ENC_SUFFIX) >=
  127. dirent_name_max_len) {
  128. errno = ENAMETOOLONG;
  129. break;
  130. }
  131. strcat(entp->d_name, ENC_SUFFIX);
  132. }
  133. if (filler(buf, entp->d_name, &statbf, 0, 0)) {
  134. errno = ENOBUFS;
  135. break;
  136. }
  137. }
  138. closedir(dirp);
  139. return -errno;
  140. }
  141. static int rgpgfs_open(const char *mount_path, struct fuse_file_info *fi) {
  142. char source_path[FUSE_PATH_BUF_LEN];
  143. if (rgpgfs_strncpy_without_suffix(source_path, mount_path, ENC_SUFFIX,
  144. FUSE_PATH_BUF_LEN - 1)) {
  145. fprintf(stderr, "%s: invalid suffix in path: %s\n", __func__, mount_path);
  146. return -ENOENT;
  147. }
  148. char cache_path[CACHE_PATH_BUF_LEN];
  149. if (rgpgfs_encrypt(source_path, cache_path))
  150. return -errno;
  151. int res = open(cache_path, fi->flags);
  152. if (res == -1)
  153. return -errno;
  154. fi->fh = res;
  155. return 0;
  156. }
  157. static int rgpgfs_read(const char *path, char *buf, size_t count, off_t offset,
  158. struct fuse_file_info *fi) {
  159. if (fi == NULL) {
  160. return ENOTSUP;
  161. }
  162. ssize_t bytes_num = pread(fi->fh, buf, count, offset);
  163. if (bytes_num == -1) {
  164. return -errno;
  165. }
  166. return bytes_num;
  167. }
  168. static int rgpgfs_release(const char *path, struct fuse_file_info *fi) {
  169. close(fi->fh);
  170. return 0;
  171. }
  172. static struct fuse_operations rgpgfs_fuse_operations = {
  173. .getattr = rgpgfs_getattr,
  174. .open = rgpgfs_open,
  175. .read = rgpgfs_read,
  176. .release = rgpgfs_release,
  177. .readdir = rgpgfs_readdir,
  178. .access = rgpgfs_access,
  179. };
  180. int main(int argc, char *argv[]) {
  181. struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
  182. rgpgfs_config.recipient_name = NULL;
  183. fuse_opt_parse(&args, &rgpgfs_config, rgpgfs_opts, NULL);
  184. printf("gpgme version: %s\n", gpgme_check_version(NULL));
  185. gpg_error_t gpgme_init_err = gpgme_new(&gpgme_ctx);
  186. if (gpgme_init_err != GPG_ERR_NO_ERROR) {
  187. fprintf(stderr, "Failed to initialize gpgme: %s (%d)\n",
  188. gpg_strerror(gpgme_init_err), gpgme_init_err);
  189. return 1;
  190. }
  191. if (rgpgfs_config.recipient_name == NULL) {
  192. fprintf(stderr, "Missing parameter --recipient\n");
  193. return 1;
  194. }
  195. printf("recipient name: %s\n", rgpgfs_config.recipient_name);
  196. if (rgpgfs_gpgme_get_encrypt_key(gpgme_ctx, rgpgfs_config.recipient_name,
  197. &gpgme_recip_key)) {
  198. gpgme_release(gpgme_ctx);
  199. return 1;
  200. }
  201. gpgme_user_id_t gpgme_recip_id = gpgme_recip_key->uids;
  202. while (gpgme_recip_id != NULL) {
  203. printf("recipient: %s\n", gpgme_recip_id->uid);
  204. gpgme_recip_id = gpgme_recip_id->next;
  205. }
  206. printf("recipient fingerprint: %s\n", gpgme_recip_key->fpr);
  207. if (mkdtemp(cache_dir) == NULL) {
  208. perror("Failed to create cache dir");
  209. return 1;
  210. }
  211. printf("cache: %s\n", cache_dir);
  212. int fuse_main_err =
  213. fuse_main(args.argc, args.argv, &rgpgfs_fuse_operations, NULL);
  214. // TODO rm -r cache_dir (see man nftw)
  215. gpgme_release(gpgme_ctx);
  216. return fuse_main_err;
  217. }