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.

248 lines
4.5 KiB

5 years ago
  1. #define _GNU_SOURCE
  2. #include "fuse_chan.h"
  3. #include "fuse_lowlevel.h"
  4. #include "fuse_kernel.h"
  5. #include "sys.h"
  6. #include <assert.h>
  7. #include <errno.h>
  8. #include <fcntl.h>
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <sys/ioctl.h>
  13. #include <unistd.h>
  14. static const char DEV_FUSE[] = "/dev/fuse";
  15. static
  16. void*
  17. aligned_mem(const uint64_t size_)
  18. {
  19. int rv;
  20. void *mem;
  21. int pagesize;
  22. pagesize = sys_get_pagesize();
  23. rv = posix_memalign(&mem,pagesize,size_);
  24. if(rv == 0)
  25. return mem;
  26. errno = rv;
  27. return NULL;
  28. }
  29. static
  30. int
  31. clone_devfuse_fd(const int devfuse_fd_)
  32. {
  33. #ifdef FUSE_DEV_IOC_CLONE
  34. int rv;
  35. int clone_fd;
  36. clone_fd = open(DEV_FUSE,O_RDWR|O_CLOEXEC);
  37. if(clone_fd == -1)
  38. return devfuse_fd_;
  39. rv = ioctl(clone_fd,FUSE_DEV_IOC_CLONE,&devfuse_fd_);
  40. if(rv == -1)
  41. {
  42. perror("fuse: failed to clone /dev/fuse");
  43. close(clone_fd);
  44. return devfuse_fd_;
  45. }
  46. return clone_fd;
  47. #else
  48. return devfuse_fd_;
  49. #endif
  50. }
  51. int
  52. fuse_msg_bufsize(void)
  53. {
  54. int bufsize;
  55. bufsize = ((FUSE_MSG_MAX_PAGES + 1) * sys_get_pagesize());
  56. return bufsize;
  57. }
  58. fuse_chan_t*
  59. fuse_chan_new(int32_t devfuse_fd_,
  60. uint32_t flags_)
  61. {
  62. int rv;
  63. int bufsize;
  64. fuse_chan_t *ch;
  65. ch = (fuse_chan_t*)calloc(1,sizeof(fuse_chan_t));
  66. if(ch == NULL)
  67. {
  68. fprintf(stderr, "fuse: failed to allocate channel memory\n");
  69. return NULL;
  70. }
  71. bufsize = fuse_msg_bufsize();
  72. ch->fd = clone_devfuse_fd(devfuse_fd_);
  73. ch->buf = aligned_mem(bufsize);
  74. ch->bufsize = bufsize;
  75. ch->flags = flags_;
  76. if(flags_ & (FUSE_CHAN_SPLICE_READ|FUSE_CHAN_SPLICE_WRITE))
  77. {
  78. rv = sys_alloc_pipe(ch->splice_pipe,bufsize);
  79. if(rv == -1)
  80. ch->flags &= ~(FUSE_CHAN_SPLICE_READ|FUSE_CHAN_SPLICE_WRITE);
  81. }
  82. return ch;
  83. }
  84. static
  85. int64_t
  86. fuse_chan_recv_splice(fuse_chan_t *ch_,
  87. char *buf_,
  88. uint64_t size_)
  89. {
  90. int64_t rv;
  91. struct iovec iov;
  92. restart_splice:
  93. rv = splice(ch_->fd,NULL,ch_->splice_pipe[1],NULL,size_,SPLICE_F_MOVE);
  94. switch((rv == -1) ? errno : 0)
  95. {
  96. case 0:
  97. break;
  98. case ENOENT:
  99. case EINTR:
  100. case EAGAIN:
  101. goto restart_splice;
  102. case ENODEV:
  103. return 0;
  104. default:
  105. return -errno;
  106. }
  107. iov.iov_base = buf_;
  108. iov.iov_len = rv;
  109. restart_vmsplice:
  110. rv = vmsplice(ch_->splice_pipe[0],&iov,1,SPLICE_F_MOVE);
  111. switch((rv == -1) ? errno : 0)
  112. {
  113. case 0:
  114. break;
  115. case ENOENT:
  116. case EINTR:
  117. case EAGAIN:
  118. goto restart_vmsplice;
  119. case ENODEV:
  120. return 0;
  121. default:
  122. return -errno;
  123. }
  124. return rv;
  125. }
  126. static
  127. int
  128. fuse_chan_recv_read(fuse_chan_t *ch_,
  129. char *buf_,
  130. uint64_t size_)
  131. {
  132. int64_t rv;
  133. restart:
  134. rv = read(ch_->fd,buf_,size_);
  135. switch((rv == -1) ? errno : 0)
  136. {
  137. case 0:
  138. break;
  139. case ENOENT:
  140. case EINTR:
  141. case EAGAIN:
  142. goto restart;
  143. case ENODEV:
  144. return 0;
  145. default:
  146. return -errno;
  147. }
  148. if(rv < sizeof(struct fuse_in_header))
  149. {
  150. fprintf(stderr, "fuse: short read on fuse device\n");
  151. return -EIO;
  152. }
  153. return rv;
  154. }
  155. int64_t
  156. fuse_chan_recv(fuse_chan_t *ch_,
  157. char *buf_,
  158. size_t size_)
  159. {
  160. if(ch_->flags & FUSE_CHAN_SPLICE_READ)
  161. return fuse_chan_recv_splice(ch_,buf_,size_);
  162. return fuse_chan_recv_read(ch_,buf_,size_);
  163. }
  164. static
  165. int64_t
  166. fuse_chan_send_write(fuse_chan_t *ch_,
  167. const struct iovec iov_[],
  168. size_t count_)
  169. {
  170. int64_t rv;
  171. if(iov_ == NULL)
  172. return 0;
  173. rv = writev(ch_->fd,iov_,count_);
  174. if(rv == -1)
  175. return -errno;
  176. return rv;
  177. }
  178. static
  179. int64_t
  180. fuse_chan_send_splice(fuse_chan_t *ch_,
  181. const struct iovec iov_[],
  182. size_t count_)
  183. {
  184. int64_t rv;
  185. rv = vmsplice(ch_->splice_pipe[1],iov_,count_,SPLICE_F_MOVE);
  186. if(rv == -1)
  187. return -errno;
  188. rv = splice(ch_->splice_pipe[0],NULL,ch_->fd,NULL,rv,SPLICE_F_MOVE);
  189. return rv;
  190. }
  191. int64_t
  192. fuse_chan_send(fuse_chan_t *ch,
  193. const struct iovec iov[],
  194. size_t count)
  195. {
  196. if(ch->flags & FUSE_CHAN_SPLICE_WRITE)
  197. return fuse_chan_send_splice(ch,iov,count);
  198. return fuse_chan_send_write(ch,iov,count);
  199. }
  200. void
  201. fuse_chan_destroy(fuse_chan_t *ch)
  202. {
  203. close(ch->fd);
  204. if(ch->flags & (FUSE_CHAN_SPLICE_READ|FUSE_CHAN_SPLICE_WRITE))
  205. {
  206. close(ch->splice_pipe[0]);
  207. close(ch->splice_pipe[1]);
  208. }
  209. free(ch->buf);
  210. free(ch);
  211. }