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