Python os.fwalk: เดินต้นไม้ไฟล์พร้อม dir_fd เพื่อลด path race
os.walk คืนชื่อโฟลเดอร์และไฟล์ ส่วน os.fwalk เพิ่ม file descriptor ของไดเรกทอรีปัจจุบันมาด้วย ทำให้เราเรียก API ที่รองรับ dir_fd โดยอ้างชื่อไฟล์สั้น ๆ กับ “กุญแจของห้องนี้” แทนการประกอบ absolute path ใหม่ทุกครั้งครับ
WHY: ลดช่องว่างระหว่างดู path กับเปิดไฟล์
งาน filesystem บางแบบมีช่วงเวลาระหว่างตรวจชื่อกับใช้งานจริง ถ้ามี process อื่นเปลี่ยน symlink หรือย้าย path ระหว่างนั้น เราอาจเปิดคนละเป้าหมาย การผูก operation เข้ากับ descriptor ของไดเรกทอรีช่วยลดการพึ่ง path resolution จาก root ใหม่ทั้งหมด แม้ไม่ใช่ sandbox และไม่แทน permission check
import os
import tempfile
import unittest
class FwalkTests(unittest.TestCase):
def test_read_relative_to_current_directory_fd(self):
with tempfile.TemporaryDirectory() as root:
nested = os.path.join(root, "nested")
os.mkdir(nested)
with open(os.path.join(nested, "note.txt"), "w", encoding="utf-8") as handle:
handle.write("forest")
found = []
for _dirpath, _dirnames, filenames, dirfd in os.fwalk(root):
if "note.txt" not in filenames:
continue
fd = os.open("note.txt", os.O_RDONLY, dir_fd=dirfd)
try:
found.append(os.read(fd, 64).decode("utf-8"))
finally:
os.close(fd)
self.assertEqual(found, ["forest"])
if __name__ == "__main__":
unittest.main()
descriptor ที่ fwalk yield ใช้ได้ถึง iteration ถัดไปเท่านั้น หากต้องเก็บข้ามรอบให้ os.dup แล้วปิดสำเนาเอง เปรียบเหมือนกุญแจชั่วคราวที่เจ้าหน้าที่ส่งให้ดูห้องนี้ พอเดินไปห้องถัดไปกุญแจเดิมถูกเรียกคืน
ขอบเขตความปลอดภัย
dir_fd ลด path race บางชนิดแต่ไม่ได้ตรวจว่าเนื้อหาเชื่อถือได้ ไม่ได้ป้องกันไฟล์ใหญ่, device file หรือ permission ที่กว้าง และ API บางระบบปฏิบัติการไม่รองรับ descriptor-relative operation ควรเช็ก os.supports_dir_fd หากเขียน library ข้ามแพลตฟอร์ม
อย่าแก้ dirnames ระหว่าง fwalk แบบไม่เข้าใจ topdown และอย่าตาม symlink โดยไม่กำหนด policy เอกสารตั้ง follow_symlinks เป็น false โดยปริยาย ซึ่งเป็นค่าเริ่มต้นที่ปลอดภัยกว่าสำหรับ tree ที่ผู้ใช้อื่นแก้ได้
Recap
- ใช้ dir_fd กับชื่อ relative เมื่อ API รองรับ
- duplicate descriptor หากต้องเก็บเกิน iteration
- ปิด file descriptor ทุกตัวใน finally
- ยังคง validate file type, size และ permission