ข้อกำหนด POSIX File Permissions และ System Calls สำหรับการจัดการไฟล์ คือรากฐานของระบบความปลอดภัยและการเข้าถึงทรัพยากรระดับล่าง (Low-level I/O) บนระบบปฏิบัติการตระกูล Unix-like ทั้งหมด โดยเชื่อมโยงระหว่างบิตข้อมูลใน Inode, การตรวจสอบสิทธิ์ของเคอร์เนล และ API ภาษา C
โครงสร้าง POSIX File Permissions (File Mode Bits)
ในระดับเคอร์เนล POSIX กำหนดให้สิทธิ์และชนิดของไฟล์ถูกเก็บอยู่ในตัวแปรประเภท mode_t (ขนาดอย่างน้อย 16 บิต) ภายในโครงสร้าง struct stat โดยแบ่งบิตออกเป็นส่วนๆ ดังนี้:
Plaintext
15 14 13 12 | 11 10 9 | 8 7 6 | 5 4 3 | 2 1 0 (Bit index)
[ File Type ] [ Special ] [ Owner ] [ Group ] [ Other ]
1. ค่าสิทธิ์มาตรฐาน (rwx)
- Read (
r/ 4)
อ่านเนื้อหาไฟล์ หรืออ่านรายชื่อไฟล์ใน Directory ผ่านreaddir() - Write (
w/ 2)
แก้ไข/เขียนทับเนื้อหาไฟล์ หรือสร้าง/ลบ/เปลี่ยนชื่อไฟล์ภายใน Directory - Execute (
x/ 1)
รันโปรแกรมไบนารี/สคริปต์ หรือสิทธิ์การ “traverse” (เดินข้ามผ่าน) Directory เพื่อเข้าถึงไฟล์ข้างใน
2. Special Permissions (บิตพิเศษ 3 บิต)
| บิตพิเศษ | Octal | สัญลักษณ์ | พฤติกรรมบน Regular File | พฤติกรรมบน Directory |
SetUID (S_ISUID) | 4000 | s (Owner) | โปรเซสรันด้วยสิทธิ์ Effective UID ของเจ้าของไฟล์ | ไม่มีผลตามมาตรฐาน POSIX |
SetGID (S_ISGID) | 2000 | s (Group) | โปรเซสรันด้วยสิทธิ์ Effective GID ของกลุ่มไฟล์ | ไฟล์ใหม่ที่สร้างใต้ Directory นี้จะสืบทอด Group ID จากไดเรกทอรี |
Sticky Bit (S_ISVTX) | 1000 | t (Other) | ไม่มีผลในระบบสมัยใหม่ (อดีตเคยใช้แคช Text segment) | ผู้ใช้จะลบหรือเปลี่ยนชื่อไฟล์ได้ก็ต่อเมื่อเป็นเจ้าของไฟล์นั้น หรือเจ้าของ Directory (เช่น /tmp) |
อัลกอริทึมการตรวจสอบสิทธิ์ของ Kernel (Permission Check Algorithm)
เมื่อโปรเซสเรียก System Call เพื่อเข้าถึงไฟล์ เคอร์เนลจะตรวจสอบตามลำดับ จากบนลงล่าง และหยุดทันทีที่ตรงเงื่อนไขแรก (First match wins)
- Root Override
หาก Effective UID เป็น0(Superuser) จะได้รับอนุญาตผ่านเกือบทุกกรณี (ยกเว้น Execute ที่ต้องการบิตxอย่างน้อย 1 จุด) - Owner Check
หาก Effective UID ของโปรเซสตรงกับ Owner UID ของไฟล์ -> ตรวจสอบเฉพาะชุดสิทธิ์ Owner เท่านั้น (หาก Owner บล็อกสิทธิ์ แม้ Group จะอนุญาต โปรเซสจะถูกปฏิเสธทันทีด้วยข้อผิดพลาดEACCES) - Group Check
หากตรงกับ Owner GID หรือตรงกับหนึ่งใน Supplementary Group IDs ของโปรเซส -> ตรวจสอบเฉพาะชุดสิทธิ์ Group - Other Check
หากไม่เข้าเงื่อนไขข้างต้น -> ตรวจสอบชุดสิทธิ์ Other
ระบบ POSIX System Calls พื้นฐานสำหรับ File I/O
POSIX กำหนด System Calls ผ่าน Header <fcntl.h>, <unistd.h>, และ <sys/stat.h> โดยส่งคืนผลลัพธ์เป็น File Descriptor (FD) ซึ่งเป็นตัวเลขจำนวนเต็มชี้ไปยัง File Table Entry ใน Kernel Space
Plaintext
User Process Kernel Space
+---------------+ +-----------------------+ +-------------------+
| FD Table | ------> | Open File Table | ------> | V-node / Inode |
| 0: stdin | | - Status flags (R/W) | | - File size |
| 1: stdout | | - Current offset | | - Mode & Owners |
| 3: myfile.txt| +-----------------------+ +-------------------+
+---------------+
1. open / openat – การเปิดหรือสร้างไฟล์
C
int open(const char *path, int oflag, ... /* mode_t mode */);
- Flags หลัก (
oflag)- โหมดการเข้าถึง (ต้องเลือก 1 อย่าง)
O_RDONLY,O_WRONLY,O_RDWR - Action flags (รวมกันด้วย Bitwise OR)
O_CREAT(สร้างไฟล์ถ้าไม่มี),O_TRUNC(ล้างข้อมูลเดิมให้เหลือ 0),O_APPEND(เลื่อน offset ไปท้ายไฟล์ก่อนเขียนเสมอ),O_CLOEXEC(ปิด FD อัตโนมัติเมื่อเรียกexec)
- โหมดการเข้าถึง (ต้องเลือก 1 อย่าง)
- Mode
จำเป็นต้องระบุเมื่อมีO_CREAT(เช่น0644หรือS_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) โดยค่าสิทธิ์จริงที่ถูกสร้างจะคำนวณผ่าน:$$\text{Permissions} = \text{mode} \ \& \ (\sim\text{umask})$$
2. read – การอ่านข้อมูล
C
ssize_t read(int fildes, void *buf, size_t nbyte);
- พยายามอ่านข้อมูลจำนวน
nbyteจากตำแหน่ง Offset ปัจจุบันเข้าไปยังบัฟเฟอร์buf - Return Value
> 0
จำนวนไบต์ที่อ่านได้จริง (อาจน้อยกว่าnbyte)0
ถึงจุดสิ้นสุดของไฟล์ (End of File – EOF)-1
เกิดข้อผิดพลาด ตรวจสอบสาเหตุผ่านตัวแปรerrno(เช่นEINTR,EIO,EBADF)
3. write – การเขียนข้อมูล
C
ssize_t write(int fildes, const void *buf, size_t nbyte);
- เขียนข้อมูลจำนวน
nbyteจากbufลงในไฟล์ - Return Value
จำนวนไบต์ที่เขียนสำเร็จ หรือ-1เมื่อเกิด Error (เช่นENOSPCกรณีดิสก์เต็ม) - การเขียนผ่าน
writeปกติจะค้างอยู่ใน Page Cache ของ Kernel ยังไม่ลง Disk ทันทีจนกว่าจะเรียกfsync(fildes)
4. lseek – การเปลี่ยนตำแหน่ง Read/Write Offset
C
off_t lseek(int fildes, off_t offset, int whence);
whenceDirective:SEEK_SET
เลื่อนไปที่ตำแหน่งoffsetโดยนับจากจุดเริ่มต้นของไฟล์SEEK_CUR
เลื่อนไปจากตำแหน่งปัจจุบันบวกด้วยoffsetSEEK_END
เลื่อนไปจากจุดสิ้นสุดของไฟล์บวกด้วยoffset(สามารถเลื่อนเลยขนาดไฟล์จริงเพื่อสร้าง Sparse File ได้)
5. close – การคืนทรัพยากร File Descriptor
C
int close(int fildes);
- ปลดปล่อย File Descriptor คืนสู่ตาราง เพื่อนำกลับไปใช้ใหม่ ป้องกันปัญหา File Descriptor Leak (จำกัดตามขีดจำกัด
RLIMIT_NOFILE)
ตัวอย่างการทำงานจริง: การสร้างไฟล์แบบกำหนดสิทธิ์และเขียนข้อมูล (POSIX C)
C
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
int main(void) {
const char *path = "secure_output.txt";
const char *data = "Classified system information\n";
// กำหนดสิทธิ์: เฉพาะ Owner เท่านั้นที่อ่านและเขียนได้ (0600)
mode_t file_mode = S_IRUSR | S_IWUSR;
// สั่งเปิดไฟล์: ถ้าไม่มีให้สร้าง, ถ้ามีให้เขียนทับ, ป้องกันการรั่วไหลไปยัง Child process
int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, file_mode);
if (fd == -1) {
perror("Error opening file");
return 1;
}
size_t bytes_to_write = strlen(data);
ssize_t bytes_written = write(fd, data, bytes_to_write);
if (bytes_written == -1) {
perror("Error writing to file");
close(fd);
return 1;
}
// บังคับ Flush ข้อมูลและ Metadata จาก Page Cache ลง Disk
if (fsync(fd) == -1) {
perror("Error syncing file to disk");
}
if (close(fd) == -1) {
perror("Error closing file");
return 1;
}
printf("File created and written successfully with mode 0600\n");
return 0;
}
ความแตกต่างระหว่าง Low-level I/O และ C Standard Library
| คุณสมบัติ | POSIX System Calls (open, read, write) | C Standard I/O (fopen, fread, fwrite) |
| Interface Handle | File Descriptor (จำนวนเต็ม int) | Stream Pointer (FILE *) |
| Buffering | ไม่มี Buffer ฝั่ง User space (เรียก Trap สู่ Kernel ทันที) | มี User-space Buffer ช่วยลด Overhead ของ System Call |
| Portability | เฉพาะระบบที่รองรับมาตรฐาน POSIX | ทุกระบบที่มี C Runtime (รวมทั้ง Bare-metal และ Windows ทั่วไป) |
| Use Cases | งานระดับระบบ, IPC, Sockets, ควบคุม Permission, Direct I/O | งานประมวลผลข้อความทั่วไป, ไฟล์ Format, งานข้ามแพลตฟอร์ม |