news 2026/6/17 6:24:30

ngx_open_file_wrapper

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ngx_open_file_wrapper

1 定义

ngx_open_file_wrapper 函数 定义在 ./nginx-1.24.0/src/core/ngx_open_file_cache.c
staticngx_fd_tngx_open_file_wrapper(ngx_str_t*name,ngx_open_file_info_t*of,ngx_int_tmode,ngx_int_tcreate,ngx_int_taccess,ngx_log_t*log){ngx_fd_tfd;#if!(NGX_HAVE_OPENAT)fd=ngx_open_file(name->data,mode,create,access);if(fd==NGX_INVALID_FILE){of->err=ngx_errno;of->failed=ngx_open_file_n;returnNGX_INVALID_FILE;}returnfd;#elseu_char*p,*cp,*end;ngx_fd_tat_fd;ngx_str_tat_name;if(of->disable_symlinks==NGX_DISABLE_SYMLINKS_OFF){fd=ngx_open_file(name->data,mode,create,access);if(fd==NGX_INVALID_FILE){of->err=ngx_errno;of->failed=ngx_open_file_n;returnNGX_INVALID_FILE;}returnfd;}p=name->data;end=p+name->len;at_name=*name;if(of->disable_symlinks_from){cp=p+of->disable_symlinks_from;*cp='\0';at_fd=ngx_open_file(p,NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,NGX_FILE_OPEN,0);*cp='/';if(at_fd==NGX_INVALID_FILE){of->err=ngx_errno;of->failed=ngx_open_file_n;returnNGX_INVALID_FILE;}at_name.len=of->disable_symlinks_from;p=cp+1;}elseif(*p=='/'){at_fd=ngx_open_file("/",NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,NGX_FILE_OPEN,0);if(at_fd==NGX_INVALID_FILE){of->err=ngx_errno;of->failed=ngx_openat_file_n;returnNGX_INVALID_FILE;}at_name.len=1;p++;}else{at_fd=NGX_AT_FDCWD;}for(;;){cp=ngx_strlchr(p,end,'/');if(cp==NULL){break;}if(cp==p){p++;continue;}*cp='\0';if(of->disable_symlinks==NGX_DISABLE_SYMLINKS_NOTOWNER){fd=ngx_openat_file_owner(at_fd,p,NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,NGX_FILE_OPEN,0,log);}else{fd=ngx_openat_file(at_fd,p,NGX_FILE_SEARCH|NGX_FILE_NONBLOCK|NGX_FILE_NOFOLLOW,NGX_FILE_OPEN,0);}*cp='/';if(fd==NGX_INVALID_FILE){of->err=ngx_errno;of->failed=ngx_openat_file_n;gotofailed;}if(at_fd!=NGX_AT_FDCWD&&ngx_close_file(at_fd)==NGX_FILE_ERROR){ngx_log_error(NGX_LOG_ALERT,log,ngx_errno,ngx_close_file_n" \"%V\" failed",&at_name);}p=cp+1;at_fd=fd;at_name.len=cp-at_name.data;}if(p==end){/* * If pathname ends with a trailing slash, assume the last path * component is a directory and reopen it with requested flags; * if not, fail with ENOTDIR as per POSIX. * * We cannot rely on O_DIRECTORY in the loop above to check * that the last path component is a directory because * O_DIRECTORY doesn't work on FreeBSD 8. Fortunately, by * reopening a directory, we don't depend on it at all. */fd=ngx_openat_file(at_fd,".",mode,create,access);gotodone;}if(of->disable_symlinks==NGX_DISABLE_SYMLINKS_NOTOWNER&&!(create&(NGX_FILE_CREATE_OR_OPEN|NGX_FILE_TRUNCATE))){fd=ngx_openat_file_owner(at_fd,p,mode,create,access,log);}else{fd=ngx_openat_file(at_fd,p,mode|NGX_FILE_NOFOLLOW,create,access);}done:if(fd==NGX_INVALID_FILE){of->err=ngx_errno;of->failed=ngx_openat_file_n;}failed:if(at_fd!=NGX_AT_FDCWD&&ngx_close_file(at_fd)==NGX_FILE_ERROR){ngx_log_error(NGX_LOG_ALERT,log,ngx_errno,ngx_close_file_n" \"%V\" failed",&at_name);}returnfd;#endif}
ngx_open_file_wrapper 函数是 Nginx 对文件打开操作的统一安全封装。 它根据 `disable_symlinks` 指令的配置,在不限制符号链接时直接调用 `open`; 在需要限制时,通过逐级解析路径、使用 `openat` 并设置 `O_NOFOLLOW` 等标志, 确保不会跟随不符合安全策略的符号链接,从而防止路径穿越攻击。 同时处理跨平台差异并提供统一的错误报告。

2 详解

1 函数签名

staticngx_fd_tngx_open_file_wrapper(ngx_str_t*name,ngx_open_file_info_t*of,ngx_int_tmode,ngx_int_tcreate,ngx_int_taccess,ngx_log_t*log)
返回值 成功返回有效 FD, 失败返回 NGX_INVALID_FILE
参数1 ngx_str_t *name 要打开的文件的完整路径
参数2 ngx_open_file_info_t *of 指向 ngx_open_file_info_t 结构体的指针, 承载输入控制参数和输出结果
参数3 ngx_int_t mode 表示打开文件的访问模式和标志
参数4 ngx_int_t create 当文件不存在时的创建行为,以及是否截断等
参数5 ngx_int_t access 在创建文件时使用的访问权限
参数6 ngx_log_t *log 日志对象

2 逻辑流程

1 不支持openat 2 支持openat 1 允许符号链接 2 限制符号链接

{ngx_fd_tfd;
声明文件描述符变量 fd, 用于临时存储打开操作的结果。

1 不支持 openat

#if!(NGX_HAVE_OPENAT)fd=ngx_open_file(name->data,mode,create,access);if(fd==NGX_INVALID_FILE){of->err=ngx_errno;of->failed=ngx_open_file_n;returnNGX_INVALID_FILE;}returnfd;

2 支持openat

#elseu_char*p,*cp,*end;ngx_fd_tat_fd;ngx_str_tat_name;
声明局部变量

if(of->disable_symlinks==NGX_DISABLE_SYMLINKS_OFF){fd=ngx_open_file(name->data,mode,create,access);if(fd==NGX_INVALID_FILE){of->err=ngx_errno;of->failed=ngx_open_file_n;returnNGX_INVALID_FILE;}returnfd;}
完全允许符号链接 当配置为不限制符号链接时,直接使用普通 open。 失败 记录错误 返回 无效文件描述符 成功 返回 文件描述符

限制符号链接
p=name->data;end=p+name->len;at_name=*name;if(of->disable_symlinks_from){cp=p+of->disable_symlinks_from;*cp='\0';at_fd=ngx_open_file(p,NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,NGX_FILE_OPEN,0);*cp='/';if(at_fd==NGX_INVALID_FILE){of->err=ngx_errno;of->failed=ngx_open_file_n;returnNGX_INVALID_FILE;}at_name.len=of->disable_symlinks_from;p=cp+1;}elseif(*p=='/'){at_fd=ngx_open_file("/",NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,NGX_FILE_OPEN,0);if(at_fd==NGX_INVALID_FILE){of->err=ngx_errno;of->failed=ngx_openat_file_n;returnNGX_INVALID_FILE;}at_name.len=1;p++;}else{at_fd=NGX_AT_FDCWD;}for(;;){cp=ngx_strlchr(p,end,'/');if(cp==NULL){break;}if(cp==p){p++;continue;}*cp='\0';if(of->disable_symlinks==NGX_DISABLE_SYMLINKS_NOTOWNER){fd=ngx_openat_file_owner(at_fd,p,NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,NGX_FILE_OPEN,0,log);}else{fd=ngx_openat_file(at_fd,p,NGX_FILE_SEARCH|NGX_FILE_NONBLOCK|NGX_FILE_NOFOLLOW,NGX_FILE_OPEN,0);}*cp='/';if(fd==NGX_INVALID_FILE){of->err=ngx_errno;of->failed=ngx_openat_file_n;gotofailed;}if(at_fd!=NGX_AT_FDCWD&&ngx_close_file(at_fd)==NGX_FILE_ERROR){ngx_log_error(NGX_LOG_ALERT,log,ngx_errno,ngx_close_file_n" \"%V\" failed",&at_name);}p=cp+1;at_fd=fd;at_name.len=cp-at_name.data;}if(p==end){/* * If pathname ends with a trailing slash, assume the last path * component is a directory and reopen it with requested flags; * if not, fail with ENOTDIR as per POSIX. * * We cannot rely on O_DIRECTORY in the loop above to check * that the last path component is a directory because * O_DIRECTORY doesn't work on FreeBSD 8. Fortunately, by * reopening a directory, we don't depend on it at all. */fd=ngx_openat_file(at_fd,".",mode,create,access);gotodone;}if(of->disable_symlinks==NGX_DISABLE_SYMLINKS_NOTOWNER&&!(create&(NGX_FILE_CREATE_OR_OPEN|NGX_FILE_TRUNCATE))){fd=ngx_openat_file_owner(at_fd,p,mode,create,access,log);}else{fd=ngx_openat_file(at_fd,p,mode|NGX_FILE_NOFOLLOW,create,access);}done:if(fd==NGX_INVALID_FILE){of->err=ngx_errno;of->failed=ngx_openat_file_n;}failed:if(at_fd!=NGX_AT_FDCWD&&ngx_close_file(at_fd)==NGX_FILE_ERROR){ngx_log_error(NGX_LOG_ALERT,log,ngx_errno,ngx_close_file_n" \"%V\" failed",&at_name);}returnfd;#endif}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/15 15:39:25

小说下载器终极指南:如何轻松构建你的私人数字图书馆

小说下载器终极指南:如何轻松构建你的私人数字图书馆 【免费下载链接】novel-downloader 一个可扩展的通用型小说下载器。 项目地址: https://gitcode.com/gh_mirrors/no/novel-downloader 你是否曾经遇到过这样的情况:网络不稳定时无法继续阅读心…

作者头像 李华
网站建设 2026/6/16 9:38:14

MCProtocolLib数据包处理指南:从握手到游戏状态的完整流程解析

MCProtocolLib数据包处理指南:从握手到游戏状态的完整流程解析 【免费下载链接】MCProtocolLib A library for communication with a Minecraft client/server. 项目地址: https://gitcode.com/gh_mirrors/mc/MCProtocolLib MCProtocolLib是一款专为Minecraf…

作者头像 李华