# name\_to\_handle\_at && open\_by\_handle\_at

**URL:** <https://sysec.cc/t/topic/32>\
**Category:** Linux/Unix\
**Created:** [2026 年8 月 16 日 12:20 UTC](https://sysec.cc/t/topic/32 "2026-08-16T12:20:46Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

作者： ![EBK](https://sysec.cc/user_avatar/sysec.cc/ebk/32/7_2.png) [@EBK](https://sysec.cc/u/EBK)\
发布日期： [2026 年8 月 16 日 12:20 UTC](https://sysec.cc/t/topic/32/1 "2026-08-16T12:20:46Z")

</div>

# 场景

如果我们对某个文件在系统中建立了非常多的硬链接，而我们需要清理掉他们。  
例如像是关闭基于硬链接的反勒索诱捕能力这种场景。

# 问题

此时大部分人可能和我最初想的一样，使用：`find ./ -inum 【inode号】 -exec rm -rf {} \;`  
但此时如果面临的是几十上百万的文件系统呢？上面这种方式是非常浪费时间和性能的。  
即使你在建立硬链接的过程中，记录了每个硬链接的存储位置，在一些目录被重命名的情况下，又会出现缓存失效的问题。  
最后就会导致文件系统一地鸡毛……

# 解决方案

所以有没有一种性能又高，还能无视路径被重命名的方式呢？  
有的有的！

此时就要用到 `name_to_handle_at` 和 `open_by_handle_at` 这对孪生兄弟了。  
相较于 `find` 命令基于应用层文件系统 `自顶向下` 的遍历，这对孪生兄弟则是在内核中通过 `inode` 信息进行 `自底向上` 的遍历，速度极快。  
使用这两个接口，仅需要满足三个条件：

- 一：至少要知道一个硬链接文件所在位置，用于提取句柄信息；
- 二：要知道硬链接所在分区路径，这个简单的 `df` 命令就可以看到，况且不通的分区不允许互相搭建硬链接；
- 三：需要 `CAP_DAC_READ_SEARCH` 权限，这个用 `root` 权限即可无视；

# 使用说明

其中 `name_to_handle_at` 主要是用来获取文件句柄信息；

```c
    struct file_handle *fhp = (struct file_handle *)malloc(sizeof(struct file_handle) + 128);
    fhp->handle_bytes = 128;

    // 获取目标文件的底层句柄
    if (name_to_handle_at(AT_FDCWD, target_path, fhp, &mount_id, 0) == -1) {
        perror("name_to_handle_at 失败");
        free(fhp);
        exit(EXIT_FAILURE);
    }

```

`open_by_handle_at` 则是利用文件句柄信息去打开文件。

```c
    // 打开挂载点文件描述符（一般传文件所在分区挂载路径，例如 /、/home/、/data……）
    int mount_fd = open(mount_point, O_RDONLY | O_DIRECTORY);
    if (mount_fd == -1) {
        perror("打开挂载点失败");
        free(fhp);
        exit(EXIT_FAILURE);
    }

    // 通过底层句柄直接打开文件 (使用 O_PATH 轻量级打开以提高性能，不需要读写权限)
    int fd = open_by_handle_at(mount_fd, fhp, O_PATH);
    if (fd == -1) {
        perror("open_by_handle_at 失败，文件可能已被删除或被跨分区移动");
        close(mount_fd);
        free(fhp);
        exit(EXIT_FAILURE);
    }

```

在文件成功打开之后，就可以用`readlink`去查看"/proc/self/fd/【被打开的fd】"所指向的路径了。

```c
    char fd_path[64];
    char real_path[PATH_MAX];
    snprintf(fd_path, sizeof(fd_path), "/proc/self/fd/%d", fd);
	
    ssize_t len = readlink(fd_path, real_path, sizeof(real_path) - 1);
    if (len == -1) {
        perror("获取路径失败");
        close(fd); close(mount_fd); free(fhp);
        exit(EXIT_FAILURE);
    }
    real_path[len] = '\0';

```

**如上查寻删除后，循环往复，便达到了删除所有硬链接的目的**  
**如果仅仅是查寻，可能需要使用先删除再搭建硬链接的方式了**  
**因为该方法在不删除 fd 指向的目标文件的情况下，也无法获取其它硬链接路径……**
