# 경로 탐색: 이름을 찾는 동안 다른 프로세스가 이름을 바꾸면

v6.6 / fs/namei.c

경로 문자열을 inode로 연결하려면 각 디렉터리에서 다음 이름을 찾아야 합니다. 자주 찾는 이름은 dentry 캐시에 있지만, 다른 태스크가 같은 순간 이름을 바꾸거나 항목을 무효화할 수 있습니다. lookup_fast는 캐시에서 빠르게 찾은 결과를 그대로 믿지 않고 일관성을 확인하며, 필요하면 더 안전한 탐색 방식으로 바꿉니다.

## lookup_fast

```c

static struct dentry *lookup_fast(struct nameidata *nd)
{
	struct dentry *dentry, *parent = nd->path.dentry;
	int status = 1;

	/*
	 * Rename seqlock is not required here because in the off chance
	 * of a false negative due to a concurrent rename, the caller is
	 * going to fall back to non-racy lookup.
	 */
	if (nd->flags & LOOKUP_RCU) {
		dentry = __d_lookup_rcu(parent, &nd->last, &nd->next_seq);
		if (unlikely(!dentry)) {
			if (!try_to_unlazy(nd))
				return ERR_PTR(-ECHILD);
			return NULL;
		}

		/*
		 * This sequence count validates that the parent had no
		 * changes while we did the lookup of the dentry above.
		 */
		if (read_seqcount_retry(&parent->d_seq, nd->seq))
			return ERR_PTR(-ECHILD);

		status = d_revalidate(dentry, nd->flags);
		if (likely(status > 0))
			return dentry;
		if (!try_to_unlazy_next(nd, dentry))
			return ERR_PTR(-ECHILD);
		if (status == -ECHILD)
			/* we'd been told to redo it in non-rcu mode */
			status = d_revalidate(dentry, nd->flags);
	} else {
		dentry = __d_lookup(parent, &nd->last);
		if (unlikely(!dentry))
			return NULL;
		status = d_revalidate(dentry, nd->flags);
	}
	if (unlikely(status <= 0)) {
		if (!status)
			d_invalidate(dentry);
		dput(dentry);
		return ERR_PTR(status);
	}
	return dentry;
}

```

### 1618행

```c

static struct dentry *lookup_fast(struct nameidata *nd)

```

진행 중인 경로 탐색 상태 nd를 받아 현재 이름의 dentry를 캐시에서 찾습니다.

### 1620행

```c

	struct dentry *dentry, *parent = nd->path.dentry;

```

결과 포인터와 현재 부모 dentry를 준비합니다. 부모는 nd의 현재 경로에서 얻습니다.

### 1621행

```c

	int status = 1;

```

기본 재검증 상태를 유효하다는 양수로 준비합니다.

### 1628행

```c

	if (nd->flags & LOOKUP_RCU) {

```

현재 탐색이 참조 획득 비용을 줄이는 RCU 모드인지 확인합니다.

### 1629행

```c

		dentry = __d_lookup_rcu(parent, &nd->last, &nd->next_seq);

```

부모와 마지막 이름으로 캐시를 조회하고 찾은 자식의 변경 순번도 보관합니다.

### 1630행

```c

		if (unlikely(!dentry)) {

```

캐시에서 찾지 못한 드문 경우를 처리합니다. unlikely는 확률 힌트입니다.

### 1631행

```c

			if (!try_to_unlazy(nd))

```

RCU 방식에서 참조를 확보하는 방식으로 안전하게 전환할 수 있는지 시도합니다.

### 1632행

```c

				return ERR_PTR(-ECHILD);

```

전환 중 상태를 확정할 수 없으면 -ECHILD 오류 포인터로 상위의 재탐색을 요청합니다.

### 1633행

```c

			return NULL;

```

전환에 성공했지만 캐시 항목이 없으므로 NULL을 돌려 느린 조회가 이어지게 합니다.

### 1640행

```c

		if (read_seqcount_retry(&parent->d_seq, nd->seq))

```

캐시 조회 동안 부모 dentry가 변경됐는지 순번으로 검사합니다.

### 1641행

```c

			return ERR_PTR(-ECHILD);

```

부모가 바뀌었다면 결과를 확정하지 않고 재탐색 신호를 반환합니다.

### 1643행

```c

		status = d_revalidate(dentry, nd->flags);

```

찾은 dentry와 현재 탐색 플래그로 파일시스템 재검증을 요청합니다. 이 버전의 d_revalidate에는 부모 inode나 이름 인자를 별도로 넘기지 않습니다.

### 1644행

```c

		if (likely(status > 0))

```

유효하다는 양수 결과가 가장 흔한 경로입니다.

### 1645행

```c

			return dentry;

```

RCU 탐색 상태를 유지한 채 찾은 dentry를 반환합니다.

### 1646행

```c

		if (!try_to_unlazy_next(nd, dentry))

```

재검증이 끝나지 않은 경우 다음 dentry까지 포함해 참조 기반 탐색으로 전환합니다.

### 1647행

```c

			return ERR_PTR(-ECHILD);

```

필요한 참조를 안전하게 확보하지 못하면 상위에서 다시 탐색하도록 알립니다.

### 1648행

```c

		if (status == -ECHILD)

```

파일시스템이 RCU가 아닌 방식의 재검증을 요구했는지 검사합니다.

### 1650행

```c

			status = d_revalidate(dentry, nd->flags);

```

RCU 모드를 벗어나 다시 검증해야 한다는 요청에 따라 같은 dentry를 변경된 탐색 플래그로 재검증합니다.

### 1651행

```c

	} else {

```

처음부터 RCU 모드가 아니었던 탐색 경로입니다.

### 1652행

```c

		dentry = __d_lookup(parent, &nd->last);

```

일반 참조 기반 dentry 조회를 수행합니다.

### 1653행

```c

		if (unlikely(!dentry))

```

이 방식에서도 캐시 항목을 찾지 못했는지 확인합니다.

### 1654행

```c

			return NULL;

```

캐시 미스로 NULL을 반환하여 후속 탐색으로 넘깁니다.

### 1655행

```c

		status = d_revalidate(dentry, nd->flags);

```

일반 참조 기반 조회에서 얻은 dentry가 유효한지 현재 탐색 플래그와 함께 확인합니다.

### 1657행

```c

	if (unlikely(status <= 0)) {

```

재검증이 무효 0 또는 음수 오류를 반환한 경우를 처리합니다.

### 1658행

```c

		if (!status)

```

음수 오류가 아니라 캐시 항목 자체가 무효라는 0인지 구분합니다.

### 1659행

```c

			d_invalidate(dentry);

```

해당 dentry를 무효화하여 낡은 결과가 다시 사용되지 않게 합니다.

### 1660행

```c

		dput(dentry);

```

이 경로가 확보한 dentry 참조를 내려놓습니다.

### 1661행

```c

		return ERR_PTR(status);

```

음수면 오류 포인터를, 0이면 NULL을 반환합니다. ERR_PTR(0)은 NULL이라는 점이 중요합니다.

### 1663행

```c

	return dentry;

```

유효성 검사를 통과한 dentry를 반환합니다.

