inode에는 파일 이름이 없다 — 파일 시스템이 감추는 것

"Permission denied"가 떴다. 파일 권한을 고쳤는데도 안 된다. 왜? 종종 진짜 원인은 파일이 아니라 부모 디렉토리에 있다. 또 디스크에 공간이 남았는데 파일이 안 만들어진다 — 이번엔 inode가 다 떨어졌기 때문이다. 이런 일들은 파일 시스템이 실제로 어떻게 돌아가는지를 모르면 진단할 수 없다.

리눅스는 "모든 것은 파일"이라고 한다. 그런데 디스크는 그냥 0과 1의 블록 덩어리다. 이 블록들을 어떻게 "이름 있는 파일"로 조직하고, 누가 접근할지 통제하는가 — 이 글이 푸는 질문이다.

"모든 것은 파일" — VFS 추상화

리눅스는 디스크 파일, 하드웨어 장치, 프로세스 정보, 네트워크 소켓까지 모두 파일로 다룬다. 그래서 같은 도구(cat, ls, grep)로 모든 것을 조작할 수 있다. 이 통일을 가능하게 하는 커널 계층이 VFS(Virtual File System)다.

flowchart TB
    APP[응용 프로그램<br/>open/read/write] --> VFS[VFS 계층<br/>공통 인터페이스]
    VFS --> XFS[XFS 드라이버]
    VFS --> EXT[ext4 드라이버]
    VFS --> PROC[procfs/proc]
    VFS --> SYS[sysfs/sys]
    VFS --> DEV[devtmpfs/dev]

VFS는 각 파일 시스템(XFS/ext4/proc/sysfs 등)의 차이를 감추고 동일한 시스템 콜을 제공한다. 덕분에 cat/ls가 XFS든 procfs든 같은 방식으로 동작하고, /proc/meminfocat으로 읽는 것이 가능하다.

inode에는 이름이 없다 — 도서관 카드 카탈로그

많은 초보자가 "inode에 파일 이름이 들어있겠지"라고 생각한다. 틀렸다. inode에는 파일 이름이 없다. 이름은 디렉토리가 보관한다(이름 → inode 번호 매핑 표).

  파일 시스템 도서관
파일 이름 "report.pdf" 책 제목
inode 메타데이터 + 데이터 블록 포인터 카드 카탈로그(위치·저자·페이지 수)
데이터 블록 실제 파일 내용 실제 책
디렉토리 이름 → inode 매핑 표 색인함(제목 → 카드 위치)

도서관 비유는 "이름은 카드에 없고 색인함에 있다"는 관계만 잡는다. 파일 시스템은 같은 inode에 이름 여러 개(하드 링크)가 가능하고, 디스크는 블록 단위 비연속 할당이라는 점에서 비유를 넘어선다.

이름이 inode가 아니라 디렉토리에 있다는 설계 덕분에:

  • 같은 inode에 여러 이름(하드 링크)을 가질 수 있다.
  • 파일 이름 바꾸기(mv)는 데이터 복사 없이 디렉토리 항목만 변경 → 즉시 완료.

구조를 보자:

flowchart LR
    BS[Boot Sector] --> SB[Superblock<br/>전체 메타정보]
    SB --> IT[Inode Table<br/>파일 메타데이터]
    IT --> DB[Data Blocks<br/>실제 파일 내용]
  • Superblock: 파일 시스템 전체 정보(크기, 타입, 마운트 상태). 손상 시 복구 대상.
  • inode: 파일 1개당 1개. 권한·소유자·크기·타임스탬프 + 데이터 블록 포인터.
  • Data block: 실제 파일 데이터.

inode 고갈의 함정: 디스크 여유 공간이 있어도 inode가 다 떨어지면 파일 생성이 안 된다(df -i로 확인). 작은 파일 수십만 개(메일 큐 등)가 있는 시스템에서 발생한다. "공간은 있는데 파일이 안 만들어진다"의 원인.

