рекурсивная функция для управления заданным путем

Я работаю над модификацией дидактической ОС xv6 (написанной на c) для поддержки символических ссылок (сокращений AKA). Символическая ссылка — это файл типа T_SYM, который содержит путь к месту назначения. Для этого я написал рекурсивную функцию, которая получает путь и буфер и заполняет буфер «настоящим» путем (т. любой уровень пути).

По сути, если у меня есть путь a/b/c/d и ссылка от f до a/b, следующие операции должны быть эквивалентны:

cd a/b/c/d

cd f/c/d

Теперь код написан, но проблема, которую я пытаюсь решить, — это проблема начала пути с «/» (что означает, что путь абсолютный, а не относительный). Прямо сейчас, если я запускаю его с путем с именем /dir1, он обрабатывает его как dir1 (относительный, а не абсолютный).

Это основная функция, она вызывает рекурсивную функцию. pathname — заданный путь, buf будет содержать реальный путь.

int readlink(char *pathname, char *buf, size_t bufsize){
    char name[DIRSIZ];
    char realpathname[100];

    memset(realpathname,0,100);
    realpathname[0] = '/';

    if(get_real_path(pathname, name, realpathname, 0, 0)){
       memmove(buf, realpathname, strlen(realpathname));
       return strlen(realpathname);
    }

    return -1; 
}

Это рекурсивная часть. функция возвращает структуру inode (которая представляет файл или каталог в системе). он строит реальный путь внутри реального пути. ilock и iunlock используются для безопасного использования индекса.

struct inode* get_real_path(char *path, char *name, char* realpath, int position){
    struct inode *ip, *next;
    char buf[100];
    char newpath[100];

    if(*path == '/')
        ip = iget(ROOTDEV, ROOTINO);// ip gets the root directory
    else
        ip = idup(proc->cwd); // ip gets the current working directory

    while((path = skipelem(path, name)) != 0){name will get the next directory in the path, path will get the rest of the directories
        ilock(ip);
        if(ip->type != T_DIR){//if ip is a directory
            realpath[position-1] = '\0';
            iunlockput(ip);
            return 0;
        }

        if((next = dirlookup(ip, name, 0)) == 0){//next will get the inode of the next directory
            realpath[position-1] = '\0';
            iunlockput(ip);
            return 0;
        }
        iunlock(ip);

        ilock(next);

        if (next->type == T_SYM){ //if next is a symbolic link
            readi(next, buf, 0, next->size); //buf contains the path inside the symbolic link (which is a path)
            buf[next->size] = 0;
            iunlockput(next);

            next = get_real_path(buf, name, newpath, 0);//call it recursively (might still be a symbolic link)

            if(next == 0){
                realpath[position-1] = '\0';
                iput(ip);

                return 0;
            }

            name = newpath;
            position = 0;
        }     
        else
        iunlock(next);

        memmove(realpath + position, name, strlen(name));
        position += strlen(name);
        realpath[position++]='/';
        realpath[position] = '\0';

        iput(ip);

        ip = next;
    }  
    realpath[position-1] = '\0';

    return ip;
}

Я пробовал много способов сделать это правильно, но безуспешно. Если кто увидит проблему, буду рад услышать решение. Спасибо, Эяль


person Eyal    schedule 27.06.2012    source источник
comment
так что же происходит с этим кодом? оно раздавит? или дать неправильный ответ? или просто продолжает бежать, ничего не говоря?   -  person Qnan    schedule 28.06.2012
comment
Не зная ошибки, с которой вы столкнулись, трудно сделать конкретные предложения; но я скептически отношусь как к (а) рекурсивной функции в ядре, так и к (б) построению полных путей внутри ядра. Если вы продолжите использовать рекурсивную функцию, вам в конечном итоге потребуется ограничить глубину рекурсии, чтобы избежать использования всей доступной памяти ядра. Я также удивлен, что вы редактируете пути на лету - прохождение каждого отдельного компонента пути за раз кажется мне более правдоподобным, чем создание потенциально огромной новой строки для описания реального пути...   -  person sarnold    schedule 28.06.2012
comment
@MikhailKozhevnikov - код дает неправильный ответ (как я объяснил, при вводе пути, начинающегося с косой черты, который является абсолютным путем, он ведет себя неправильно)   -  person Eyal    schedule 28.06.2012
comment
@sarnold - на самом деле был фрагмент кода, ограничивающий глубину до 16 ссылок, но я удалил его, чтобы максимально упростить. Причина, по которой я решил отредактировать путь на лету, заключается в том, что он является частью задания, и требованием для этой части было реализовать функцию readlink, которая возвращает реальный путь ссылки.   -  person Eyal    schedule 28.06.2012
comment
Ура, упрощенный код! Превосходно. :D   -  person sarnold    schedule 28.06.2012
comment
.. Между прочим, функция readlink() в других системах гораздо проще — она просто считывает конкретную ссылку из символической ссылки и возвращает ее пользователю. Если именованный файл не является символической ссылкой, происходит сбой (EINVAL).   -  person sarnold    schedule 28.06.2012
comment
таким образом это было намного проще, но в этой версии readlink() должна разыменовывать любые символические ссылки, встречающиеся в пути.   -  person Eyal    schedule 28.06.2012
comment
У меня не было большого опыта работы с xv6, но... не могли бы вы просто пройтись по этой штуке и выяснить, что не так? Лично у меня возникают проблемы с отладкой чужого кода, просто глядя на него :)   -  person Qnan    schedule 28.06.2012


Ответы (2)


Думаю, понятно, что после запуска get_real_path(pathname, name, realpathname, 0, 0) realpathname не может начинаться с косой черты.

Если функция выполняется успешно, memmove(realpath + position, name, strlen(name)) гарантирует, что realpath начинается с name, поскольку переменная position всегда содержит ноль при первом вызове memmove. Я бы предложил что-то вроде

if(*path == '/') {
   ip = iget(ROOTDEV, ROOTINO); // ip gets the root
   realpath[position++] = '/';  
} else
   ip = idup(proc->cwd); // ip gets the current working directory

P.S. Я не уверен, почему вы ставите косую черту в realpathname перед выполнением get_real_path, поскольку на данный момент вы действительно не знаете, является ли предоставленный путь абсолютным.

person Qnan    schedule 28.06.2012

Хорошо, нашел проблему... Проблема была глубже, чем я думал... Каким-то образом реальный путь иногда менялся без видимой причины... но причиной была строка: name = newpath;

решение состояло в том, чтобы изменить эту строку на strcpy(name,newpath);

предыдущая строка сделала привязку между именем и реальным путем... что может быть нормально, если мы не имели дело с программными ссылками. При разыменовании подпути эта привязка все испортила.

Спасибо за попытки

person Eyal    schedule 01.07.2012