xref: /unit/src/nxt_job_file.c (revision 0:a63ceefd6ab0)
1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) NGINX, Inc.
5  */
6 
7 
8 #include <nxt_main.h>
9 
10 
11 static void nxt_job_file_open_and_read(nxt_thread_t *thr, void *obj,
12     void *data);
13 static nxt_int_t nxt_job_file_open(nxt_job_file_t *jbf);
14 static nxt_int_t nxt_job_file_info(nxt_job_file_t *jbf);
15 static nxt_int_t nxt_job_file_mmap(nxt_job_file_t *jbf, size_t size);
16 static nxt_int_t nxt_job_file_read_data(nxt_job_file_t *jbf, size_t size);
17 static nxt_int_t nxt_job_file_read_required(nxt_job_file_t *jbf);
18 
19 
20 nxt_job_file_t *
21 nxt_job_file_create(nxt_mem_pool_t *mp)
22 {
23     nxt_job_file_t  *jbf;
24 
25     jbf = nxt_job_create(mp, sizeof(nxt_job_file_t));
26 
27     if (nxt_fast_path(jbf != NULL)) {
28         jbf->file.fd = NXT_FILE_INVALID;
29         jbf->file.accessed = NXT_FILE_ACCESSED_LONG_AGO;
30         jbf->read_required = nxt_job_file_read_required;
31     }
32 
33     return jbf;
34 }
35 
36 
37 void
38 nxt_job_file_init(nxt_job_file_t *jbf)
39 {
40     nxt_job_init(&jbf->job, sizeof(nxt_job_file_t));
41 
42     jbf->file.fd = NXT_FILE_INVALID;
43     jbf->file.accessed = NXT_FILE_ACCESSED_LONG_AGO;
44     jbf->read_required = nxt_job_file_read_required;
45 }
46 
47 
48 /*
49  * Must be a function but not a macro, because
50  * it can be used as function pointer.
51  */
52 
53 void
54 nxt_job_file_read(nxt_thread_t *thr, nxt_job_t *job)
55 {
56     nxt_job_start(thr, job, nxt_job_file_open_and_read);
57 }
58 
59 
60 static void
61 nxt_job_file_open_and_read(nxt_thread_t *thr, void *obj, void *data)
62 {
63     size_t              size;
64     nxt_int_t           n;
65     nxt_bool_t          read_ahead;
66     nxt_file_t          *file;
67     nxt_job_file_t      *jbf;
68     nxt_work_handler_t  handler;
69 
70     jbf = obj;
71     file = &jbf->file;
72 
73     nxt_log_debug(thr->log, "file job read: \"%FN\"", file->name);
74 
75     if (file->fd != NXT_FILE_INVALID && jbf->close_before_open) {
76         nxt_file_close(file);
77         file->fd = NXT_FILE_INVALID;
78     }
79 
80     if (file->fd == NXT_FILE_INVALID) {
81 
82         switch (nxt_job_file_open(jbf)) {
83 
84         case NXT_OK:
85             break;
86 
87         case NXT_DECLINED:
88             handler = jbf->ready_handler;
89             goto done;
90 
91         default: /* NXT_ERROR */
92             handler = jbf->error_handler;
93             goto done;
94         }
95     }
96 
97     if (file->size > 0) {
98 
99         if (jbf->buffer != NULL) {
100             size = nxt_buf_mem_size(&jbf->buffer->mem);
101             size = nxt_min(file->size, (nxt_off_t) size);
102             read_ahead = nxt_buf_is_mmap(jbf->buffer);
103 
104         } else {
105             size = nxt_min(file->size, 1024 * 1024);
106             read_ahead = jbf->read_ahead;
107         }
108 
109         if (read_ahead) {
110             nxt_file_read_ahead(&jbf->file, jbf->offset, size);
111         }
112 
113         if (jbf->buffer != NULL) {
114 
115             if (nxt_buf_is_mmap(jbf->buffer)) {
116                 n = nxt_job_file_mmap(jbf, size);
117 
118             } else {
119                 n = nxt_job_file_read_data(jbf, size);
120             }
121 
122             if (nxt_slow_path(n != NXT_OK)) {
123                 handler = jbf->error_handler;
124                 goto done;
125             }
126         }
127     }
128 
129     if (jbf->offset == file->size) {
130         jbf->complete = 1;
131 
132         if (jbf->close) {
133             nxt_file_close(file);
134             file->fd = NXT_FILE_INVALID;
135         }
136     }
137 
138     nxt_job_return(thr, &jbf->job, jbf->ready_handler);
139     return;
140 
141 done:
142 
143     if (file->fd != NXT_FILE_INVALID) {
144         nxt_file_close(file);
145         file->fd = NXT_FILE_INVALID;
146     }
147 
148     nxt_job_return(thr, &jbf->job, handler);
149 }
150 
151 
152 static nxt_int_t
153 nxt_job_file_open(nxt_job_file_t *jbf)
154 {
155     nxt_int_t  n;
156 
157     if (jbf->test_before_open) {
158         n = nxt_job_file_info(jbf);
159 
160         if (n != NXT_OK) {
161             goto test_directory;
162         }
163 
164         if (jbf->file.type == NXT_FILE_DIRECTORY) {
165             return NXT_DECLINED;
166         }
167 
168         if (jbf->read_required(jbf) != NXT_OK) {
169             return NXT_DECLINED;
170         }
171     }
172 
173     n = nxt_file_open(&jbf->file, NXT_FILE_RDONLY, NXT_FILE_OPEN, 0);
174 
175     if (n == NXT_OK) {
176         n = nxt_job_file_info(jbf);
177 
178         if (nxt_fast_path(n == NXT_OK)) {
179 
180             if (jbf->file.type == NXT_FILE_DIRECTORY) {
181                 return NXT_DECLINED;
182             }
183 
184             return jbf->read_required(jbf);
185         }
186 
187         return n;
188     }
189 
190 test_directory:
191 
192     if (jbf->directory_end != 0
193         && jbf->file.error != NXT_ENOTDIR
194         && jbf->file.error != NXT_ENAMETOOLONG
195         && jbf->file.error != NXT_EACCES)
196     {
197         jbf->file.name[jbf->directory_end] = '\0';
198 
199         return nxt_job_file_info(jbf);
200     }
201 
202     return n;
203 }
204 
205 
206 static nxt_int_t
207 nxt_job_file_info(nxt_job_file_t *jbf)
208 {
209     nxt_int_t        n;
210     nxt_file_t       *file;
211     nxt_file_info_t  fi;
212 
213     file = &jbf->file;
214 
215     n = nxt_file_info(file, &fi);
216 
217     if (n != NXT_OK) {
218         return NXT_ERROR;
219     }
220 
221     if (nxt_is_file(&fi)) {
222         file->type = NXT_FILE_REGULAR;
223         file->size = nxt_file_size(&fi);
224         file->mtime = nxt_file_mtime(&fi);
225 
226     } else if (nxt_is_dir(&fi)) {
227         file->type = NXT_FILE_DIRECTORY;
228         file->size = nxt_file_size(&fi);
229         file->mtime = nxt_file_mtime(&fi);
230     }
231 
232     return NXT_OK;
233 }
234 
235 
236 static nxt_int_t
237 nxt_job_file_mmap(nxt_job_file_t *jbf, size_t size)
238 {
239     u_char             *p, *end;
240     static nxt_uint_t  n;
241 
242     p = nxt_mem_map(NULL, &jbf->buffer->mmap, size, NXT_MEM_MAP_READ,
243                     (NXT_MEM_MAP_FILE | NXT_MEM_MAP_PREFAULT),
244                     jbf->file.fd, jbf->offset);
245 
246     if (nxt_fast_path(p != NXT_MEM_MAP_FAILED)) {
247 
248         end = p + size;
249 
250         jbf->buffer->mem.pos = p;
251         jbf->buffer->mem.free = end;
252         jbf->buffer->mem.start = p;
253         jbf->buffer->mem.end = end;
254         jbf->buffer->file_end += size;
255         jbf->offset += size;
256 
257         /*
258          * The mapped pages should be already preloaded in the kernel page
259          * cache by nxt_file_read_ahead().  Touching them should wire the pages
260          * in user land memory if mmap() did not do this.  Adding to the static
261          * variable "n" disables the loop elimination during optimization.
262          */
263         n += *p;
264 
265         for (p = nxt_align_ptr(p, nxt_pagesize); p < end; p += nxt_pagesize) {
266             n += *p;
267         }
268 
269         return NXT_OK;
270     }
271 
272     return NXT_ERROR;
273 }
274 
275 
276 static nxt_int_t
277 nxt_job_file_read_data(nxt_job_file_t *jbf, size_t size)
278 {
279     ssize_t  n;
280 
281     n = nxt_file_read(&jbf->file, jbf->buffer->mem.pos, size, jbf->offset);
282 
283     if (nxt_fast_path(n > 0)) {
284 
285         jbf->buffer->mem.free += n;
286         jbf->offset += n;
287 
288         if (nxt_buf_is_file(jbf->buffer)) {
289             jbf->buffer->file_end += n;
290         }
291 
292         return NXT_OK;
293     }
294 
295     return NXT_ERROR;
296 }
297 
298 
299 static nxt_int_t
300 nxt_job_file_read_required(nxt_job_file_t *jbf)
301 {
302     return NXT_OK;
303 }
304