C 中的 Chmod 分配错误的权限
Chmod in C assigning wrong permissions
以下是我的方法代码,该方法将文件从路径复制到文件到作为目标提供的目录。副本工作得很好,但是我的 chmod 调用为目标中的复制文件分配了错误的权限。如果源中的权限是644,则复制的文件的权限是170或120。
几个小时以来我一直在尝试调试它,这让我有点发疯所以非常感谢任何帮助。
void copy_file(char* src, char* dest) {
char a;
//extract file name through a duplicate ptr
char* fname = strdup(src);
char* dname = basename(fname);
//open read and write streams
FILE* read;
FILE* write;
read = fopen(src, "r");
chdir(dest);
write = fopen(dname, "w");
//error checking
if (read == NULL) //|| (write == NULL))
{
perror("Read Error: ");
exit(0);
}
else if (write == NULL)
{
perror("Write Error: ");
exit(0);
}
//write from src to dest char by char
while (1){
a = fgetc(read);
if (a == EOF)
{
break;
}
fputc(a, write);
}
//close files
fclose(read);
fclose(write);
// this is where I attempt to assign source file permissions
//and it goes horribly wrong
struct stat src_st;
if(stat(src, &src_st)){
perror("stat: ");
}
chmod(dname, src_st.st_mode);
printf("%o\n", src_st.st_mode & 0777);
}
你fopen(src, "r")
,然后你chdir(dest)
。这意味着当您稍后调用 stat(src, &src_st)
时,没有理由认为 stat
将访问与 fopen
相同的文件,或者实际上 stat
将访问任何文件完全没有。
如果 stat
失败,您仍然会继续调用 chmod
,因此您将 src_st.st_mode
中的任何随机垃圾传递给 chmod
。
你应该在调用fclose(src)
之前使用fstat(fileno(read), &src_st)
,而不是调用stat(src, &src_st)
。
基本问题是您必须立即、chdir
和stat
检查您的系统调用.
例如,我尝试的第一件事是 copy_file( "test.data", "test2.data" )
没有意识到它需要一个目标 目录 。
char* fname = strdup(src);
char* dname = basename(fname);
dname
现在是 test.data
,与来源相同。
read = fopen(src, "r"); // succeeds
chdir(dest); // fails
write = fopen(dname, "w"); // blows away test.data, the source
你最终会检查 read
和 write
,但是在损坏完成之后。
吹掉你的源文件非常糟糕。您的代码处理失败的系统调用很重要。如果你不这样做,它就会继续航行,造成混乱和破坏。
C 中的大多数系统调用 return 0 表示成功。这是一种反模式,其中 return 值是一个错误标志,因此 false 是失败,其他任何东西都指示错误类型(尽管 stat
不使用那个,它使用 errno)。
失败时,stat
returns -1 为真。所以这是错误的方法。
struct stat src_st;
if(stat(src, &src_st)){
perror("stat: ");
}
相反,您必须检查非零值。
struct stat src_st;
if(stat(src, &src_st) != 0 ){
// Note that I don't use perror, it doesn't provide enough information.
fprintf(stderr, "Could not stat %s: %s\n", src, strerror(errno));
exit(1);
}
如您所料,这会变得非常乏味,您会忘记,或者每次都略有不同。您需要围绕这些函数编写包装器来为您处理错误。
FILE *fopen_checked( const char *file, const char *mode ) {
FILE *fp = fopen(file, mode);
if( file == NULL ) {
fprintf(stderr, "Could not open '%s' for '%s': %s", file, mode, strerror(errno));
exit(1);
}
return fp;
}
这不是最好的错误处理,但它至少可以确保您的代码适当地停止并着火。
关于 chdir
的注意事项:如果可以避免,请不要使用它。 chdir
影响程序的全局状态,当前工作目录,全局变量增加了一切的复杂性。一个函数改变目录而不是像你的那样改变回来是非常非常容易的。现在你的进程处于一个奇怪的状态。
例如,如果有人这样做 copy_file( "somefile", "foo" )
,这会使程序留在 foo/
中。如果他们然后 copy_file( "otherfile", "foo" )
他们会尝试将 foo/otherfile 复制到 foo/foo/otherfile.
并且,与 一样,您的 stat
失败,因为进程现在位于不同的目录中。因此,即使执行 chdir
的函数也会被它混淆。
确保您的函数总是 chdir
返回到像 C 这样的语言的原始目录是非常困难的,并且使错误处理变得非常复杂。相反,留在您的原始目录中并使用 basename
之类的函数将路径连接在一起。
最后,避免混淆你的文件操作。使用文件名或使用文件描述符,但尽量不要同时使用两者。这意味着如果您使用 fopen
,请使用 fstat
和 fchmod
。您可能必须使用 fileno
从 FILE 指针中获取文件描述符。
这避免了随身携带文件描述符和文件名这两项数据并使其保持同步。它还避免了 chdir
或文件被重命名甚至删除的问题,只要文件描述符保持打开状态,它仍然可以工作。
这也是一个问题:
char a;
...
while (1){
a = fgetc(read);
if (a == EOF)
{
break;
}
fputc(a, write);
}
fgetc()
returnsint
,不是char
。每 the C Standard, 7.21.7.1 The fgetc
function:
7.21.7.1 The fgetc
function
Synopsis
#include <stdio.h>
int fgetc(FILE *stream);
假设 sizeof( int ) > sizeof( char )
,char
值是有符号的,2s 补整数,并且 EOF
是一个 int
定义为 -1
(都非常通用值),读取带有 char a = fgetc( stream );
的文件将在读取有效的 0xFF
字符值时失败。如果您的实现的默认 char
值为 unsigned char
,char a = fgetc( stream );
将永远不会产生匹配 EOF
.
的值
以下是我的方法代码,该方法将文件从路径复制到文件到作为目标提供的目录。副本工作得很好,但是我的 chmod 调用为目标中的复制文件分配了错误的权限。如果源中的权限是644,则复制的文件的权限是170或120。
几个小时以来我一直在尝试调试它,这让我有点发疯所以非常感谢任何帮助。
void copy_file(char* src, char* dest) {
char a;
//extract file name through a duplicate ptr
char* fname = strdup(src);
char* dname = basename(fname);
//open read and write streams
FILE* read;
FILE* write;
read = fopen(src, "r");
chdir(dest);
write = fopen(dname, "w");
//error checking
if (read == NULL) //|| (write == NULL))
{
perror("Read Error: ");
exit(0);
}
else if (write == NULL)
{
perror("Write Error: ");
exit(0);
}
//write from src to dest char by char
while (1){
a = fgetc(read);
if (a == EOF)
{
break;
}
fputc(a, write);
}
//close files
fclose(read);
fclose(write);
// this is where I attempt to assign source file permissions
//and it goes horribly wrong
struct stat src_st;
if(stat(src, &src_st)){
perror("stat: ");
}
chmod(dname, src_st.st_mode);
printf("%o\n", src_st.st_mode & 0777);
}
你fopen(src, "r")
,然后你chdir(dest)
。这意味着当您稍后调用 stat(src, &src_st)
时,没有理由认为 stat
将访问与 fopen
相同的文件,或者实际上 stat
将访问任何文件完全没有。
如果 stat
失败,您仍然会继续调用 chmod
,因此您将 src_st.st_mode
中的任何随机垃圾传递给 chmod
。
你应该在调用fclose(src)
之前使用fstat(fileno(read), &src_st)
,而不是调用stat(src, &src_st)
。
基本问题是您必须立即、chdir
和stat
检查您的系统调用.
例如,我尝试的第一件事是 copy_file( "test.data", "test2.data" )
没有意识到它需要一个目标 目录 。
char* fname = strdup(src);
char* dname = basename(fname);
dname
现在是 test.data
,与来源相同。
read = fopen(src, "r"); // succeeds
chdir(dest); // fails
write = fopen(dname, "w"); // blows away test.data, the source
你最终会检查 read
和 write
,但是在损坏完成之后。
吹掉你的源文件非常糟糕。您的代码处理失败的系统调用很重要。如果你不这样做,它就会继续航行,造成混乱和破坏。
C 中的大多数系统调用 return 0 表示成功。这是一种反模式,其中 return 值是一个错误标志,因此 false 是失败,其他任何东西都指示错误类型(尽管 stat
不使用那个,它使用 errno)。
失败时,stat
returns -1 为真。所以这是错误的方法。
struct stat src_st;
if(stat(src, &src_st)){
perror("stat: ");
}
相反,您必须检查非零值。
struct stat src_st;
if(stat(src, &src_st) != 0 ){
// Note that I don't use perror, it doesn't provide enough information.
fprintf(stderr, "Could not stat %s: %s\n", src, strerror(errno));
exit(1);
}
如您所料,这会变得非常乏味,您会忘记,或者每次都略有不同。您需要围绕这些函数编写包装器来为您处理错误。
FILE *fopen_checked( const char *file, const char *mode ) {
FILE *fp = fopen(file, mode);
if( file == NULL ) {
fprintf(stderr, "Could not open '%s' for '%s': %s", file, mode, strerror(errno));
exit(1);
}
return fp;
}
这不是最好的错误处理,但它至少可以确保您的代码适当地停止并着火。
关于 chdir
的注意事项:如果可以避免,请不要使用它。 chdir
影响程序的全局状态,当前工作目录,全局变量增加了一切的复杂性。一个函数改变目录而不是像你的那样改变回来是非常非常容易的。现在你的进程处于一个奇怪的状态。
例如,如果有人这样做 copy_file( "somefile", "foo" )
,这会使程序留在 foo/
中。如果他们然后 copy_file( "otherfile", "foo" )
他们会尝试将 foo/otherfile 复制到 foo/foo/otherfile.
并且,与 stat
失败,因为进程现在位于不同的目录中。因此,即使执行 chdir
的函数也会被它混淆。
确保您的函数总是 chdir
返回到像 C 这样的语言的原始目录是非常困难的,并且使错误处理变得非常复杂。相反,留在您的原始目录中并使用 basename
之类的函数将路径连接在一起。
最后,避免混淆你的文件操作。使用文件名或使用文件描述符,但尽量不要同时使用两者。这意味着如果您使用 fopen
,请使用 fstat
和 fchmod
。您可能必须使用 fileno
从 FILE 指针中获取文件描述符。
这避免了随身携带文件描述符和文件名这两项数据并使其保持同步。它还避免了 chdir
或文件被重命名甚至删除的问题,只要文件描述符保持打开状态,它仍然可以工作。
这也是一个问题:
char a;
...
while (1){
a = fgetc(read);
if (a == EOF)
{
break;
}
fputc(a, write);
}
fgetc()
returnsint
,不是char
。每 the C Standard, 7.21.7.1 The fgetc
function:
7.21.7.1 The
fgetc
functionSynopsis
#include <stdio.h> int fgetc(FILE *stream);
假设 sizeof( int ) > sizeof( char )
,char
值是有符号的,2s 补整数,并且 EOF
是一个 int
定义为 -1
(都非常通用值),读取带有 char a = fgetc( stream );
的文件将在读取有效的 0xFF
字符值时失败。如果您的实现的默认 char
值为 unsigned char
,char a = fgetc( stream );
将永远不会产生匹配 EOF
.