pwd指令的简单实现

本文详细探讨了pwd指令在不同目录层级下的工作原理,并针对遇到的问题提出了解决方案,包括逐层查找文件系统路径、识别根目录、处理挂载路径限制等关键步骤。进一步介绍了改进路径解析的方法,通过检查inode号一致性和使用结构化统计信息来构建完整绝对路径。同时,文章指出在符号链接目录下执行pwd时的问题,并提供了部分解决方案的思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >



pwd指令的简单实现

2015-08-23

pwd以绝对路径打印当前的工作目录。因为整个系统的文件组织是树形的,所以,可以从当前目录逐层向根目录进行查找,当找到根目录,即可得到完全路径。

1.如何逐层查找?

系统通过inode节点来管理文件,所以每个文件会有一个inode号。目录是比较特殊的文件,通过<inode, name>的列表组织目录下的文件。每个目录下有两个特殊的文件名".", "..",分别表示当前目录和父目录。

当前目录             父目录

通过查找父目录,知道当前目录的名字是sub_dir



int
get_name_from_inode( char *cpDir, ino_t inoInput, char *cpName )
{
    DIR *fpDir;
    struct dirent *stpEntry;

    if( ( fpDir = opendir( cpDir ) ) == NULL )
    {
        fprintf( stderr, "[%s:%d]opendir() error: %s\n", _FL_, strerror( errno ) );
        return -1;
    }

    while( ( stpEntry = readdir( fpDir ) ) != NULL )
    {
        if( strcmp( stpEntry->d_name, "dev" ) == 0 )
            printf( "dev ino: %d\n", stpEntry->d_ino );

        if( stpEntry->d_ino == inoInput )
        {
            printf( "d_name: %s\n", stpEntry->d_name );
            strcpy( cpName, stpEntry->d_name );
            break;
        }
    }

    closedir( fpDir );
    return stpEntry != NULL ? 0 : -1;
}

2.如何知道到了根目录?

到达根目录说明没有父目录了,但是".", ".."还是存在于根目录,只是其inode号是相同的。


int
do_pwd( const char *ccpPath, char *cpCurr )
{
    ino_t inoCurr;
    ino_t inoParent;
    char  caName[ NAMELEN ];

    if( get_inode_from_name( ccpPath, &inoCurr ) == -1 ||
            get_inode_from_name( "..", &inoParent ) == -1 )
    {
        return -1;
    }

    /* haven't reached the root of the file system */
    if( inoCurr != inoParent )
    {
        chdir( ".." );
        if( get_name_from_inode( ".", inoCurr, caName ) != 0 )
        {
            fprintf( stderr, "[%s:%d]get_name_from_inode() error: %s\n", _FL_, strerror( errno ) );
            return -1;
        }
        do_pwd( ".", cpCurr );
        strcat( cpCurr, caName );
    }

    strcat( cpCurr, "/" );
    return 0;
}

其执行结果如下:

运行结果和系统的工具比较,不太让人满意。因为每个文件系统是一棵完整的树,而我的代码目录所处的文件系统被挂载在/home目录下,所以到达jliu这级目录时,已经到达了文件系统的根,其".",".."inode号是相同的。

3.如何打破文件系统的限制,形成完整的绝对路径?

思路一:

查看系统所有的挂载路径,将所有挂载路径和上面求得的路径拼接起来,如果是合法路径且inode号与最初目录的inode号相同,即是想要的最终的路径。

int
do_pwd_fix( const char *ccpPath, char *cpCurr )
{
    char caFullPath[ 2 * NAMELEN ];
    char caMounted[ NAMELEN ];
    char caLine[ LINELEN ];
    ino_t inoCurr;
    int  iRet;
    struct stat stBuf;
    FILE *fp = NULL;

    if( get_inode_from_name( ccpPath, &inoCurr ) == -1 )
    {
        fprintf( stderr, "[%s:%d]get_inode_from_name() error: %s\n", _FL_, strerror( errno ) );
        return -1;
    }

    /* get the current directory in a file system */
    if( do_pwd( ccpPath, cpCurr ) != 0 )
    {
        fprintf( stderr, "[%s:%d]do_pwd() error: %s\n", _FL_, strerror( errno ) );
        return -1;
    }

    /* the file system is mounted at root */
    if( ( iRet = stat( cpCurr, &stBuf ) == 0 ) && stBuf.st_ino == inoCurr )
        return 0;

    /*
    ** find the mouned point of the file system,
    ** which not mounted at the root
    */
    if( ( fp = fopen( "/etc/mtab", "r" ) ) == NULL )
    {
        fprintf( stderr, "[%s:%d]fopen() error: %s\n", _FL_, strerror( errno ) );
        return -1;
    }

    while( fgets( caLine, LINELEN, fp ) != NULL )
    {
        memset( caMounted, 0, sizeof( caMounted ) );
        memset( caFullPath, 0, sizeof( caFullPath ) );
        sscanf( caLine, "%*s%s", caMounted );

        /*
        ** the items mounted at root have been handled
        */
        if( strcmp( caMounted, "/" ) == 0 )
            continue;

        strcpy( caFullPath, caMounted );
        strcat( caFullPath, cpCurr );


        /*
        ** check whether the path is legal
        */
        if( ( iRet = stat( caFullPath, &stBuf ) != 0 )
                && ( errno == ENOENT || errno == EACCES ) )
        {
            continue;
        }
        else if( iRet == 0 && stBuf.st_ino != inoCurr )
        {
            continue;
        }
        else if( iRet == 0 && stBuf.st_ino == inoCurr )
        {
            strcpy( cpCurr, caFullPath );
            break;
        }
        else
        {
            fprintf( stderr, "[%s:%d]stat() error: %s\n", _FL_, strerror( errno ) );
            return -1;
        }
    }

    fclose( fp );
    return strcmp( cpCurr, caFullPath );
}

