C多线程和realpath

时间:2016-12-17 23:11:56

标签: c multithreading race-condition working-directory realpath

我正在创建一个程序,该程序以递归方式查找指定目录及其子目录中C文件之间的所有#include依赖项。依赖路径应该是绝对路径,因此我使用 realpath 来解析相对路径和符号链接。由于可能有很多文件,我决定使用OpenMP或pthreads对程序进行多线程处理。

问题是realpath解析了工作目录中的路径。所有线程共享相同的工作目录,因此我需要在 chdir 和realpath上放置一个互斥锁。

是否有任何替代标准函数到realpath,它还使目录从路径中解析路径?

2 个答案:

答案 0 :(得分:2)

有许多带有at后缀的POSIX函数(例如openat())可以与指定的目录一起使用。但是,POSIX中没有realpathat()函数。还没有opendirat(),但有fdopendir()为开放目录文件描述符创建DIR流。

在多线程程序中,任何使用chdir()都是充满希望的。

您应该重新考虑您的算法以使用各种*at()函数来避免需要更改目录。您可以打开目录(open()openat()O_DIRECTORY,但O_DIRECTORY不是100%必需,也不支持在macOS上),以便您可以使用*at()调用中的目录文件描述符来正确访问文件。

答案 1 :(得分:0)

我在解决方案上做了一些工作。它绝不是最佳的,但至少它似乎起作用。我创建了函数 abspathat ,它将相对路径转换为绝对路径。然后我使用内置的 readlinkat 来修复符号链接。该解决方案处理转向路径,如" ../ code.c" " ./ code.c" " code.c"进入" /dir/code.c"。但是它目前没有修复诸如../dir/../code.c之类的路径,尽管为什么有人会创建这样的路径。它也不检查文件是否确实存在。使用此代码随意改进或做任何你喜欢的事。

#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <dirent.h>
#include <stdio.h>
/*****************************************************************************/
char *abspathat(char *dirpath, int dirlen, char *path);
/*****************************************************************************/
static const int MAX_FILEPATH = 4096;
/*****************************************************************************/
char *realpathat(int dirfd, char *dirpath, int dirlen, char *path) {
    char *abs = abspathat(dirpath, dirlen, path);
    char *buf = malloc(sizeof(char)*MAX_FILEPATH);
    ssize_t size = readlinkat(dirfd, abs, buf, MAX_FILEPATH);
    char *realpath;
    if(size != -1) {
        realpath = malloc(sizeof(size+1));
        memcpy(realpath, buf, size);
        realpath[size] = '\0';
        free(abs);
    } else {
        realpath = abs;
    }
    free(buf);
    return realpath;
}
/*---------------------------------------------------------------------------*/
char *abspathat(char *dirpath, int dirlen, char *path) {
    /* If absolute */
    if(path[0] == '/') {
        return path;
    }
    int i;
    char *right;
    int d = 0;
    int rlen = strlen(path);
    int llen = 0;
    if(path[0] == '.') {
        if(path[1] == '.' && path[2] == '/') {
            for(i = 3, d = 1; path[i] == '.'
                    && path[i+1] == '.'
                    && path[i+2] == '/'
                    && i < rlen; i+=3) {
                d++;
            }
            right = &path[i];
            rlen -= i;
        } else if(path[1] == '/') {
            right = &path[2];
            rlen -= 2;
        }
    } else {
        right = &path[0];
    }
    for(i = dirlen - 1 - (dirpath[dirlen-1] == '/'); d && i; i--) {
        if(dirpath[i] == '/') {
            d--;
        }
    }
    llen = i+1;
    char *cpy = malloc(sizeof(char)*(llen + rlen + 2));
    memcpy(cpy, dirpath, llen);
    cpy[llen] = '/';
    memcpy(cpy+llen+1, right, rlen);
    cpy[llen+rlen+1] = '\0';
    return cpy;
}
/*---------------------------------------------------------------------------*/
int main(int argc, char *argv[]) {
    if(argc == 3) {
        char *dirpath = argv[1];
        DIR *d = opendir(dirpath);
        char *path = argv[2];
        char *resolved = realpathat(dirfd(d), dirpath, strlen(dirpath), path);
        printf("%s\n", resolved);
    } else {
        printf("realpathat [directory] [filepath]\n");
    }
    return 0;
}