stx_fileio.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. /*
  2. * File I/O extension to the State Threads Library.
  3. */
  4. /*
  5. * The contents of this file are subject to the Mozilla Public
  6. * License Version 1.1 (the "License"); you may not use this file
  7. * except in compliance with the License. You may obtain a copy of
  8. * the License at http://www.mozilla.org/MPL/
  9. *
  10. * Software distributed under the License is distributed on an "AS
  11. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  12. * implied. See the License for the specific language governing
  13. * rights and limitations under the License.
  14. *
  15. * The Original Code is the file I/O extension to the State Threads Library.
  16. *
  17. * The Initial Developer of the Original Code is Jeff
  18. * <jlb-st@houseofdistraction.com>. Portions created by the Initial
  19. * Developer are Copyright (C) 2002 the Initial Developer. All Rights
  20. * Reserved.
  21. *
  22. * Contributor(s): (none)
  23. *
  24. * Alternatively, the contents of this file may be used under the
  25. * terms of the GNU General Public License Version 2 or later (the
  26. * "GPL"), in which case the provisions of the GPL are applicable
  27. * instead of those above. If you wish to allow use of your
  28. * version of this file only under the terms of the GPL and not to
  29. * allow others to use your version of this file under the MPL,
  30. * indicate your decision by deleting the provisions above and
  31. * replace them with the notice and other provisions required by
  32. * the GPL. If you do not delete the provisions above, a recipient
  33. * may use your version of this file under either the MPL or the
  34. * GPL.
  35. */
  36. #include <stdlib.h>
  37. #include "stx_fileio.h"
  38. #define STX_FILEIO_SIGNUM SIGUSR2
  39. typedef struct {
  40. st_netfd_t data_fd;
  41. st_netfd_t control_fd;
  42. pid_t pid;
  43. } fileio_data_t;
  44. #define FILEREADER_MAX_READ 1024
  45. typedef struct {
  46. off_t offset;
  47. ssize_t nbytes;
  48. } file_reader_cb_t;
  49. /**
  50. * Fork a process to read a file and return its pid. Receives
  51. * offset/length commands from control stream and sends corresponding data
  52. * to out stream. A zero length on the control stream signals an end.
  53. *
  54. * @param fd stream from which to read
  55. * @param control_out receives the file descriptor to which control commands can be sent
  56. * @param fd_out receives the file descriptor from which the output of the command can be read.
  57. * @return PID of the process created to execute the command
  58. */
  59. pid_t
  60. file_reader(int fd, int *fd_control, int *fd_out)
  61. {
  62. pid_t pid;
  63. int control_pipe[2], out_pipe[2];
  64. if (pipe(control_pipe) < 0 || pipe(out_pipe) < 0)
  65. return (pid_t)-1;
  66. pid = fork();
  67. if (pid == (pid_t) -1)
  68. {
  69. close(control_pipe[0]);
  70. close(control_pipe[1]);
  71. close(out_pipe[0]);
  72. close(out_pipe[1]);
  73. return pid;
  74. }
  75. else if (pid == (pid_t) 0)
  76. {
  77. // child
  78. off_t pos = 0;
  79. file_reader_cb_t cb;
  80. char buf[FILEREADER_MAX_READ];
  81. if (fd == -1)
  82. _exit(EXIT_FAILURE);
  83. while (sizeof(cb) == read(control_pipe[0], &cb, sizeof(cb))) {
  84. ssize_t nb;
  85. if (0 >= cb.nbytes)
  86. goto clean_exit;
  87. if (pos != cb.offset) {
  88. pos = lseek(fd, cb.offset, SEEK_SET);
  89. if (pos == (off_t)-1)
  90. break;
  91. }
  92. nb = read(fd, buf, cb.nbytes);
  93. if (nb == (ssize_t)-1)
  94. break;
  95. pos += nb;
  96. write(out_pipe[1], (char *)&nb, sizeof(nb));
  97. write(out_pipe[1], buf, nb);
  98. }
  99. perror("ERROR: file_reader: ");
  100. clean_exit:
  101. close(control_pipe[0]);
  102. close(control_pipe[1]);
  103. close(out_pipe[0]);
  104. close(out_pipe[1]);
  105. _exit(EXIT_SUCCESS);
  106. }
  107. // parent
  108. close(out_pipe[1]);
  109. close(control_pipe[0]);
  110. *fd_out = out_pipe[0];
  111. *fd_control = control_pipe[1];
  112. return pid;
  113. }
  114. /**
  115. * fileio_data_t destructor callback
  116. */
  117. static void
  118. fileio_data_destructor(void *dat_in)
  119. {
  120. if (dat_in) {
  121. fileio_data_t *dat = (fileio_data_t *)dat_in;
  122. file_reader_cb_t cb;
  123. cb.offset = 0;
  124. cb.nbytes = 0;
  125. st_write(dat->control_fd, (char *)&cb, sizeof(cb),
  126. ST_UTIME_NO_TIMEOUT);
  127. waitpid(dat->pid, NULL, 0);
  128. st_netfd_close(dat->control_fd);
  129. st_netfd_close(dat->data_fd);
  130. free(dat_in);
  131. }
  132. }
  133. /**
  134. * Retrieve fileio_data_t struct from an st descriptor. Create and store
  135. * a new one if needed.
  136. */
  137. static fileio_data_t *get_fileio_data(st_netfd_t fd)
  138. {
  139. fileio_data_t *dat = (fileio_data_t *)st_netfd_getspecific(fd);
  140. if (!dat) {
  141. int fd_control, fd_out;
  142. pid_t pid = file_reader(st_netfd_fileno(fd), &fd_control, &fd_out);
  143. if (pid != (pid_t)-1) {
  144. dat = (fileio_data_t *)calloc(1, sizeof(fileio_data_t));
  145. dat->control_fd = st_netfd_open(fd_control);
  146. dat->data_fd = st_netfd_open(fd_out);
  147. dat->pid = pid;
  148. st_netfd_setspecific(fd, dat, fileio_data_destructor);
  149. }
  150. }
  151. return dat;
  152. }
  153. /**
  154. * Read data from the specified section of a file. Uses a forked
  155. * file_reader process to do the actual reading so as to avoid causing all
  156. * State Threads to block.
  157. *
  158. * @param fd must refer to a seekable file.
  159. * @param offset absolute offset within the file
  160. * @param buf output buffer
  161. * @param nbytes size of the output buffer
  162. * @param timeout
  163. */
  164. ssize_t
  165. stx_file_read(st_netfd_t fd, off_t offset, void *buf, size_t nbytes, st_utime_t timeout)
  166. {
  167. fileio_data_t *dat = get_fileio_data(fd);
  168. if (dat) {
  169. file_reader_cb_t cb;
  170. ssize_t ret = (ssize_t)-1;
  171. cb.offset = offset;
  172. cb.nbytes = nbytes;
  173. st_write(dat->control_fd, (char *)&cb, sizeof(cb), timeout);
  174. if (sizeof(ret) == st_read(dat->data_fd, (char *)&ret, sizeof(ret), timeout) && 0 < ret && ret <= nbytes) {
  175. return st_read(dat->data_fd, buf, ret, timeout);
  176. } else {
  177. return ret;
  178. }
  179. }
  180. return (ssize_t)-1;
  181. }