运行结果如下:

初看起来没什么问题,但是将可执行文件放到/home/dev下去执行,结果如下:

1)问题一:/home目录下执行mypwd,打印出来的是/,显然不正确。

因为/home目录的inode号确实和/目录的inode号相同。

2)问题二:/dev目录下无法正常的工作,即使系统调用没有出错,但是好像代码逻辑有问题。

初步判断是因为stat()出来的st_inode和在父目录readdir()出来的d_ino不同。

 

思路二:

针对上面的两个问题,可以看出单纯使用父目录和当前目录的inode号作为判断依据貌似不太可靠。转换思路比较struct stat的内容,作为判断的依据,memcmp( &stBufCurr, &stBufParent, sizeof( struct stat ) );。这个时候,我放弃使用挂载路径拼接的方式求完整绝对路径的方式。而是让程序不停滴chdir,直到memcmp( &stBufCurr, &stBufParent, sizeof( struct stat ) ) ==0,说明已经到达最终的root

int
do_pwd_fix( const char *ccpPath, char *cpCurr )
{
    char        caFullPath[ 2 * NAMELEN ];
    char        caMounted[ NAMELEN ];
    item        *pLink;
    struct stat stBufStd;
    struct stat stBufCurr;
    struct stat stBufParent;

    if( stat( ccpPath, &stBufStd) != 0 )
    {
        fprintf( stderr, "[%s:%d]stat() error: %s\n", _FL_, strerror( errno ) );
        return -1;
    }

    /*
    ** get the current directory in a file system
    */
    if( do_pwd( ccpPath, cpCurr ) != 0 )
    {
        fprintf( stderr, "[%s:%d]do_pwd() error: %s\n", _FL_, strerror( errno ) );
        return -1;
    }

    /*
    ** whether reach the final root
    */
    if( stat( cpCurr, &stBufCurr ) == 0 && memcmp( &stBufCurr, &stBufStd, sizeof( struct stat ) ) == 0 )
    {
        /* reach the final root, so respond to me */
        return 0;
    }

    /*
    ** haven't reached the final root, go on changing direcotry
    ** until memcmp( &stBufCurr, &stBufParent ) == 0
    */
    if( stat( ".", &stBufCurr ) != 0 )
    {
        fprintf( stderr, "[%s:%d]stat() error: %s\n", _FL_, strerror( errno ) );
        return -1;
    }

    if( chdir( ".." ) != 0 && stat( ".", &stBufParent ) != 0 )
    {
        fprintf( stderr, "[%s:%d]chdir() or stat() error: %s\n", _FL_, strerror( errno ) );
        return -1;
    }

    init_link( &pLink );
    insert_link( &pLink , "." );
    insert_link( &pLink , ".." );
    while( memcmp( &stBufCurr, &stBufParent, sizeof( struct stat ) ) != 0 )
    {
        /* search the directory beyond the file system, stored in caMounted */
        if( get_name_from_inode_ext( ".", &stBufStd, caMounted, pLink, cpCurr ) != 0 )
        {
            fprintf( stderr, "[%s:%d]get_name_from_inode() error: %s\n", _FL_, strerror( errno ) );
            clear_link( pLink );
            return -1;
        }
        strcpy( caFullPath, caMounted );
        strcat( caFullPath, cpCurr );

        strcpy( cpCurr, "/" );
        strcat( cpCurr, caFullPath );

        memcpy( &stBufCurr, &stBufParent, sizeof( struct stat ) );
        if( chdir( ".." ) != 0 && stat( ".", &stBufParent) != 0 )
        {
            clear_link( pLink );
            fprintf( stderr, "[%s:%d]chdir() or stat() error: %s\n", _FL_, strerror( errno ) );
            return -1;
        }
    }

    /* reach the final root */
    clear_link( pLink );
    return 0;
}

运行结果如下:

 

4.但是又发现一个新的问题,进入一个符号连接的目录,执行mypwd,其结果如下:

打印出来的是从符号连接指向的实际目录的目录路径,而不是符号连接的目录路径。

暂时没有找到方法解决。

/* TODO */


5.源代码

http://download.youkuaiyun.com/detail/qq123386926/9039517


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值