我希望能够检查是否可以在Linux上打开文件(用于读取或读取和写入)。但是我无法控制打开文件的代码,因此我不能做我通常会做的事情,即打开它然后处理错误。
我很感激,在调用返回之后但在打开调用之前,由于权限更改,任何检查都会出现竞争条件,但我正在尝试避免从我无法控制的库中记录某些不需要的错误
我知道stat,但我不想尝试复制检查用户ID和组ID的逻辑。
答案 0 :(得分:8)
您可以使用:
access("filename", R_OK);
或
euidaccess("filename", R_OK);
检查您的UID或EUID是否具有对相应文件的读取权限。 (如果你正在运行setuid,UID和EUID会有所不同)
答案 1 :(得分:1)
使用euidaccess或access,尽管您几乎总是希望使用前者。
答案 2 :(得分:1)
(编辑:添加这个的原因是,通过这种方法,你可以确保你可以避免竞争条件。也就是说,这是一个非常棘手的方法,所以也许只是应对潜在的竞争条件是一个更好的实际方法)
如果你的目标是保护你不拥有的代码免受未处理的错误的影响,那么使用LD_PRELOAD拦截open open本身可能是有用的。 malloc的一个例子是:Overriding 'malloc' using the LD_PRELOAD mechanism
这里我快速即兴发布你如何做到这一点 - 基本上是一个拦截器,它将启动一个交互式shell来纠正错误。
警告:许多公开呼叫实际上由于合法原因而失败,例如:当程序遍历尝试查找文件的路径中的不同目录时,请将此代码视为教育示例,仅用于此示例代码 - 如果您接近现实世界使用,您的代码肯定需要要聪明。尽管如此,让我们来看看。
首先,你无法控制的“攻击性”程序:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char *argv[]) {
int res = 0;
printf("About to try to open the file...\n");
res = open("/tmp/unreadable", O_RDONLY);
printf("The result after opening: %d\n", res);
if (res < 0) {
perror("Could not open, and here is what the errno says");
} else {
char buf[1024];
int fd = res;
res = read(fd, buf, sizeof(buf));
printf("Read %d bytes, here are the first few:\n", res);
buf[30] = 0;
printf("%s\n", buf);
close(fd);
}
}
然后是拦截器:
#include <stdio.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdlib.h>
#define __USE_GNU
#include <dlfcn.h>
static int (*real_open)(const char *pathname, int flags, ...)=NULL;
static void __open_trace_init(void)
{
real_open = dlsym(RTLD_NEXT, "open");
if (NULL == real_open) {
fprintf(stderr, "Error in `dlsym`: %s\n", dlerror());
return;
}
}
int open(const char *pathname, int flags, ...)
{
if(real_open==NULL)
__open_trace_init();
va_list va;
int res = 0;
do {
if (flags & O_CREAT) {
int mode = 0;
va_start(va, flags);
mode = va_arg(va, int);
va_end(va);
fprintf(stderr, "open(%s, %x, %x) = ", pathname, flags, mode);
res = real_open(pathname, flags, mode);
fprintf(stderr, "%d\n", res);
} else {
fprintf(stderr, "open(%s, %x) = ", pathname, flags);
res = real_open(pathname, flags);
fprintf(stderr, "%d\n", res);
}
if (res < 0) {
printf("The open has returned an error. Please correct and we retry.\n");
system("/bin/sh");
}
} while (res < 0);
return res;
}
以下是运行时的样子:
ayourtch@ayourtch-lnx:~$ echo This is unreadable >/tmp/unreadable ayourtch@ayourtch-lnx:~$ chmod 0 /tmp/unreadable ayourtch@ayourtch-lnx:~/misc/stackoverflow$ LD_PRELOAD=./intercept ./a.out About to try to open the file... open(/tmp/unreadable, 0) = -1 The open has returned an error. Please correct and we retry. open(/dev/tty, 802) = 3 open(/dev/tty, 802) = 3 open(/home/ayourtch/.bash_history, 0) = 3 open(/home/ayourtch/.bash_history, 0) = 3 open(/lib/terminfo/x/xterm, 0) = 3 open(/etc/inputrc, 0) = 3 sh-4.1$ ls -al /tmp/unreadable ---------- 1 ayourtch ayourtch 19 2011-10-18 13:03 /tmp/unreadable sh-4.1$ chmod 444 /tmp/unreadable sh-4.1$ exit open(/home/ayourtch/.bash_history, 401) = 3 open(/home/ayourtch/.bash_history, 0) = 3 open(/home/ayourtch/.bash_history, 201) = 3 open(/tmp/unreadable, 0) = 3 The result after opening: 3 Read 19 bytes, here are the first few: This is unreadable �0 ayourtch@ayourtch-lnx:~/misc/stackoverflow$
顺便说一下,这个例子也暴露了第一个“测试”代码中的一个明显的错误 - 我应该检查读取的字符数至少为30并相应地放置空字符。
无论如何,该代码应该是错误的并且在控件之外,所以在其中有一个错误是好的 - 否则你不需要使用这种黑客: - )