XFS vs ext4 — Rocky 10의 기본은 XFS

항목 XFS(RHEL 10 기본) ext4
개발 SGI, 1990년대 초 리눅스 전통(ext2/3 진화)
확장 성장만 가능(grow), 축소 불가 성장/축소(축소는 오프라인) 모두
inode 동적 할당, 64-bit(2^32 한계 없음) 고정, 2^32 한계
대용량/IOPS 대형 파일·고동시성에 강 단일 스레드/소규모에 적합
특수 기능 reflink(COW 복사), xattr, project quota 익숙함, 단순
생성/복구 mkfs.xfs / xfs_repair mkfs.ext4 / e2fsck

특별한 이유 없으면 XFS(RHEL 기본)를 써라. 현대 서버는 대용량·고동시성이고, XFS는 대형 파일(수백 MB 이상)·수천 IOPS 환경에서 ext4보다 일관되게 우수하다. 또 reflink(COW 복사)로 스냅샷·백업이 효율적이다. 단, 축소 불가는 단점 — 줄이려면 백업→재생성해야 한다.

FHS — 디렉토리 계층 표준

리눅스 디렉토리 구조는 FHS(Filesystem Hierarchy Standard)로 표준화되어 있다. 핵심 디렉토리:

경로 용도
/ 최상위 루트
/bin, /sbin 기본 명령. RHEL 8+부터 /usr/bin, /usr/sbin으로 심볼릭 통합
/boot 커널·initramfs·GRUB 부트 파일
/etc 시스템 설정 파일(editable text configuration)
/usr UNIX System Resources — 사용자 명령·라이브러리·문서
/var 가변 데이터 — 로그, 스풀, 메일
/home, /root 일반 사용자 홈 / root 홈
/tmp 임시 파일(부팅 시 초기화 가능)
/dev 장치 파일(블록/문자 장치)
/proc 가상 fs — 커널/프로세스 정보(/proc/<pid>/, /proc/meminfo)
/sys 가상 fs — 하드웨어/커널 모듈 정보(sysfs)
/mnt, /media 임시 마운트 / 이동식 미디어
/run 런타임 데이터(부팅마다 초기화, 과거 /var/run)

권한 — 9비트가 누가 뭘 할 수 있는지를 결정한다

각 파일/디렉토리는 3개 주체 × 3개 권한 = 9비트(+특수 3비트)로 통제된다.

주체 기호   권한 파일 디렉토리
소유자 u   r(4) 내용 읽기 ls(목록 조회)
그룹 g   w(2) 내용 수정 파일 생성/삭제
그 외 o   x(1) 실행 진입(cd)

8진수: rwx = 4+2+1 = 7. 예: 755 = rwxr-xr-x.

디렉토리 x권한의 비밀

초보자가 자주 틀리는 부분: "디렉토리에 x가 있으면 실행? 이상하다." — 디렉토리에서 x'진입'(cd) 권한이다. 디렉토리에 r만 있고 x가 없으면 ls는 되지만 cd/내부 파일 접근이 안 된다. r은 목록 보기, x는 들어가기.

특수 비트

비트 기호 8진수 자리 효과
setuid s(user 자리) 첫 자리 4 실행 시 소유자 권한으로 동작(예: /usr/bin/passwd)
setgid s(group 자리) 첫 자리 2 파일: 그룹 권한으로 실행. 디렉토리: 새 파일이 디렉토리 그룹 상속
sticky t(others 자리) 첫 자리 1 공유 디렉토리에서 본인 소유 파일만 삭제(/tmp가 대표)

4자리 8진수: 4755 = setuid + rwxr-xr-x. 1777 = sticky + rwxrwxrwt(= /tmp).

파일 삭제 권한의 함정 — 도입부의 수수께끼 풀기

