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