xref: /unit/src/nxt_file.c (revision 2425:a94d0c252c09)
1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) NGINX, Inc.
5  */
6 
7 #include <nxt_main.h>
8 
9 
10 nxt_int_t
nxt_file_open(nxt_task_t * task,nxt_file_t * file,nxt_uint_t mode,nxt_uint_t create,nxt_file_access_t access)11 nxt_file_open(nxt_task_t *task, nxt_file_t *file, nxt_uint_t mode,
12     nxt_uint_t create, nxt_file_access_t access)
13 {
14 #ifdef __CYGWIN__
15     mode |= O_BINARY;
16 #endif
17 
18     /* O_NONBLOCK is to prevent blocking on FIFOs, special devices, etc. */
19     mode |= (O_NONBLOCK | create);
20 
21     file->fd = open((char *) file->name, mode, access);
22 
23     file->error = (file->fd == -1) ? nxt_errno : 0;
24 
25 #if (NXT_DEBUG)
26     nxt_thread_time_update(task->thread);
27 #endif
28 
29     nxt_debug(task, "open(\"%FN\", 0x%uXi, 0x%uXi): %FD err:%d",
30               file->name, mode, access, file->fd, file->error);
31 
32     if (file->fd != -1) {
33         return NXT_OK;
34     }
35 
36     if (file->log_level != 0) {
37         nxt_log(task, file->log_level, "open(\"%FN\") failed %E",
38                 file->name, file->error);
39     }
40 
41     return NXT_ERROR;
42 }
43 
44 
45 #if (NXT_HAVE_OPENAT2)
46 
47 nxt_int_t
nxt_file_openat2(nxt_task_t * task,nxt_file_t * file,nxt_uint_t mode,nxt_uint_t create,nxt_file_access_t access,nxt_fd_t dfd,nxt_uint_t resolve)48 nxt_file_openat2(nxt_task_t *task, nxt_file_t *file, nxt_uint_t mode,
49     nxt_uint_t create, nxt_file_access_t access, nxt_fd_t dfd,
50     nxt_uint_t resolve)
51 {
52     struct open_how  how;
53 
54     nxt_memzero(&how, sizeof(how));
55 
56     /* O_NONBLOCK is to prevent blocking on FIFOs, special devices, etc. */
57     mode |= (O_NONBLOCK | create);
58 
59     how.flags = mode;
60     how.mode = access;
61     how.resolve = resolve;
62 
63     file->fd = syscall(SYS_openat2, dfd, file->name, &how, sizeof(how));
64 
65     file->error = (file->fd == -1) ? nxt_errno : 0;
66 
67 #if (NXT_DEBUG)
68     nxt_thread_time_update(task->thread);
69 #endif
70 
71     nxt_debug(task, "openat2(%FD, \"%FN\"): %FD err:%d", dfd, file->name,
72               file->fd, file->error);
73 
74     if (file->fd != -1) {
75         return NXT_OK;
76     }
77 
78     if (file->log_level != 0) {
79         nxt_log(task, file->log_level, "openat2(%FD, \"%FN\") failed %E", dfd,
80                 file->name, file->error);
81     }
82 
83     return NXT_ERROR;
84 }
85 
86 #endif
87 
88 
89 void
nxt_file_close(nxt_task_t * task,nxt_file_t * file)90 nxt_file_close(nxt_task_t *task, nxt_file_t *file)
91 {
92     nxt_debug(task, "close(%FD)", file->fd);
93 
94     if (close(file->fd) != 0) {
95         nxt_alert(task, "close(%FD, \"%FN\") failed %E",
96                   file->fd, file->name, nxt_errno);
97     }
98 }
99 
100 
101 ssize_t
nxt_file_write(nxt_file_t * file,const u_char * buf,size_t size,nxt_off_t offset)102 nxt_file_write(nxt_file_t *file, const u_char *buf, size_t size,
103     nxt_off_t offset)
104 {
105     ssize_t  n;
106 
107     nxt_thread_debug(thr);
108 
109     n = pwrite(file->fd, buf, size, offset);
110 
111     file->error = (n < 0) ? nxt_errno : 0;
112 
113     nxt_thread_time_debug_update(thr);
114 
115     nxt_log_debug(thr->log, "pwrite(%FD, %p, %uz, %O): %z",
116                   file->fd, buf, size, offset, n);
117 
118     if (nxt_fast_path(n >= 0)) {
119         return n;
120     }
121 
122     nxt_thread_log_alert("pwrite(%FD, \"%FN\", %p, %uz, %O) failed %E",
123                          file->fd, file->name, buf, size,
124                          offset, file->error);
125 
126     return NXT_ERROR;
127 }
128 
129 
130 ssize_t
nxt_file_read(nxt_file_t * file,u_char * buf,size_t size,nxt_off_t offset)131 nxt_file_read(nxt_file_t *file, u_char *buf, size_t size, nxt_off_t offset)
132 {
133     ssize_t  n;
134 
135     nxt_thread_debug(thr);
136 
137     n = pread(file->fd, buf, size, offset);
138 
139     file->error = (n <= 0) ? nxt_errno : 0;
140 
141     nxt_thread_time_debug_update(thr);
142 
143     nxt_log_debug(thr->log, "pread(%FD, %p, %uz, %O): %z",
144                   file->fd, buf, size, offset, n);
145 
146     if (nxt_fast_path(n >= 0)) {
147         return n;
148     }
149 
150     nxt_thread_log_alert("pread(%FD, \"%FN\", %p, %uz, %O) failed %E",
151                          file->fd, file->name, buf, size,
152                          offset, file->error);
153 
154     return NXT_ERROR;
155 }
156 
157 
158 #if (NXT_HAVE_READAHEAD)
159 
160 /* FreeBSD 8.0 fcntl(F_READAHEAD, size) enables read ahead up to the size. */
161 
162 void
nxt_file_read_ahead(nxt_file_t * file,nxt_off_t offset,size_t size)163 nxt_file_read_ahead(nxt_file_t *file, nxt_off_t offset, size_t size)
164 {
165     int     ret;
166     u_char  buf;
167 
168     ret = fcntl(file->fd, F_READAHEAD, (int) size);
169 
170     nxt_thread_log_debug("fcntl(%FD, F_READAHEAD, %uz): %d",
171                          file->fd, size, ret);
172 
173     if (nxt_fast_path(ret != -1)) {
174         (void) nxt_file_read(file, &buf, 1, offset);
175         return;
176     }
177 
178     nxt_thread_log_alert("fcntl(%FD, \"%FN\", F_READAHEAD, %uz) failed %E",
179                          file->fd, file->name, size, nxt_errno);
180 }
181 
182 #elif (NXT_HAVE_POSIX_FADVISE)
183 
184 /*
185  * POSIX_FADV_SEQUENTIAL
186  *   Linux doubles the default readahead window size of a backing device
187  *   which is usually 128K.
188  *
189  *   FreeBSD does nothing.
190  *
191  * POSIX_FADV_WILLNEED
192  *   Linux preloads synchronously up to 2M of specified file region in
193  *   the kernel page cache.  Linux-specific readahead(2) syscall does
194  *   the same.  Both operations are blocking despite posix_fadvise(2)
195  *   claims the opposite.
196  *
197  *   FreeBSD does nothing.
198  */
199 
200 void
nxt_file_read_ahead(nxt_file_t * file,nxt_off_t offset,size_t size)201 nxt_file_read_ahead(nxt_file_t *file, nxt_off_t offset, size_t size)
202 {
203     nxt_err_t  err;
204 
205     err = posix_fadvise(file->fd, offset, size, POSIX_FADV_WILLNEED);
206 
207     nxt_thread_log_debug("posix_fadvise(%FD, \"%FN\", %O, %uz, %d): %d",
208                          file->fd, file->name, offset, size,
209                          POSIX_FADV_WILLNEED, err);
210 
211     if (nxt_fast_path(err == 0)) {
212         return;
213     }
214 
215     nxt_thread_log_alert("posix_fadvise(%FD, \"%FN\", %O, %uz, %d) failed %E",
216                          file->fd, file->name, offset, size,
217                          POSIX_FADV_WILLNEED, err);
218 }
219 
220 #elif (NXT_HAVE_RDAHEAD)
221 
222 /* MacOSX fcntl(F_RDAHEAD). */
223 
224 void
nxt_file_read_ahead(nxt_file_t * file,nxt_off_t offset,size_t size)225 nxt_file_read_ahead(nxt_file_t *file, nxt_off_t offset, size_t size)
226 {
227     int     ret;
228     u_char  buf;
229 
230     ret = fcntl(file->fd, F_RDAHEAD, 1);
231 
232     nxt_thread_log_debug("fcntl(%FD, F_RDAHEAD, 1): %d", file->fd, ret);
233 
234     if (nxt_fast_path(ret != -1)) {
235         (void) nxt_file_read(file, &buf, 1, offset);
236         return;
237     }
238 
239     nxt_thread_log_alert("fcntl(%FD, \"%FN\", F_RDAHEAD, 1) failed %E",
240                          file->fd, file->name, nxt_errno);
241 }
242 
243 #else
244 
245 void
nxt_file_read_ahead(nxt_file_t * file,nxt_off_t offset,size_t size)246 nxt_file_read_ahead(nxt_file_t *file, nxt_off_t offset, size_t size)
247 {
248     u_char  buf;
249 
250     (void) nxt_file_read(file, &buf, 1, offset);
251 }
252 
253 #endif
254 
255 
256 nxt_int_t
nxt_file_info(nxt_file_t * file,nxt_file_info_t * fi)257 nxt_file_info(nxt_file_t *file, nxt_file_info_t *fi)
258 {
259     int  n;
260 
261     if (file->fd == NXT_FILE_INVALID) {
262         n = stat((char *) file->name, fi);
263 
264         file->error = (n != 0) ? nxt_errno : 0;
265 
266         nxt_thread_log_debug("stat(\"%FN)\": %d", file->name, n);
267 
268         if (n == 0) {
269             return NXT_OK;
270         }
271 
272         if (file->log_level != 0) {
273             nxt_thread_log_error(file->log_level, "stat(\"%FN\") failed %E",
274                                  file->name, file->error);
275         }
276 
277         return NXT_ERROR;
278 
279     } else {
280         n = fstat(file->fd, fi);
281 
282         file->error = (n != 0) ? nxt_errno : 0;
283 
284         nxt_thread_log_debug("fstat(%FD): %d", file->fd, n);
285 
286         if (n == 0) {
287             return NXT_OK;
288         }
289 
290         /* Use NXT_LOG_ALERT because fstat() error on open file is strange. */
291 
292         nxt_thread_log_alert("fstat(%FD, \"%FN\") failed %E",
293                              file->fd, file->name, file->error);
294 
295         return NXT_ERROR;
296     }
297 }
298 
299 
300 nxt_int_t
nxt_file_delete(nxt_file_name_t * name)301 nxt_file_delete(nxt_file_name_t *name)
302 {
303     nxt_thread_log_debug("unlink(\"%FN\")", name);
304 
305     if (nxt_fast_path(unlink((char *) name) == 0)) {
306         return NXT_OK;
307     }
308 
309     nxt_thread_log_alert("unlink(\"%FN\") failed %E", name, nxt_errno);
310 
311     return NXT_ERROR;
312 }
313 
314 
315 nxt_int_t
nxt_file_set_access(nxt_file_name_t * name,nxt_file_access_t access)316 nxt_file_set_access(nxt_file_name_t *name, nxt_file_access_t access)
317 {
318     if (nxt_fast_path(chmod((char *) name, access) == 0)) {
319         return NXT_OK;
320     }
321 
322     nxt_thread_log_alert("chmod(\"%FN\") failed %E", name, nxt_errno);
323 
324     return NXT_ERROR;
325 }
326 
327 
328 nxt_int_t
nxt_file_rename(nxt_file_name_t * old_name,nxt_file_name_t * new_name)329 nxt_file_rename(nxt_file_name_t *old_name, nxt_file_name_t *new_name)
330 {
331     int  ret;
332 
333     nxt_thread_log_debug("rename(\"%FN\", \"%FN\")", old_name, new_name);
334 
335     ret = rename((char *) old_name, (char *) new_name);
336     if (nxt_fast_path(ret == 0)) {
337         return NXT_OK;
338     }
339 
340     nxt_thread_log_alert("rename(\"%FN\", \"%FN\") failed %E",
341                          old_name, new_name, nxt_errno);
342 
343     return NXT_ERROR;
344 }
345 
346 
347 /*
348  * ioctl(FIONBIO) sets a non-blocking mode using one syscall,
349  * thereas fcntl(F_SETFL, O_NONBLOCK) needs to learn the current state
350  * using fcntl(F_GETFL).
351  *
352  * ioctl() and fcntl() are syscalls at least in Linux 2.2, FreeBSD 2.x,
353  * and Solaris 7.
354  *
355  * Linux 2.4 uses BKL for ioctl() and fcntl(F_SETFL).
356  * Linux 2.6 does not use BKL.
357  */
358 
359 #if (NXT_HAVE_FIONBIO)
360 
361 nxt_int_t
nxt_fd_nonblocking(nxt_task_t * task,nxt_fd_t fd)362 nxt_fd_nonblocking(nxt_task_t *task, nxt_fd_t fd)
363 {
364     int  nb;
365 
366     nb = 1;
367 
368     if (nxt_fast_path(ioctl(fd, FIONBIO, &nb) != -1)) {
369         return NXT_OK;
370     }
371 
372     nxt_alert(task, "ioctl(%d, FIONBIO) failed %E", fd, nxt_errno);
373 
374     return NXT_ERROR;
375 
376 }
377 
378 
379 nxt_int_t
nxt_fd_blocking(nxt_task_t * task,nxt_fd_t fd)380 nxt_fd_blocking(nxt_task_t *task, nxt_fd_t fd)
381 {
382     int  nb;
383 
384     nb = 0;
385 
386     if (nxt_fast_path(ioctl(fd, FIONBIO, &nb) != -1)) {
387         return NXT_OK;
388     }
389 
390     nxt_alert(task, "ioctl(%d, !FIONBIO) failed %E", fd, nxt_errno);
391 
392     return NXT_ERROR;
393 }
394 
395 #else /* !(NXT_HAVE_FIONBIO) */
396 
397 nxt_int_t
nxt_fd_nonblocking(nxt_task_t * task,nxt_fd_t fd)398 nxt_fd_nonblocking(nxt_task_t *task, nxt_fd_t fd)
399 {
400     int  flags;
401 
402     flags = fcntl(fd, F_GETFL);
403 
404     if (nxt_slow_path(flags == -1)) {
405         nxt_alert(task, "fcntl(%d, F_GETFL) failed %E", fd, nxt_errno);
406         return NXT_ERROR;
407     }
408 
409     flags |= O_NONBLOCK;
410 
411     if (nxt_slow_path(fcntl(fd, F_SETFL, flags) == -1)) {
412         nxt_alert(task, "fcntl(%d, F_SETFL, O_NONBLOCK) failed %E",
413                   fd, nxt_errno);
414         return NXT_ERROR;
415     }
416 
417     return NXT_OK;
418 }
419 
420 
421 nxt_int_t
nxt_fd_blocking(nxt_task_t * task,nxt_fd_t fd)422 nxt_fd_blocking(nxt_task_t *task, nxt_fd_t fd)
423 {
424     int  flags;
425 
426     flags = fcntl(fd, F_GETFL);
427 
428     if (nxt_slow_path(flags == -1)) {
429         nxt_alert(task, "fcntl(%d, F_GETFL) failed %E", fd, nxt_errno);
430         return NXT_ERROR;
431     }
432 
433     flags &= O_NONBLOCK;
434 
435     if (nxt_slow_path(fcntl(fd, F_SETFL, flags) == -1)) {
436         nxt_alert(task, "fcntl(%d, F_SETFL, !O_NONBLOCK) failed %E",
437                   fd, nxt_errno);
438         return NXT_ERROR;
439     }
440 
441     return NXT_OK;
442 }
443 
444 #endif /* NXT_HAVE_FIONBIO */
445 
446 
447 ssize_t
nxt_fd_write(nxt_fd_t fd,u_char * buf,size_t size)448 nxt_fd_write(nxt_fd_t fd, u_char *buf, size_t size)
449 {
450     ssize_t    n;
451     nxt_err_t  err;
452 
453     n = write(fd, buf, size);
454 
455     err = (n == -1) ? nxt_errno : 0;
456 
457     nxt_thread_log_debug("write(%FD, %p, %uz): %z", fd, buf, size, n);
458 
459     if (nxt_slow_path(n <= 0)) {
460         nxt_thread_log_alert("write(%FD) failed %E", fd, err);
461     }
462 
463     return n;
464 }
465 
466 
467 ssize_t
nxt_fd_read(nxt_fd_t fd,u_char * buf,size_t size)468 nxt_fd_read(nxt_fd_t fd, u_char *buf, size_t size)
469 {
470     ssize_t    n;
471     nxt_err_t  err;
472 
473     n = read(fd, buf, size);
474 
475     err = (n == -1) ? nxt_errno : 0;
476 
477     nxt_thread_log_debug("read(%FD, %p, %uz): %z", fd, buf, size, n);
478 
479     if (nxt_slow_path(n <= 0)) {
480 
481         if (err == NXT_EAGAIN) {
482             return 0;
483         }
484 
485         nxt_thread_log_alert("read(%FD) failed %E", fd, err);
486     }
487 
488     return n;
489 }
490 
491 
492 void
nxt_fd_close(nxt_fd_t fd)493 nxt_fd_close(nxt_fd_t fd)
494 {
495     nxt_thread_log_debug("close(%FD)", fd);
496 
497     if (nxt_slow_path(close(fd) != 0)) {
498         nxt_thread_log_alert("close(%FD) failed %E", fd, nxt_errno);
499     }
500 }
501 
502 
503 FILE *
nxt_file_fopen(nxt_task_t * task,const char * pathname,const char * mode)504 nxt_file_fopen(nxt_task_t *task, const char *pathname, const char *mode)
505 {
506     int   err;
507     FILE  *fp;
508 
509 #if (NXT_DEBUG)
510     nxt_thread_time_update(task->thread);
511 #endif
512 
513     fp = fopen(pathname, mode);
514     err = (fp == NULL) ? nxt_errno : 0;
515 
516     nxt_debug(task, "fopen(\"%s\", \"%s\"): fp:%p err:%d", pathname, mode, fp,
517               err);
518 
519     if (nxt_fast_path(fp != NULL)) {
520         return fp;
521     }
522 
523     nxt_alert(task, "fopen(\"%s\") failed %E", pathname, err);
524 
525     return NULL;
526 }
527 
528 
529 void
nxt_file_fclose(nxt_task_t * task,FILE * fp)530 nxt_file_fclose(nxt_task_t *task, FILE *fp)
531 {
532     nxt_debug(task, "fclose(%p)", fp);
533 
534     if (nxt_slow_path(fclose(fp) == -1)) {
535         nxt_alert(task, "fclose() failed %E", nxt_errno);
536     }
537 }
538 
539 
540 /*
541  * nxt_file_redirect() redirects the file to the fd descriptor.
542  * Then the fd descriptor is closed.
543  */
544 
545 nxt_int_t
nxt_file_redirect(nxt_file_t * file,nxt_fd_t fd)546 nxt_file_redirect(nxt_file_t *file, nxt_fd_t fd)
547 {
548     nxt_thread_log_debug("dup2(%FD, %FD, \"%FN\")", fd, file->fd, file->name);
549 
550     if (dup2(fd, file->fd) == -1) {
551         nxt_thread_log_alert("dup2(%FD, %FD, \"%FN\") failed %E",
552                              fd, file->fd, file->name, nxt_errno);
553         return NXT_ERROR;
554     }
555 
556     if (close(fd) != 0) {
557         nxt_thread_log_alert("close(%FD, \"%FN\") failed %E",
558                              fd, file->name, nxt_errno);
559         return NXT_ERROR;
560     }
561 
562     return NXT_OK;
563 }
564 
565 
566 /* nxt_file_stdout() redirects the stdout descriptor to the file. */
567 
568 nxt_int_t
nxt_file_stdout(nxt_file_t * file)569 nxt_file_stdout(nxt_file_t *file)
570 {
571     nxt_thread_log_debug("dup2(%FD, %FD, \"%FN\")",
572                          file->fd, STDOUT_FILENO, file->name);
573 
574     if (dup2(file->fd, STDOUT_FILENO) != -1) {
575         return NXT_OK;
576     }
577 
578     nxt_thread_log_alert("dup2(%FD, %FD, \"%FN\") failed %E",
579                          file->fd, STDOUT_FILENO, file->name, nxt_errno);
580 
581     return NXT_ERROR;
582 }
583 
584 
585 /* nxt_file_stderr() redirects the stderr descriptor to the file. */
586 
587 nxt_int_t
nxt_file_stderr(nxt_file_t * file)588 nxt_file_stderr(nxt_file_t *file)
589 {
590     nxt_thread_log_debug("dup2(%FD, %FD, \"%FN\")",
591                          file->fd, STDERR_FILENO, file->name);
592 
593     if (dup2(file->fd, STDERR_FILENO) != -1) {
594         return NXT_OK;
595     }
596 
597     nxt_thread_log_alert("dup2(%FD, %FD, \"%FN\") failed %E",
598                          file->fd, STDERR_FILENO, file->name, nxt_errno);
599 
600     return NXT_ERROR;
601 }
602 
603 
604 nxt_int_t
nxt_stderr_start(void)605 nxt_stderr_start(void)
606 {
607     int  flags, fd;
608 
609     flags = fcntl(nxt_stderr, F_GETFL);
610 
611     if (flags != -1) {
612         /*
613          * If the stderr output of a multithreaded application is
614          * redirected to a file:
615          *    Linux, Solaris and MacOSX do not write atomically to the output;
616          *    MacOSX besides adds zeroes to the output.
617          * O_APPEND fixes this.
618          */
619         (void) fcntl(nxt_stderr, F_SETFL, flags | O_APPEND);
620 
621     } else {
622         /*
623          * The stderr descriptor is closed before application start.
624          * Reserve the stderr descriptor for future use.  Errors are
625          * ignored because anyway they could be written nowhere.
626          */
627         fd = open("/dev/null", O_WRONLY | O_APPEND);
628 
629         if (fd != -1) {
630             (void) dup2(fd, nxt_stderr);
631 
632             if (fd != nxt_stderr) {
633                 (void) close(fd);
634             }
635         }
636     }
637 
638     return flags;
639 }
640 
641 
642 nxt_int_t
nxt_pipe_create(nxt_task_t * task,nxt_fd_t * pp,nxt_bool_t nbread,nxt_bool_t nbwrite)643 nxt_pipe_create(nxt_task_t *task, nxt_fd_t *pp, nxt_bool_t nbread,
644     nxt_bool_t nbwrite)
645 {
646     if (pipe(pp) != 0) {
647         nxt_alert(task, "pipe() failed %E", nxt_errno);
648 
649         return NXT_ERROR;
650     }
651 
652     nxt_debug(task, "pipe(): %FD:%FD", pp[0], pp[1]);
653 
654     if (nbread) {
655         if (nxt_fd_nonblocking(task, pp[0]) != NXT_OK) {
656             return NXT_ERROR;
657         }
658     }
659 
660     if (nbwrite) {
661         if (nxt_fd_nonblocking(task, pp[1]) != NXT_OK) {
662             return NXT_ERROR;
663         }
664     }
665 
666     return NXT_OK;
667 }
668 
669 
670 void
nxt_pipe_close(nxt_task_t * task,nxt_fd_t * pp)671 nxt_pipe_close(nxt_task_t *task, nxt_fd_t *pp)
672 {
673     nxt_debug(task, "pipe close(%FD:%FD)", pp[0], pp[1]);
674 
675     if (close(pp[0]) != 0) {
676         nxt_alert(task, "pipe close(%FD) failed %E", pp[0], nxt_errno);
677     }
678 
679     if (close(pp[1]) != 0) {
680         nxt_alert(task, "pipe close(%FD) failed %E", pp[1], nxt_errno);
681     }
682 }
683 
684 
685 size_t
nxt_dir_current(char * buf,size_t len)686 nxt_dir_current(char *buf, size_t len)
687 {
688     if (nxt_fast_path(getcwd(buf, len) != NULL)) {
689         return nxt_strlen(buf);
690     }
691 
692     nxt_thread_log_alert("getcwd(%uz) failed %E", len, nxt_errno);
693 
694     return 0;
695 }
696