Mercurial > hg > nginx-quic
view src/os/unix/ngx_files.c @ 5356:acd51b0f6fd4
Disable symlinks: use O_PATH to open path components.
It was introduced in Linux 2.6.39, glibc 2.14 and allows to obtain
file descriptors without actually opening files. Thus made it possible
to traverse path with openat() syscalls without the need to have read
permissions for path components. It is effectively emulates O_SEARCH
which is missing on Linux.
O_PATH is used in combination with O_RDONLY. The last one is ignored
if O_PATH is used, but it allows nginx to not fail when it was built on
modern system (i.e. glibc 2.14+) and run with a kernel older than 2.6.39.
Then O_PATH is unknown to the kernel and ignored, while O_RDONLY is used.
Sadly, fstat() is not working with O_PATH descriptors till Linux 3.6.
As a workaround we fallback to fstatat() with the AT_EMPTY_PATH flag
that was introduced at the same time as O_PATH.
author | Valentin Bartenev <vbart@nginx.com> |
---|---|
date | Mon, 02 Sep 2013 08:07:59 +0400 |
parents | 1e2d5d3f9f6b |
children | 1fdba317ee6d |
line wrap: on
line source
/* * Copyright (C) Igor Sysoev * Copyright (C) Nginx, Inc. */ #include <ngx_config.h> #include <ngx_core.h> #if (NGX_HAVE_FILE_AIO) ngx_uint_t ngx_file_aio = 1; #endif ssize_t ngx_read_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset) { ssize_t n; ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0, "read: %d, %p, %uz, %O", file->fd, buf, size, offset); #if (NGX_HAVE_PREAD) n = pread(file->fd, buf, size, offset); if (n == -1) { ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, "pread() \"%s\" failed", file->name.data); return NGX_ERROR; } #else if (file->sys_offset != offset) { if (lseek(file->fd, offset, SEEK_SET) == -1) { ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, "lseek() \"%s\" failed", file->name.data); return NGX_ERROR; } file->sys_offset = offset; } n = read(file->fd, buf, size); if (n == -1) { ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, "read() \"%s\" failed", file->name.data); return NGX_ERROR; } file->sys_offset += n; #endif file->offset += n; return n; } ssize_t ngx_write_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset) { ssize_t n, written; ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0, "write: %d, %p, %uz, %O", file->fd, buf, size, offset); written = 0; #if (NGX_HAVE_PWRITE) for ( ;; ) { n = pwrite(file->fd, buf + written, size, offset); if (n == -1) { ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, "pwrite() \"%s\" failed", file->name.data); return NGX_ERROR; } file->offset += n; written += n; if ((size_t) n == size) { return written; } offset += n; size -= n; } #else if (file->sys_offset != offset) { if (lseek(file->fd, offset, SEEK_SET) == -1) { ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, "lseek() \"%s\" failed", file->name.data); return NGX_ERROR; } file->sys_offset = offset; } for ( ;; ) { n = write(file->fd, buf + written, size); if (n == -1) { ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, "write() \"%s\" failed", file->name.data); return NGX_ERROR; } file->offset += n; written += n; if ((size_t) n == size) { return written; } size -= n; } #endif } ngx_fd_t ngx_open_tempfile(u_char *name, ngx_uint_t persistent, ngx_uint_t access) { ngx_fd_t fd; fd = open((const char *) name, O_CREAT|O_EXCL|O_RDWR, access ? access : 0600); if (fd != -1 && !persistent) { (void) unlink((const char *) name); } return fd; } #define NGX_IOVS 8 ssize_t ngx_write_chain_to_file(ngx_file_t *file, ngx_chain_t *cl, off_t offset, ngx_pool_t *pool) { u_char *prev; size_t size; ssize_t total, n; ngx_array_t vec; struct iovec *iov, iovs[NGX_IOVS]; /* use pwrite() if there is the only buf in a chain */ if (cl->next == NULL) { return ngx_write_file(file, cl->buf->pos, (size_t) (cl->buf->last - cl->buf->pos), offset); } total = 0; vec.elts = iovs; vec.size = sizeof(struct iovec); vec.nalloc = NGX_IOVS; vec.pool = pool; do { prev = NULL; iov = NULL; size = 0; vec.nelts = 0; /* create the iovec and coalesce the neighbouring bufs */ while (cl && vec.nelts < IOV_MAX) { if (prev == cl->buf->pos) { iov->iov_len += cl->buf->last - cl->buf->pos; } else { iov = ngx_array_push(&vec); if (iov == NULL) { return NGX_ERROR; } iov->iov_base = (void *) cl->buf->pos; iov->iov_len = cl->buf->last - cl->buf->pos; } size += cl->buf->last - cl->buf->pos; prev = cl->buf->last; cl = cl->next; } /* use pwrite() if there is the only iovec buffer */ if (vec.nelts == 1) { iov = vec.elts; n = ngx_write_file(file, (u_char *) iov[0].iov_base, iov[0].iov_len, offset); if (n == NGX_ERROR) { return n; } return total + n; } if (file->sys_offset != offset) { if (lseek(file->fd, offset, SEEK_SET) == -1) { ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, "lseek() \"%s\" failed", file->name.data); return NGX_ERROR; } file->sys_offset = offset; } n = writev(file->fd, vec.elts, vec.nelts); if (n == -1) { ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno, "writev() \"%s\" failed", file->name.data); return NGX_ERROR; } if ((size_t) n != size) { ngx_log_error(NGX_LOG_CRIT, file->log, 0, "writev() \"%s\" has written only %z of %uz", file->name.data, n, size); return NGX_ERROR; } ngx_log_debug2(NGX_LOG_DEBUG_CORE, file->log, 0, "writev: %d, %z", file->fd, n); file->sys_offset += n; file->offset += n; offset += n; total += n; } while (cl); return total; } ngx_int_t ngx_set_file_time(u_char *name, ngx_fd_t fd, time_t s) { struct timeval tv[2]; tv[0].tv_sec = ngx_time(); tv[0].tv_usec = 0; tv[1].tv_sec = s; tv[1].tv_usec = 0; if (utimes((char *) name, tv) != -1) { return NGX_OK; } return NGX_ERROR; } ngx_int_t ngx_create_file_mapping(ngx_file_mapping_t *fm) { fm->fd = ngx_open_file(fm->name, NGX_FILE_RDWR, NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); if (fm->fd == NGX_INVALID_FILE) { ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno, ngx_open_file_n " \"%s\" failed", fm->name); return NGX_ERROR; } if (ftruncate(fm->fd, fm->size) == -1) { ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno, "ftruncate() \"%s\" failed", fm->name); goto failed; } fm->addr = mmap(NULL, fm->size, PROT_READ|PROT_WRITE, MAP_SHARED, fm->fd, 0); if (fm->addr != MAP_FAILED) { return NGX_OK; } ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno, "mmap(%uz) \"%s\" failed", fm->size, fm->name); failed: if (ngx_close_file(fm->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, fm->log, ngx_errno, ngx_close_file_n " \"%s\" failed", fm->name); } return NGX_ERROR; } void ngx_close_file_mapping(ngx_file_mapping_t *fm) { if (munmap(fm->addr, fm->size) == -1) { ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno, "munmap(%uz) \"%s\" failed", fm->size, fm->name); } if (ngx_close_file(fm->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, fm->log, ngx_errno, ngx_close_file_n " \"%s\" failed", fm->name); } } ngx_int_t ngx_open_dir(ngx_str_t *name, ngx_dir_t *dir) { dir->dir = opendir((const char *) name->data); if (dir->dir == NULL) { return NGX_ERROR; } dir->valid_info = 0; return NGX_OK; } ngx_int_t ngx_read_dir(ngx_dir_t *dir) { dir->de = readdir(dir->dir); if (dir->de) { #if (NGX_HAVE_D_TYPE) dir->type = dir->de->d_type; #else dir->type = 0; #endif return NGX_OK; } return NGX_ERROR; } ngx_int_t ngx_open_glob(ngx_glob_t *gl) { int n; n = glob((char *) gl->pattern, 0, NULL, &gl->pglob); if (n == 0) { return NGX_OK; } #ifdef GLOB_NOMATCH if (n == GLOB_NOMATCH && gl->test) { return NGX_OK; } #endif return NGX_ERROR; } ngx_int_t ngx_read_glob(ngx_glob_t *gl, ngx_str_t *name) { size_t count; #ifdef GLOB_NOMATCH count = (size_t) gl->pglob.gl_pathc; #else count = (size_t) gl->pglob.gl_matchc; #endif if (gl->n < count) { name->len = (size_t) ngx_strlen(gl->pglob.gl_pathv[gl->n]); name->data = (u_char *) gl->pglob.gl_pathv[gl->n]; gl->n++; return NGX_OK; } return NGX_DONE; } void ngx_close_glob(ngx_glob_t *gl) { globfree(&gl->pglob); } ngx_err_t ngx_trylock_fd(ngx_fd_t fd) { struct flock fl; ngx_memzero(&fl, sizeof(struct flock)); fl.l_type = F_WRLCK; fl.l_whence = SEEK_SET; if (fcntl(fd, F_SETLK, &fl) == -1) { return ngx_errno; } return 0; } ngx_err_t ngx_lock_fd(ngx_fd_t fd) { struct flock fl; ngx_memzero(&fl, sizeof(struct flock)); fl.l_type = F_WRLCK; fl.l_whence = SEEK_SET; if (fcntl(fd, F_SETLKW, &fl) == -1) { return ngx_errno; } return 0; } ngx_err_t ngx_unlock_fd(ngx_fd_t fd) { struct flock fl; ngx_memzero(&fl, sizeof(struct flock)); fl.l_type = F_UNLCK; fl.l_whence = SEEK_SET; if (fcntl(fd, F_SETLK, &fl) == -1) { return ngx_errno; } return 0; } #if (NGX_HAVE_POSIX_FADVISE) && !(NGX_HAVE_F_READAHEAD) ngx_int_t ngx_read_ahead(ngx_fd_t fd, size_t n) { int err; err = posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL); if (err == 0) { return 0; } ngx_set_errno(err); return NGX_FILE_ERROR; } #endif #if (NGX_HAVE_O_DIRECT) ngx_int_t ngx_directio_on(ngx_fd_t fd) { int flags; flags = fcntl(fd, F_GETFL); if (flags == -1) { return NGX_FILE_ERROR; } return fcntl(fd, F_SETFL, flags | O_DIRECT); } ngx_int_t ngx_directio_off(ngx_fd_t fd) { int flags; flags = fcntl(fd, F_GETFL); if (flags == -1) { return NGX_FILE_ERROR; } return fcntl(fd, F_SETFL, flags & ~O_DIRECT); } #endif #if (NGX_HAVE_STATFS) size_t ngx_fs_bsize(u_char *name) { struct statfs fs; if (statfs((char *) name, &fs) == -1) { return 512; } if ((fs.f_bsize % 512) != 0) { return 512; } return (size_t) fs.f_bsize; } #elif (NGX_HAVE_STATVFS) size_t ngx_fs_bsize(u_char *name) { struct statvfs fs; if (statvfs((char *) name, &fs) == -1) { return 512; } if ((fs.f_frsize % 512) != 0) { return 512; } return (size_t) fs.f_frsize; } #else size_t ngx_fs_bsize(u_char *name) { return 512; } #endif