PATH_MAX
通常是一个固定的常量(针对posix系统),它指定了您在系统调用中传递给内核的路径参数的最大长度(例如open(2)
系统调用)
但是,文件可以拥有的最大路径长度不受此常量的影响,您可以使用此简单的shell脚本进行测试:
$ i=0
> while [ "$i" -lt 2000 ]
> do mkdir "$i"
> cd "$i"
> i=`expr "$i" + 1`
> done
$ pwd
/home/lcu/0/1/2/3/4/5/6/7/8/9/[intermediate output not shown...]/1996/1997/1998/1999
Now, if you try:
$ cd `pwd`
-bash: cd: /home/lcu/0/1/2/3/4/5/6/7/8/9/[intermediate output not shown...]/1996/1997/1998/1999: File name too long
显示出问题在于bash尝试执行chdir(2)系统调用。(下面有一个小程序,展示了如何打开文件名比PATH_MAX常量更长的文件)
你将进入一个目录,它的名称中包含的字符比PATH_MAX常量要多得多。这并不意味着您不能从根目录访问该文件,但您必须这样做:
更改当前目录(如脚本所示)
使用openat(2)和friends来使用一个更接近的打开目录作为起点来访问它。文件路径元素的绝对数量仅受文件系统中inode的数量和总容量的限制。
编辑
当只有#include 并且使用FILENAME_MAX就足够大以容纳任何文件路径时?
唯一支持在 POSIX 操作系统中打开任意文件长度并同时具有可移植性的方法是将路径分成足够短的路径块,并使用 n-1 个 chdir(2) 到达您的文件实际位置。然后,使用最后一个块调用 open(2) 系统调用,如果您的系统支持,则使用 fchdir(2) 系统调用返回到您所在的目录。 如果您有 openat(2) 系统调用,则可以使用 opendir() 调用打开目录(因为您只使用它来打开下一个目录),并关闭前一个目录,以便足够接近最终目录,以便使用 openat(2) 系统调用打开它。
以下是一个(正在进行中的)myfopen() 调用,尝试从 中的 PATH_MAX 限制中打开一个长于此限制的文件:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "myfopen.h"
FILE *my_fopen(const char *filename, const char *mode)
{
if (strlen(filename) > PATH_MAX) {
char *work_name = strdup(filename);
if (!work_name) {
return NULL;
}
char *to_free = work_name;
int dir_fd = -1;
while(strlen(work_name) >= PATH_MAX) {
char *p = work_name + PATH_MAX - 1;
while(*p != '/') p--;
*p++ = '\0';
if (dir_fd < 0) {
dir_fd = open(work_name, 0);
if (dir_fd < 0) {
ERR("open: %s: %s\n", work_name,
strerror(errno));
free(to_free);
return NULL;
}
} else {
int aux_fd = openat(dir_fd, work_name, 0);
close(dir_fd);
if (aux_fd < 0) {
ERR("openat: %s: %s\n", work_name,
strerror(errno));
free(to_free);
return NULL;
}
dir_fd = aux_fd;
}
work_name = p;
}
int fd = openat(
dir_fd,
work_name,
O_RDONLY);
close(dir_fd);
if (fd < 0) {
fprintf(stderr, "openat: %s: %s\n",
work_name, strerror(errno));
free(to_free);
return NULL;
}
free(to_free);
return fdopen(fd, mode);
}
return fopen(filename, mode);
}
你需要使用适当的位掩码集来传递给最终的openat()
调用,以遵守fopen()
与open()
调用指定打开模式的不同方式。
最大文件名长度的基本问题(为什么内核设计者不支持无界缓冲区来容纳文件的完整名称)是基于安全的。如果允许用户将一个20GB长的文件名传递到内核空间中,可能会耗尽内核内存空间,这是不允许的(这应该是系统中的一种严重漏洞,因为恶意用户可以通过传递非常长的文件名来阻塞整个内核)。
在正常情况下,我从未处理过超过1024字节的文件,除了演示这个特定的问题,所以我认为你应该接受这个限制,并努力使用短文件名(短于1024是一个很好的限制)。
另一方面,你在这里提到了许多常量:
FILENAME_MAX
被stdio系统使用,但它的值只能与stdio例程一起使用。它的文档说明如下:
这个宏常量扩展为一个整数表达式,对应于数组char元素所需的大小,以容纳库允许的最长文件名字符串。或者,如果库没有这样的限制,则设置为用于保存文件名的字符数组的建议大小。这意味着这是一个安全的长度,您可以将其与
fopen(3)
配合使用并传递一个有效的文件名。
PATH_MAX
是您可以在系统上使用的安全值。如果尝试打开文件(使用
open(2)
系统调用)或擦除(
unlink(2)
),重命名等操作,如果文件名超过此长度,则可能会遇到问题。
文件路径长度的限制来自系统调用子系统的限制,而不是操作涉及的文件系统,因此通常该限制适用于系统中的所有文件,而不仅仅是特定已挂载文件系统的文件(这些文件系统可以进一步受到限制,也可能不受限制)。
在我看来,这些限制非常合理,使用已发布的值对于大多数情况来说都是可以的。而且操作系统中没有任何工具能够打开路径超过
PATH_MAX
的文件。