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.源代码