我没有问这个问题,因为它在我第一次发布时应该是措辞正确的。所以,我在这里编辑了一些。
下面有三个代码块,它们都读取目录的内容,并试图获取内容的统计信息。除了路径/文件名达到260个字符或更多字符外,这三种方法都有效。
在所有情况下,readdir()
都会毫无问题地返回目录内容,但stat()
会失败,并出现文件或目录不存在的错误。
第一个块将指向路径和文件名的指针传递给stat()
。第二个更改工作目录,然后只传递一个指向文件名的指针。第三种尝试使用具有完全限定路径的前缀//?/
来扩展所接受的最大路径。没有什么不同。否则,除了长路径外,所有路径都可以正常工作。
我的问题是,有没有一种方法可以让stat()
在操作系统接受的最长文件名下不会失败。我使用的是Windows7,只需键入一个文件名,直到输入框无法接受更多字符为止。或者,总的来说,有没有更好的方法可能不需要指向路径的指针,而是需要不同的标识符?
谢谢。
我认为关于stat()
的特定问题的答案是,不能用前缀将最大长度扩展到260以上,因为前缀仅用于Win32函数,而不用于POSIX函数。没有其他选择。
原始问题:在minGW-W64中,除了在下面的stat()
函数中重复路径名之外,还有其他方法可以获得文件信息吗?
从检查dirent.h
标头来看,dirent struct
中似乎没有任何内容可以在读取后简单地遍历目录中的记录,而不必重复路径。
在stat()
函数中重复路径并不困难,但似乎对最大路径应用了不同的大小限制,并且我无法获得\?
或\.
的前缀来处理它。\?
前缀完全失败;\.
返回统计数据,但在与没有它相同的大小限制下仍然失败。
似乎应该有更好的方法,因为readdir()
正在返回文件/目录,并且应该有一个标识符,但我看不到可用的标识符。或者能够仅传递ep->d_name
到stat()
,而不必包括路径,这就是下面fl_name
中的路径,因为p
只是指向路径之后fl_name
中的点的指针。
谢谢。更完整的代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
int main ( void )
{
DIR *dp;
struct dirent *ep;
struct stat info;
int rc, q, l, i = 0, v = 0;
const char *path = NULL;
char *fl_name,
*p = NULL;
const char *sec_path = "../../databases/";
unsigned int len_sec_path = strlen( sec_path );
fl_name = malloc( len_sec_path + 261 );
// Want p to be at the point in fl_name to write the actual file and directory names in order to get the stats.
// memcpy returns the pointer to the destination; so, just add the length of path to that to get to the end.
p = memcpy( fl_name, sec_path, len_sec_path + 1 ) + len_sec_path;
if ( ( dp = opendir( fl_name ) ) == NULL )
{
printf( "Failed to open the directory of: %s", fl_name );
return 1;
}
while ( ep = readdir( dp ) )
{
memcpy( p, ep->d_name, ( l = strlen( ep->d_name ) ) > 259 ? 260 : l + 1 );
printf( "%d, %sn", strlen( fl_name ), fl_name );
if ( ( rc = stat( fl_name, &info ) ) != 0 )
{
printf( "%sn", fl_name );
printf( "rc = %d, errno : %d, strerror : %sn", rc, errno, strerror( errno ) );
continue;
}
}
return 0;
}
版本2。
int main ( void )
{
DIR *dp;
struct dirent *ep;
struct stat info;
int rc, q, l, i = 0, v = 0;
const char *path = NULL;
char *fl_name,
*p = NULL,
name;
const char *sec_path = "../../databases/";
unsigned int len_sec_path = strlen( sec_path );
fl_name = malloc( len_sec_path + 261 );
// Want p to be at the point in fl_name to write the actual file and directory names in order to get the stats.
// memcpy returns the pointer to the destination; so, just add the length of path to that to get to the end.
p = memcpy( fl_name, sec_path, len_sec_path + 1 ) + len_sec_path;
char *buffer;
if ( (buffer = _getcwd( NULL, 0 )) == NULL )
{
printf( "Failed to get current working directory." );
return 1;
}
printf( "buffer is %sn", buffer );
if ( ( dp = opendir( fl_name ) ) == NULL )
{
printf( "Failed to open the directory of: %s", fl_name );
return 1;
}
if ( _chdir( sec_path ) )
{
printf( "Couldn't change directory." );
return 1;
}
while ( ep = readdir( dp ) )
{
memcpy( p, ep->d_name, ( l = strlen( ep->d_name ) ) > 259 ? 260 : l + 1 );
printf( "%d, %sn", strlen( fl_name ), fl_name );
if ( ( rc = stat( p, &info ) ) != 0 )
{
printf( "%sn", fl_name );
printf( "rc = %d, errno : %d, strerror : %sn", rc, errno, strerror( errno ) );
continue;
}
}
if ( _chdir( buffer ) )
{
printf( "Couldn't change directory." );
return 1;
}
printf( "buffer is %sn", buffer );
free( buffer );
return 0;
}
尝试使用\?
前缀来增加最大路径的代码。我认为上面的版本2有效,我认为这个带有前缀的版本有效,但发现两者都不起作用。即使当目录被更改,使得指向文件名的指针被传递到stat()
,而不需要路径时,该路径似乎仍然以某种方式被包括在最大值中,因为如果路径加上文件名达到260,则stat()
失败,说明找不到文件。我想这没什么大不了的,因为我可以捕捉和处理错误并通知用户。令人恼火的是,用户可以在他们的目录中使用操作系统会接受的文件名,但我不能在应用程序的UI中显示它们。
int get_dir_2( void )
{
DIR *dp;
struct dirent *ep;
struct _stat info;
int rc, q, i = 0, v = 0;
char *fl_name,
*p = NULL,
*path = NULL;
const char *sec_path = "../../databases/",
*prefix = "\\?\";
unsigned int l, len_sec_path = strlen( sec_path );
if ( _chdir( sec_path ) )
{
printf( "Couldn't change directory." );
return 1;
}
char *dirCWD;
if ( (dirCWD = _getcwd( NULL, 0 )) == NULL )
{
printf( "Failed to get current working directory." );
return 1;
}
path = "";
l = strlen( prefix ) + strlen( dirCWD ) + strlen( path );
fl_name = malloc( l + 301 );
p = ( fl_name + l );
sprintf( fl_name, "%s%s%s", prefix, dirCWD, path );
printf( "fl_name is %sn", fl_name );
if ( _chdir( fl_name ) )
{
printf( "Couldn't change directory." );
return 1;
}
if ( ( dp = opendir( fl_name ) ) == NULL )
{
printf( "Failed to open the directory of: %s", fl_name );
return 1;
}
while ( ep = readdir( dp ) )
{
memcpy( p, ep->d_name, ( l = strlen( ep->d_name ) ) > 259 ? 261 : l + 1 );
//printf( "%d, %sn", strcmp( ep->d_name, p ), p );
printf( "%d, %sn", strlen( fl_name ), fl_name );
if ( ( rc = _stat( p, &info ) ) != 0 )
{
printf( "%sn", fl_name );
printf( "rc = %d, errno : %d, strerror : %sn", rc, errno, strerror( errno ) );
continue;
}
printf( "ctime : %d, mtime : %d, atime : %dn", info.st_ctime, info.st_mtime, info.st_atime );
}
free( fl_name );
return 0;
}
我怀疑您的问题发生在几个方面,但如果没有验证检查来捕捉错误发生时的情况,并且使用了非标准的C函数,很难准确判断问题所在。除了验证之外,您还试图在void*
指针上使用指针算术:
p = memcpy( fl_name, sec_path, len_sec_path + 1 ) + len_sec_path;
这也可能导致问题。
您使用的_getcwd()
和_chdir()
不是标准的C函数。我不确定这些是从哪里得到的。
为了避免这些问题,请只使用有效的C函数,并验证程序中的每个步骤,这些步骤对于代码的继续定义操作是必要的。例如,验证每个分配:
#define FN_BUF_LEN 260 /* if you need a constant, #define one (or more) */
...
const char *sec_path = "../../cb/";
size_t len_sec_path = strlen (sec_path);
if (!(fl_name = malloc (len_sec_path + FN_BUF_LEN + 1))) { /* validate malloc */
perror ("malloc-fl_name");
exit (EXIT_FAILURE);
}
不要在memcpy()
的void*
返回上尝试指针运算,例如
/* Want p to be at the point in fl_name to write the actual file and
* directory names in order to get the stats. memcpy returns the pointer to the
* destination; so, just add the length of path to that to get to the end.
*/
p = memcpy (fl_name, sec_path, len_sec_path + FN_BUF_LEN + 1);
p += len_sec_path; /* memcpy() is type void*, cannot use with arithmetic */
验证对chddir()
的每次调用,例如
if (chdir (sec_path) == -1) { /* _chdir() is not std C */
perror ("chdir()"); /* validate result */
return 1;
}
和
if (chdir (buffer)) {
perror ("chdir()");
return 1;
}
把所有的部分放在一起,并在下面的评论中添加额外的想法,你可以这样做:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <sys/stat.h>
#include <limits.h>
#include <unistd.h>
#define FN_BUF_LEN 260 /* if you need a constant, #define one (or more) */
int main (void) {
DIR *dp;
struct dirent *ep;
struct stat info;
char *fl_name,
*p = NULL,
*buffer = NULL;
// const char *sec_path = "../../databases/";
const char *sec_path = "../../cb/";
size_t len_sec_path = strlen (sec_path);
if (!(fl_name = malloc (len_sec_path + FN_BUF_LEN + 1))) { /* validate malloc */
perror ("malloc-fl_name");
exit (EXIT_FAILURE);
}
/* Want p to be at the point in fl_name to write the actual file and
* directory names in order to get the stats. memcpy returns the pointer to the
* destination; so, just add the length of path to that to get to the end.
*/
p = memcpy (fl_name, sec_path, len_sec_path + FN_BUF_LEN + 1);
p += len_sec_path; /* memcpy() is type void*, cannot use with arithmetic */
if ((buffer = getcwd (NULL, 0)) == NULL) { /* _getcwd() is not std C */
perror ("getcwd()"); /* validate result */
return 1;
}
printf ("buffer is %sn", buffer);
if ((dp = opendir (fl_name)) == NULL) {
printf ("Failed to open the directory of: %s", fl_name);
return 1;
}
if (chdir (sec_path) == -1) { /* _chdir() is not std C */
perror ("chdir()"); /* validate result */
return 1;
}
while ((ep = readdir (dp))) {
size_t l = strlen (ep->d_name); /* declare vars in scope needed */
if (l > 259) { /* validate length before memcpy */
fprintf (stderr, "error: %s length %u exceeds allowable.n", ep->d_name, l);
continue;
}
/* filter the "." and ".." directories out */
if (strcmp (ep->d_name, ".") == 0 || strcmp (ep->d_name, "..") == 0)
continue;
memcpy (p, ep->d_name, l + 1);
printf ("%u, %sn", strlen (fl_name), fl_name);
int rc = stat (p, &info);
if (rc == -1) {
printf ("%sn", fl_name);
printf ("rc = %d, errno : %d, strerror : %sn", rc, errno, strerror (errno));
continue;
}
}
if (chdir (buffer)) {
perror ("chdir()");
return 1;
}
printf ("buffer is %sn", buffer);
free (buffer); /* free all memory you have allocated */
free (fl_name);
return 0;
}
示例使用/输出
使用MinGW 5.1(旧的TDM-MinGW)在Win7上进行测试,并将sec_path
更改为"../../cb/"
,以便在Win7来宾上存在文件和目录,您将拥有:
>binreaddir_prob.exe
buffer is C:UsersdavidDocumentsdevsrc-ctmp
18, ../../cb/debugtest
21, ../../cb/default.conf
19, ../../cb/farmtotals
22, ../../cb/farmtotalsfmt
14, ../../cb/first
15, ../../cb/helopp
19, ../../cb/matrixsolv
16, ../../cb/toupper
18, ../../cb/windialog
17, ../../cb/winframe
buffer is C:UsersdavidDocumentsdevsrc-ctmp
这些问题似乎是由于使用了非标准函数、void*
上的指针算法,或者其中一个步骤缺乏验证,导致故障未被注意到,最终导致未定义的行为。
如果您总是在启用警告的情况下编译,编译器就会指出其中的许多问题。对于gcc
,最低使用:
-Wall -Wextra -pedantic -Wshadow
仔细查看这些变化,如果您还有其他问题,请告诉我。