파일을 지우려면 파일 자체의 w 비트가 아니라 부모 디렉토리의 w 비트가 필요하다. 읽기 전용 파일(-r--r--r--)도 부모 디렉토리에 w가 있으면 지울 수 있다. "Permission denied"의 진짜 원인이 파일이 아니라 디렉토리인 이유다. /tmp는 sticky 비트 덕분에 남의 파일을 못 지운다.

마운트, fstab, 링크

파일 시스템을 디렉토리 트리에 연결하는 게 마운트다. /etc/fstab에 자동 마운트를 정의한다:

# 장치              마운트포인트  타입  옵션        dump fsck
/dev/mapper/rl-root /             xfs   defaults    0    0
UUID=...           /boot         xfs   defaults    0    0
/dev/mapper/rl-swap none         swap  defaults    0    0

컬럼 6개: 장치 / 마운트포인트 / 타입 / 옵션 / dump / fsck 순서. 부팅 시 자동 마운트되며, 변경 후엔 sudo mount -a로 검증한다.

하드 링크 vs 심볼릭 링크

항목 하드 링크 심볼릭 링크
명령 ln 원본 링크 ln -s 원본 링크
본질 같은 inode를 가리키는 또 하나의 이름 원본 경로를 담은 별개의 파일
inode 원본과 동일 다름(자체 inode)
원본 삭제 데이터 유지(링크 남음) 깨진 링크(dangling)
디렉토리 불가 가능
다른 fs 불가 가능

"inode에 이름이 없다"를 떠올려라. 하드 링크는 같은 inode에 이름을 하나 더 추가하는 것이다. 그래서 원본을 "지워도" inode 참조가 남아있으면 데이터는 살아있다. 단, 하드 링크는 백업이 아니다 — 같은 inode를 가리킬 뿐, 복사가 아니다.

Rocky 10에서 직접 확인하기

미검증(출처 인용). Rocky 10 VM에서 실행 권장.

df -hT            # -h 인간판독, -T 타입
df -i             # inode 사용량(공간 있는데 안 만들어지면 확인)
du -sh /var/log   # 특정 디렉토리 합계
Filesystem              Type   Size  Used Avail Use% Mounted on
/dev/mapper/rl-root     xfs     17G  4.5G   13G  27% /
# Rocky 10 — inode 직접 확인: 이름은 inode에 없다
ls -i /etc/hosts            # inode 번호
stat /etc/hosts             # 메타데이터 전체
  File: /etc/hosts
  Inode: 1234567     Links: 1
  Access: (0644/-rw-r--r--)  Uid: (0/root)  Gid: (0/root)

확인할 것: stat 출력에 파일 이름은 없다. inode는 번호로만 존재한다.

# Rocky 10 — 권한 관리
chmod 755 script.sh          # 8진수
chmod u+x,g-w file.txt       # 기호
chmod -R 644 /var/www        # 재귀
sudo chown user:group file   # 소유권 변경
sudo chmod 4755 /usr/bin/myapp    # setuid
sudo chmod 1777 /shared           # sticky (=/tmp처럼)
umask                            # 신규 파일 기본 권한 마스크(보통 022)
# Rocky 10 — 마운트 관리
findmnt                       # 트리 형태
sudo mount /dev/sdb1 /mnt     # 수동 마운트
sudo mount -a                 # fstab 수정 후 문법 검증
# Rocky 10 — 하드 vs 심볼릭 체험
ln /etc/file /root/hardlink       # 하드(같은 inode)
ln -s /etc/file /root/softlink    # 심볼릭(다른 inode)
ls -li /root/                     # inode 확인(-i)
# Rocky 10 — setuid 파일 점검(보안)
find / -perm -4000 -type f 2>/dev/null   # setuid 파일(정기 점검 권장)
find /var -name "*.log" -mtime -1        # 1일 이내 .log
find /home -size +100M                    # 100MB 초과

흔히 묻는 것, 흔히 틀리는 것

