内核以不同于您的方式来思考目录 - 它以inode号码为单位进行思考。它记录了目录的inode号码(和设备号码),这就是它作为当前目录所需的全部内容。您有时指定一个名称给它,意味着它会跟踪到对应该名称的inode号码,但它只保留inode号码,因为那是它所需的全部内容。
因此,您将需要编写一个适当的函数。您可以使用open()直接打开一个目录,以获取可由fchdir()使用的文件描述符;在许多现代系统上,您不能对其执行其他任何操作。您也可能无法打开当前目录;您应该测试该结果。这种情况很少见,但并非不存在。(SUID程序可能会chdir()到SUID权限允许的目录,但然后放弃SUID权限,使进程无法读取该目录;getcwd()调用在这种情况下也会失败-因此您必须检查错误!)此外,如果在您的(可能长时间运行的)进程打开它时删除了一个目录,则随后的getcwd()将失败。
始终检查系统调用的结果;即使它们失败,通常也有情况。有例外-getpid()是典型的例子-但它们很少。 (好吧:并不是那么少-getppid()是另一个例子,它在手册中非常接近getpid();getuid()和相关函数也不远。)
多线程应用程序是一个问题;在这些情况下使用chdir()不是一个好主意。您可能需要fork()并使子进程评估目录名称,然后以某种方式将其传递回父进程。
bignose问道:
这很有趣,但似乎与查询者报告的经验相矛盾:getcwd知道如何从fd获取路径。这表明系统至少在某些情况下知道如何从fd到路径;您能否编辑您的答案以解决此问题?
为此,了解一种机制-或至少是一种-通过该机制,getcwd()函数可以编写。忽略“没有权限”的问题,它工作的基本机制是:
- 在根目录'/'上使用stat(这样您就知道何时停止向上移动)。
- 在当前目录'.'上使用stat(这样您就知道自己在哪里);这会为您提供当前的inode。
- 直到达到根目录:
- 扫描父目录'..',直到找到与当前inode相同的条目;这会为您提供目录路径的下一个组件名称。
- 然后将当前inode更改为父目录中'.'的inode。
- 当您到达根目录时,您可以构建路径。
以下是该算法的实现。这是旧代码(最初为1986年;最后一次非美容性更改是1998年),它没有像应该那样使用fchdir()
。如果您要遍历NFS自动安装的文件系统,则它也会工作得很糟糕-这就是为什么我不再使用它的原因。但是,这大致相当于getcwd()
使用的基本方案。(哦,我看到了一个18个字符的字符串(“../123456789.abcd”)-好吧,在编写它时,我所使用的机器只有非常旧的14个字符文件名-而不是现代的flex名称。就像我说的那样,这是旧代码!我大约15年或更长时间没有见过这些文件系统了。还有一些用于处理更长名称的代码。使用时要小心。)
#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
const char jlss_id_getpwd_c[] = "@(#)$Id: getpwd.c,v 2.5 2008/02/11 08:44:50 jleffler Exp $";
#endif
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);
}
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);
if ((d_entry = readdir(dp)) == 0 ||
(d_entry = readdir(dp)) == 0)
{
closedir(dp);
return(-1);
}
status = 1;
while (status)
{
if ((d_entry = readdir(dp)) == 0)
{
closedir(dp);
return(-1);
}
else if ((d_inode = inode_number("..", d_entry->d_name)) != 0 &&
(dotdot.st_dev != device))
{
dst = &name[3];
src = d_entry->d_name;
while ((*dst++ = *src++) != '\0')
;
if (stat(name, &file))
{
continue;
}
status = (file.st_ino != inode || file.st_dev != device);
}
else
{
status = (d_inode != inode);
}
}
strncpy(d_name, d_entry->d_name, DIRSIZ);
closedir(dp);
src = d_name;
dst = path;
end = path + plen;
if (dotdot.st_ino == root.st_ino && dotdot.st_dev == root.st_dev)
{
status = 0;
if (dst < end)
*dst++ = '/';
while (dst < end && (*dst++ = *src++) != '\0')
;
}
else if (chdir(".."))
status = -1;
else
{
status = finddir(dotdot.st_ino, dotdot.st_dev, path, plen);
(void)chdir(d_name);
if (status == 0)
{
while (*dst)
dst++;
if (dst < end)
*dst++ = '/';
while (dst < end && (*dst++ = *src++) != '\0')
;
}
}
if (dst >= end)
status = -1;
return(status);
}
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>
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
fchdir
和getcwd
之间重命名目录,或者在getcwd
和将结果传递给其他函数之间重命名目录,预期的行为是什么?您仍将拥有该目录的有效文件描述符,但是您获取的名称将不匹配。 - bignosefchdir
之后,您可以调用getcwd
并获得有效路径。无论它如何进行映射,映射都会发生。 - tylerl