2011-10-18 54 views
2

我希望能够检查文件是否可以在Linux上打开(用于读取或读写)。但是,我没有控制将打开文件的代码,所以我不能做我通常会做的事情,即打开它然后处理错误。如何检查我是否有权打开文件,而无需在C中的Linux上打开文件?

我明白,由于在调用返回之后但在打开调用之前权限发生更改,总是会出现任何支票上的竞争条件,但我试图避免从库中无法控制某些不良错误记录过度。

我所知道的stat,但我不希望需要尝试复制检查用户ID和组ID的逻辑。

+0

相关:http:// stackoverflow。com/questions/4985937/how-can-my-c-program-check-if-it-has-execution-permission-on-a-given-file – Flexo

+1

如果库正在生成错误消息,那么该库设计不佳。停止使用它。正是因为这类问题,库应该被设计为返回错误值而不是生成无效的错误消息。 –

+0

在http://git.libusb.org/?p=libusb.git;a=commit;h=22d61cd0891d8304dfc1a70579cf154fd8e6644a;js=1修复之前,库是libusb,所以我没有太多的选择。 –

回答

8

您可以使用:

access("filename", R_OK); 

euidaccess("filename", R_OK); 

要检查您的UID或EUID具有读取权限到相应的文件。 (UID和EUID如果你正在运行的setuid会有所不同)

+0

'euidaccess'! (不知道这个家伙存在!)+1 – FractalSpace

1

使用euidaccessaccess,虽然你几乎可以肯定总是要使用前者。

+0

我不同意“几乎肯定希望用户使用前者” - 如果您有setuid/setgid二进制文件,通过授予对给定资源的访问权来告诉您是否要升级用户的访问权限通常很有用。 – Flexo

1

(编辑:添加这样做的原因是,使用这种方法,你可以确保你能避免竞争条件说,这是一个相当棘手的方法。所以也许只是应对潜在的竞争条件是一种更好的实用方法)。

如果你的目标是保护你不从未处理的错误自己的代码,使用LD_PRELOAD拦截公开征集本身可能是有用的。它与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; 
} 

这里是它的外观上运行时,如:

 
[email protected]:~$ echo This is unreadable >/tmp/unreadable 
[email protected]:~$ chmod 0 /tmp/unreadable 
[email protected]:~/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 
[email protected]:~/misc/stackoverflow$ 

顺便说这个例子也暴露了一个明显的错误在第一个“试验”的代码 - 我应该有检查编辑读取的字符数至少为30,并相应地放置空字符。

无论如何,该代码应该是控制的车外,所以这是好样的,以有一个bug - 否则你就不需要使用这种破解的:-)