오해 정정
"디렉토리에 x는 의미 없다" 진입(cd) 권한. r만 있고 x 없으면 내부 접근 불가
"파일 지우려면 파일 w 필요" 부모 디렉토리 w 필요. 파일 자체는 읽기전용이어도 디렉토리 w 있으면 지움
"inode에 파일 이름이 있다" 없다. 이름은 디렉토리가 보관. 그래서 하드링크 가능
"mv는 데이터를 복사한다" 같은 파일 시스템 내에선 inode 그대로, 디렉토리 항목만 변경 → 즉시
"XFS는 ext4보다 무조건 좋다" 대용량/고동시성에 강하지만 축소 불가, 메타데이터 CPU 더 씀
"inode 고갈은 안 일어난다" 작은 파일 수백만 개(메일 큐) 시스템에선 발생. df -i 확인
"/bin/usr/bin은 다르다" RHEL 8+부턴 /bin/usr/bin 심볼릭. 사실상 같음
"하드 링크는 백업이다" 같은 inode 가리킬 뿐. 복사 아님. rsync/reflink이 진짜 복사

더 깊이

  • setuid의 보안 위험: setuid 파일은 권한 승격 통로다. /usr/bin/passwd(일반 사용자가 root 권한으로 /etc/shadow 변경)가 정당한 사례지만, setuid가 걸린 의심스러운 바이너리는 침해 통로. find / -perm -4000로 정기 점검하라.
  • reflink(XFS COW 복사): cp --reflink=always는 데이터 복사 없이 같은 블록을 참조(COW). 스냅샷·백업이 거의 즉시. Stratis 같은 스냅샷 기반 백업 솔루션의 기반.
  • atime/noatime: 파일 접근 시간(atime) 기록이 매 읽기마다 디스크 쓰기를 유발. 성능 중요 서버에선 noatime으로 끈다. RHEL 최근 기본은 relatime(필요 시만 갱신).

요약 — 이 글의 결론

  • VFS: 모든 파일 시스템을 통일 인터페이스로 추상화. "모든 것은 파일"의 기반.
  • inode: 파일 1개당 1개. 메타데이터 + 데이터 블록 포인터. 이름은 inode에 없다(디렉토리가 보관) — 이 설계가 하드 링크·빠른 mv를 가능하게 한다.
  • XFS(RHEL 10 기본): 대용량/고동시성 강, reflink. 축소 불가.
  • 권한 9비트: rwx × (user/group/other). 디렉토리 x = 진입. 파일 삭제는 부모 디렉토리 w가 결정 — 이게 도입부 "Permission denied"의 진짜 원인.
  • 특수 비트: setuid(4), setgid(2), sticky(1). /tmp = sticky(1777)라 남의 파일 삭제가 안 된다.
  • inode 고갈: 공간이 있어도 파일 생성 불가. df -i로 확인.

생각해 볼 문제

  1. inode에 파일 이름이 있는가? 없다면 이름은 어디에 있고, 그 설계의 이점은?
  2. 디스크 공간이 있는데 파일 생성이 안 될 때 무엇을 확인하는가?
  3. 디렉토리에 r만 있고 x 없을 때 일어나는 일은?
  4. 읽기전용 파일(-r--r--r--)을 지울 수 있는가? 조건은?
  5. setuid, setgid, sticky 비트의 8진수 값과 효과를 말하라. /tmp는 왜 남의 파일 삭제가 안 되는가?
  6. 하드 링크와 심볼릭 링크의 차이. 원본 삭제 시 각각 어떻게 되나?
  7. VFS가 없다면 cat /proc/meminfo가 안 되는 이유는?

참고

'Tech Artifacts > Linux' 카테고리의 다른 글

Linux - 07. shell script  (0) 2026.07.09
Linux - 06. io device  (0) 2026.07.09
Linux - 04. memory  (0) 2026.07.09
Linux - 03. process  (0) 2026.07.09
Linux - 02. boot systemd  (0) 2026.07.09