内核对目录的看法与您的看法不同——它考虑的是inode数字。它为目录保存一个inode编号(和设备编号)的记录,这就是它作为当前目录所需要的全部内容。您有时会为它指定一个名称,这意味着它会跟踪与该名称对应的inode编号,但它只保留inode编号,因为这就是它所需要的全部。
因此,您必须编写一个合适的函数。您可以直接用打开目录
open()
精确地得到一个文件描述符
fchdir()
在许多现代系统中,你不能用它做任何其他事情。您也可能无法打开当前目录;您应该测试该结果。这种情况很少发生,但并不存在。(SUID程序可能
chdir()
到suid权限允许的目录,然后删除suid权限,使进程无法读取该目录;the
getcwd()
在这种情况下,调用也会失败-因此您必须错误地检查它!)另外,如果在(可能是长时间运行的)进程打开某个目录时删除了该目录,则
getcwd()。
会失败。
总是检查来自系统调用的结果;通常情况下,它们可能会失败,即使这样做非常不方便。有例外-
getpid()
是典型的例子——但它们很少而且相距甚远。(好吧:不是很遥远-
getppid()
是另一个例子,它非常接近
GETPID.()
在手册中;以及
getuid()
在手册中亲属也不远。)
多线程应用程序是一个问题;使用
CHDIR()
这不是个好主意。你可能不得不
fork()
让孩子评估目录名,然后以某种方式将其传回父目录。
比格斯问道:
这很有趣,但似乎与Querent报告的经验相反:getcwd知道如何从fd获取路径。这表明系统至少在某些情况下知道如何从fd转到path;您可以编辑您的答案来解决这个问题吗?
为此,它有助于理解
GETCWD()
可以写入函数。忽略“无权限”的问题,其工作的基本机制是:
-
在根目录“/”上使用stat(这样您就知道何时停止向上移动)。
-
在当前目录“”上使用stat(这样您就知道自己在哪里);这会给您一个当前的inode。
-
直到到达根目录:
-
扫描父目录“..”,直到找到与当前inode具有相同inode的条目;这将为您提供目录路径的下一个组件名称。
-
然后将当前inode更改为父目录中“.”的inode。
-
当您到达根目录时,您可以构建路径。
下面是该算法的一个实现。它是旧的代码(最初是1986年,最后一次非整容性的修改是在1998年),没有使用
FCH()
应该如此。如果您有要遍历的NFS自动安装文件系统,它也会工作得很糟糕——这就是为什么我不再使用它的原因。然而,这大致相当于
GETCWD()
. (噢,我看到一个18个字符的字符串(“../123456789.abcd”)—好吧,在写它的时候,我工作的机器只有非常老的14个字符的文件名—而不是现代的灵活名称。就像我说的,这是旧密码!我在15年左右的时间里,甚至更长的时间里,都没有见过这样的文件系统。还有一些代码可以处理较长的名称。小心使用。)
/*
@(#)File: $RCSfile: getpwd.c,v $
@(#)Version: $Revision: 2.5 $
@(#)Last changed: $Date: 2008/02/11 08:44:50 $
@(#)Purpose: Evaluate present working directory
@(#)Author: J Leffler
@(#)Copyright: (C) JLSS 1987-91,1997-98,2005,2008
@(#)Product: :PRODUCT:
*/
/*TABSTOP=4*/
#define _POSIX_SOURCE 1
#include "getpwd.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#if defined(_POSIX_SOURCE) || defined(USG_DIRENT)
#include "dirent.h"
#elif defined(BSD_DIRENT)
#include <sys/dir.h>
#define dirent direct
#else
What type of directory handling do you have?
#endif
#define DIRSIZ 256
typedef struct stat Stat;
static Stat root;
#ifndef lint
/* Prevent over-aggressive optimizers from eliminating ID string */
const char jlss_id_getpwd_c[] = "@(#)$Id: getpwd.c,v 2.5 2008/02/11 08:44:50 jleffler Exp $";
#endif /* lint */
/* -- Routine: inode_number */
static ino_t inode_number(char *path, char *name)
{
ino_t inode;
Stat st;
char buff[DIRSIZ + 6];
strcpy(buff, path);
strcat(buff, "/");
strcat(buff, name);
if (stat(buff, &st))
inode = 0;
else
inode = st.st_ino;
return(inode);
}
/*
-- Routine: finddir
Purpose: Find name of present working directory
Given:
In: Inode of current directory
In: Device for current directory
Out: pathname of current directory
In: Length of buffer for pathname
Maintenance Log
---------------
10/11/86 JL Original version stabilised
25/09/88 JL Rewritten to use opendir/readdir/closedir
25/09/90 JL Modified to pay attention to length
10/11/98 JL Convert to prototypes
*/
static int finddir(ino_t inode, dev_t device, char *path, size_t plen)
{
register char *src;
register char *dst;
char *end;
DIR *dp;
struct dirent *d_entry;
Stat dotdot;
Stat file;
ino_t d_inode;
int status;
static char name[] = "../123456789.abcd";
char d_name[DIRSIZ + 1];
if (stat("..", &dotdot) || (dp = opendir("..")) == 0)
return(-1);
/* Skip over "." and ".." */
if ((d_entry = readdir(dp)) == 0 ||
(d_entry = readdir(dp)) == 0)
{
/* Should never happen */
closedir(dp);
return(-1);
}
status = 1;
while (status)
{
if ((d_entry = readdir(dp)) == 0)
{
/* Got to end of directory without finding what we wanted */
/* Probably a corrupt file system */
closedir(dp);
return(-1);
}
else if ((d_inode = inode_number("..", d_entry->d_name)) != 0 &&
(dotdot.st_dev != device))
{
/* Mounted file system */
dst = &name[3];
src = d_entry->d_name;
while ((*dst++ = *src++) != '\0')
;
if (stat(name, &file))
{
/* Can't stat this file */
continue;
}
status = (file.st_ino != inode || file.st_dev != device);
}
else
{
/* Ordinary directory hierarchy */
status = (d_inode != inode);
}
}
strncpy(d_name, d_entry->d_name, DIRSIZ);
closedir(dp);
/**
** NB: we have closed the directory we are reading before we move out of it.
** This means that we should only be using one extra file descriptor.
** It also means that the space d_entry points to is now invalid.
*/
src = d_name;
dst = path;
end = path + plen;
if (dotdot.st_ino == root.st_ino && dotdot.st_dev == root.st_dev)
{
/* Found root */
status = 0;
if (dst < end)
*dst++ = '/';
while (dst < end && (*dst++ = *src++) != '\0')
;
}
else if (chdir(".."))
status = -1;
else
{
/* RECURSE */
status = finddir(dotdot.st_ino, dotdot.st_dev, path, plen);
(void)chdir(d_name); /* We've been here before */
if (status == 0)
{
while (*dst)
dst++;
if (dst < end)
*dst++ = '/';
while (dst < end && (*dst++ = *src++) != '\0')
;
}
}
if (dst >= end)
status = -1;
return(status);
}
/*
-- Routine: getpwd
Purpose: Evaluate name of current directory
Maintenance Log
---------------
10/11/86 JL Original version stabilised
25/09/88 JL Short circuit if pwd = /
25/09/90 JL Revise interface; check length
10/11/98 JL Convert to prototypes
Known Bugs
----------
1. Uses chdir() and could possibly get lost in some other directory
2. Can be very slow on NFS with automounts enabled.
*/
char *getpwd(char *pwd, size_t plen)
{
int status;
Stat here;
if (pwd == 0)
pwd = malloc(plen);
if (pwd == 0)
return (pwd);
if (stat("/", &root) || stat(".", &here))
status = -1;
else if (root.st_ino == here.st_ino && root.st_dev == here.st_dev)
{
strcpy(pwd, "/");
status = 0;
}
else
status = finddir(here.st_ino, here.st_dev, pwd, plen);
if (status != 0)
pwd = 0;
return (pwd);
}
#ifdef TEST
#include <stdio.h>
/*
-- Routine: main
Purpose: Test getpwd()
Maintenance Log
---------------
10/11/86 JL Original version stabilised
25/09/90 JL Modified interface; use GETCWD to check result
*/
int main(void)
{
char pwd[512];
int pwd_len;
if (getpwd(pwd, sizeof(pwd)) == 0)
printf("GETPWD failed to evaluate pwd\n");
else
printf("GETPWD: %s\n", pwd);
if (getcwd(pwd, sizeof(pwd)) == 0)
printf("GETCWD failed to evaluate pwd\n");
else
printf("GETCWD: %s\n", pwd);
pwd_len = strlen(pwd);
if (getpwd(pwd, pwd_len - 1) == 0)
printf("GETPWD failed to evaluate pwd (buffer is 1 char short)\n");
else
printf("GETPWD: %s (but should have failed!!!)\n", pwd);
return(0);
}
#endif /* TEST */