Chương 15 · Thuộc tính tệp Chapter 15 · File Attributes
Mỗi tệp mang theo một hồ sơ siêu dữ liệu — đọc nó, và đổi nó Mỗi tệp có một “tấm lý lịch” — cách xem và cách sửa nó Every file carries a metadata record — read it, and change it
Chương này khảo sát các thuộc tính của tệp (file metadata): kích thước, kiểu, quyền sở hữu, quyền truy cập và dấu thời gian. Kerrisk mở đầu bằng stat() — lời gọi hệ thống trả về một cấu trúc chứa phần lớn các thuộc tính ấy, hầu hết lấy thẳng từ i-node của tệp.1 Phần sau lần lượt giới thiệu các lời gọi dùng để thay đổi những thuộc tính đó, rồi khép lại bằng i-node flags (thuộc tính mở rộng kiểu ext2).1
Mỗi tệp không chỉ có nội dung, nó còn có một “tấm lý lịch”: to bao nhiêu, thuộc về ai, ai được đọc được sửa, lần cuối bị đụng vào khi nào. Chương này dạy cách đọc tấm lý lịch đó bằng lệnh stat(), và cách sửa từng mục trên đó. Nhớ nhé: nội dung tệp là một chuyện, lý lịch của tệp là chuyện khác — chương này nói về lý lịch.
This chapter investigates the attributes of files (file metadata): size, type, ownership, permissions, and timestamps. Kerrisk begins with stat() — the system call that returns a structure holding most of those attributes, mostly drawn straight from the file's i-node.1 He then introduces the calls used to change those attributes, and closes with i-node flags (ext2 extended file attributes).1
Một ý xuyên suốt chương: gần như mọi thuộc tính đều cư trú trong i-node, chứ không nằm trong tên tệp. Vì nhiều tên (hard link) có thể cùng trỏ về một i-node, mọi tên ấy chia sẻ chung một bộ quyền, một chủ sở hữu, một bộ dấu thời gian.1 Đó là lý do chmod() hay chown() tác động lên tệp (i-node) chứ không lên riêng một cái tên, và vì sao Chương 18 về liên kết (link) lại gắn bó chặt với chương này.3
Luận đề của chương: thuộc tính của một tệp — kiểu, kích thước, chủ, quyền, dấu thời gian — được lưu trong i-node; stat() đọc chúng ra một cấu trúc struct stat, còn một họ lời gọi chuyên biệt (utime(), chown(), chmod(), umask()) đặt lại từng nhóm thuộc tính, dưới những quy tắc quyền hạn chặt chẽ.1
Ý chính: lý lịch của tệp nằm trong i-node; có một lệnh để đọc hết lý lịch (stat()), và nhiều lệnh riêng để sửa từng mục. Nhưng không phải ai cũng được sửa — có luật rõ ràng. Nhớ nhé: một lệnh để xem, nhiều lệnh để sửa, và luôn có luật ai được sửa.
The chapter's thesis: a file's attributes — type, size, owner, permissions, timestamps — live in the i-node; stat() reads them into a struct stat, while a family of dedicated calls (utime(), chown(), chmod(), umask()) resets each group of attributes, under strict permission rules.1
Cụm khái niệm 01 Concept cluster 01
stat() — đọc toàn bộ siêu dữ liệu từ i-node stat() — xem hết lý lịch của một tệp stat() — reading all the metadata from the i-node
Ba lời gọi stat(), lstat() và fstat() lấy thông tin về một tệp, phần lớn rút từ i-node của tệp, và đổ vào một bộ đệm kiểu struct stat; cả ba trả về 0 khi thành công, −1 khi lỗi.12 Chúng chỉ khác nhau ở cách chỉ định tệp: stat() nhận một đường dẫn, lstat() cũng nhận đường dẫn nhưng nếu đó là symbolic link thì trả về thông tin của chính link thay vì tệp nó trỏ tới, còn fstat() nhận một file descriptor đang mở.12
Có một lệnh tên stat() giúp hỏi “tệp này to bao nhiêu, của ai, kiểu gì, sửa lần cuối khi nào?” và nhận về một bảng thông tin. Có ba kiểu hỏi: hỏi theo tên tệp, hỏi theo tên nhưng nếu là “lối tắt” (symbolic link) thì xem chính lối tắt, và hỏi theo một tệp đang mở sẵn. Nhớ nhé: stat() là lệnh “cho tôi xem lý lịch của tệp này”.
The three calls stat(), lstat() and fstat() retrieve information about a file, mostly drawn from the file i-node, into a struct stat buffer; all three return 0 on success and −1 on error.12 They differ only in how the file is specified: stat() takes a pathname, lstat() also takes a pathname but, if it is a symbolic link, returns information about the link itself rather than the file it points to, and fstat() takes an open file descriptor.12
Định nghĩa. struct stat gói mọi thuộc tính vào các trường có tiền tố st_: st_dev và st_ino (thiết bị và số i-node — cặp này định danh tệp là duy nhất trên toàn hệ), st_mode (kiểu tệp và quyền gộp chung), st_nlink (số hard link), st_uid/st_gid (chủ và nhóm), st_size (kích thước byte), st_blocks (số khối 512 byte đã cấp phát), st_blksize (kích thước khối tối ưu cho I/O), và ba dấu thời gian st_atime/st_mtime/st_ctime.12
Nói đơn giản. Bảng thông tin mà stat() trả về có nhiều ô: tệp nằm ở thiết bị nào, mang số hiệu nào, kiểu gì, của ai, to bao nhiêu byte, chiếm bao nhiêu khối đĩa, và ba cái mốc thời gian. Mỗi ô có một cái tên bắt đầu bằng st_. Nhớ nhé: stat() trả về một bảng, mỗi ô là một thuộc tính của tệp.
Definition. struct stat packs every attribute into st_-prefixed fields: st_dev and st_ino (device and i-node number — together they uniquely identify a file across the system), st_mode (file type and permissions combined), st_nlink (hard-link count), st_uid/st_gid (owner and group), st_size (size in bytes), st_blocks (512-byte blocks allocated), st_blksize (optimal block size for I/O), and the three timestamps st_atime/st_mtime/st_ctime.12
#include <sys/stat.h>
stat (const char *path, struct stat *buf)
lstat(const char *path, struct stat *buf)
fstat(int fd, struct stat *buf)
→ 0 nếu OK, −1 nếu lỗi
stat(tên) → xem tệp
lstat(tên) → xem chính lối tắt
fstat(fd) → xem tệp đang mở
đều trả về một struct stat
#include <sys/stat.h>
stat (const char *path, struct stat *buf)
lstat(const char *path, struct stat *buf)
fstat(int fd, struct stat *buf)
→ 0 on success, −1 on error
-
st_dev & st_ino
Định danh một tệp — cặp thiết bị và i-node Mỗi tệp có một “mã số” riêng Identifying a file — the device/i-node pair
Trường
st_devcho biết tệp nằm trên thiết bị nào, cònst_inolà số i-node của tệp trên thiết bị ấy; kết hợp hai giá trị này định danh tệp một cách duy nhất trên mọi hệ thống tệp.12 Các lời gọistat()vàlstat()không đòi quyền trên chính tệp, nhưng đòi quyền thực thi (tìm kiếm) trên mọi thư mục cha trong đường dẫn; cònfstat()thì luôn thành công nếu file descriptor hợp lệ.1Hai ô
st_devvàst_inogiống như “ổ đĩa số mấy” và “số thứ tự trong ổ đó”. Ghép lại ta có một mã số không trùng với tệp nào khác. Muốn hỏi lý lịch một tệp theo tên, bạn phải được phép đi qua các thư mục dẫn tới nó. Nhớ nhé: thiết bị + số i-node = mã định danh duy nhất của tệp.The
st_devfield says which device the file resides on, andst_inois the file's i-node number on that device; together these two values identify the file uniquely across all file systems.12 Thestat()andlstat()calls require no permission on the file itself, but do require execute (search) permission on all parent directories in the pathname;fstat()always succeeds given a valid descriptor.1Bạn gặp nó ở đâuWhere you meet it Khi một chương trình muốn biết hai đường dẫn có trỏ về cùng một tệp hay không, nó so cặp (st_dev,st_ino) — chính kỹ thuật mà các công cụ sao lưu dùng để không chép trùng một hard link.2 Muốn biết hai cái tên có phải cùng một tệp không, cứ so mã số (st_dev, st_ino) của chúng. When a program wants to know whether two paths refer to the same file, it compares the (st_dev,st_ino) pair — the very technique backup tools use to avoid copying the same hard link twice.2 -
st_mode & S_IFMT
Kiểu tệp — tách ra khỏi st_mode bằng S_IFMT Tệp này là loại gì? File type — extracted from st_mode with S_IFMT
Trường
st_modelà một bit mask đảm nhận hai vai: xác định kiểu tệp và quy định quyền. Lấy kiểu tệp ra bằng cách AND (&) với hằngS_IFMT, rồi so với các hằng nhưS_IFREG(tệp thường),S_IFDIR(thư mục),S_IFLNK(symbolic link)…1211 Vì thao tác này quá thường gặp, sách dùng các macro gọn nhưS_ISREG(st_mode),S_ISDIR(st_mode),S_ISLNK(st_mode)để kiểm tra trực tiếp.13Một ô duy nhất (
st_mode) vừa giữ “đây là loại tệp gì” vừa giữ “ai được làm gì với nó”. Muốn biết loại tệp, ta dùng các câu hỏi sẵn:S_ISREG(tệp thường?),S_ISDIR(thư mục?),S_ISLNK(lối tắt?). Nhớ nhé: dùng S_ISDIR, S_ISREG… để hỏi nhanh “tệp này loại gì”.The
st_modefield is a bit mask serving a dual purpose: identifying the file type and specifying the permissions. Extract the type by ANDing (&) with the constantS_IFMT, then comparing against constants such asS_IFREG(regular file),S_IFDIR(directory),S_IFLNK(symbolic link)…1211 Because this is so common, the book uses terse macros likeS_ISREG(st_mode),S_ISDIR(st_mode),S_ISLNK(st_mode)to test directly.13Bao nhiêu kiểu tệp
Bảng 15-1 trong sách liệt kê bảy macro kiểm tra kiểu: tệp thường, thư mục, thiết bị ký tự, thiết bị khối, FIFO/pipe, socket và symbolic link — tất cả đều do SUSv3 quy định và có trên Linux.13
Một lưu ý về cách đếm bit
Sách nói trên Linux dùng 4 bit cho phần kiểu tệp; nhưng vì SUSv3 không quy định cách biểu diễn kiểu tệp, chi tiết này có thể khác nhau giữa các bản hiện thực — nên hãy luôn kiểm tra qua
S_IFMT/các macro thay vì giả định số bit.1 -
size, blocks & times
Kích thước, khối đĩa và ba dấu thời gian To bao nhiêu, và ba cái mốc thời gian Size, disk blocks and the three timestamps
Với tệp thường,
st_sizelà tổng kích thước tính bằng byte; với symbolic link, nó là độ dài đường dẫn mà link trỏ tới.1 Trườngst_blocksđếm số khối 512 byte thực sự được cấp phát (đơn vị 512 byte là lựa chọn lịch sử), nên một tệp có “lỗ hổng” (hole) có thể chiếm ít khối hơn số byte của nó.12 Ba trườngst_atime,st_mtime,st_ctimelần lượt ghi thời điểm truy cập, sửa nội dung, và đổi trạng thái i-node gần nhất, tính bằng giây kể từ Epoch (1/1/1970).13Ô
st_sizecho biết tệp nặng bao nhiêu byte. Ôst_blocksđếm số khối đĩa 512 byte mà tệp thật sự chiếm. Rồi có ba mốc thời gian: lần cuối đọc, lần cuối sửa nội dung, và lần cuối đổi thông tin quản lý. Nhớ nhé: ba mốc thời gian là lần đọc (a), lần sửa nội dung (m), và lần đổi i-node (c).For a regular file,
st_sizeis the total size in bytes; for a symbolic link it is the length of the pathname the link points to.1 Thest_blocksfield counts the 512-byte blocks actually allocated (the 512-byte unit is a historical choice), so a file with a hole may occupy fewer blocks than its byte count.12 The three fieldsst_atime,st_mtime,st_ctimerecord, respectively, the time of last access, last content modification, and last i-node status change, in seconds since the Epoch (1 Jan 1970).13Bạn gặp nó ở đâuWhere you meet it Lệnhdu -khiển thị không gian thực sự chiếm, tính từst_blockschứ không từst_size— nên một tệp thưa (sparse) báo ít kilobyte hơn kích thước “danh nghĩa” của nó.1 Lệnh du đo chỗ tệp thật sự chiếm trên đĩa, nên tệp có lỗ hổng sẽ nhẹ hơn con số kích thước của nó. Thedu -kcommand shows the space actually occupied, computed fromst_blocksrather thanst_size— so a sparse file reports fewer kilobytes than its nominal size.1
Cụm khái niệm 02 Concept cluster 02
Đổi dấu thời gian và quyền sở hữu Sửa thời gian và sửa chủ của tệp Changing timestamps and ownership
Phần lớn lời gọi hệ thống tự động cập nhật dấu thời gian — đọc tệp đặt lại st_atime, ghi tệp đặt lại st_mtime. Nhưng đôi khi ta muốn chủ động đặt một giá trị tùy ý: các chương trình như tar(1) và unzip(1) làm vậy để khôi phục đúng thời gian gốc khi giải nén.18 Tương tự, quyền sở hữu (UID, GID) của một tệp mới được định theo quy tắc riêng, và có thể được đổi về sau bằng họ chown().14
Thường thì mỗi lần bạn đọc hay ghi tệp, máy tự cập nhật các mốc thời gian. Nhưng có lúc ta muốn tự tay đặt thời gian — ví dụ khi giải nén một kho lưu trữ, ta muốn tệp giữ đúng ngày giờ cũ. Chủ của tệp cũng có thể đổi được. Nhớ nhé: có lệnh để tự đặt thời gian, và có lệnh để đổi chủ của tệp.
Most system calls update timestamps automatically — reading a file resets st_atime, writing resets st_mtime. But sometimes we want to set an arbitrary value deliberately: programs such as tar(1) and unzip(1) do this to restore the original times when unpacking an archive.18 Likewise, a new file's ownership (UID, GID) is decided by specific rules, and can be changed later with the chown() family.14
Định nghĩa. utime(pathname, buf) đặt lại thời gian truy cập và sửa đổi gần nhất của tệp. Nếu buf là NULL, cả hai được đặt về thời điểm hiện tại; nếu là con trỏ tới struct utimbuf { time_t actime; time_t modtime; }, chúng nhận giá trị trong cấu trúc đó.18 Một lời gọi utime() thành công luôn đặt thời gian đổi trạng thái (st_ctime) về hiện tại.1
Nói đơn giản. Lệnh utime giúp đặt lại “lần cuối đọc” và “lần cuối sửa” của một tệp. Đưa vào NULL thì nó dùng giờ hiện tại; đưa vào hai con số thì nó dùng đúng hai con số ấy. Nhớ nhé: utime đặt lại thời gian đọc và thời gian sửa của tệp.
Definition. utime(pathname, buf) resets a file's last access and modification times. If buf is NULL, both are set to the current time; if it points to a struct utimbuf { time_t actime; time_t modtime; }, they take the values in that structure.18 A successful utime() call always sets the status-change time (st_ctime) to the current time.1
utime (path, &utimbuf) → giây
utimes(path, tv[2]) → micro giây
utimensat(dirfd, path,
timespec[2], flags) → nano giây
UTIME_NOW · UTIME_OMIT
utime → đặt theo giây
utimes → mịn hơn (micro giây)
utimensat → mịn nhất (nano giây)
đặt một mốc, giữ nguyên mốc kia
utime (path, &utimbuf) → seconds
utimes(path, tv[2]) → microseconds
utimensat(dirfd, path,
timespec[2], flags) → nanoseconds
UTIME_NOW · UTIME_OMIT
-
utime() & utimes()
utime() và utimes() — đặt thời gian theo giây và micro giây Hai lệnh đặt thời gian đời đầu utime() & utimes() — seconds and microseconds
Để đổi chỉ một trong hai mốc, ta phải đọc trước cả hai bằng
stat(), nạp một giá trị vàoutimbufvà giữ nguyên giá trị kia — vìutime()không cho đặt riêng lẻ.1 Linux còn cung cấputimes()(gốc BSD), khác biệt chính là cho đặt thời gian với độ chính xác micro giây qua mảngstruct timeval tv[2](tv[0]là access,tv[1]là modification).18 Hai hàm thư viện họ hàng làfutimes()(chỉ định tệp qua file descriptor) vàlutimes()(không dereference symbolic link).1Muốn đổi chỉ “lần cuối sửa” mà giữ nguyên “lần cuối đọc”, bạn phải đọc cả hai trước rồi đặt lại — vì lệnh cũ bắt đặt cả cặp. Có bản
utimesmới hơn, đặt thời gian mịn tới một phần triệu giây. Nhớ nhé: lệnh cũ phải đặt cả hai mốc cùng lúc; utimes chính xác hơn.To change just one of the two times, we must first read both with
stat(), load one value into theutimbufand leave the other unchanged — becauseutime()can't set them individually.1 Linux also providesutimes()(BSD-derived), whose main difference is setting times with microsecond precision via astruct timeval tv[2]array (tv[0]is access,tv[1]is modification).18 Two related library functions arefutimes()(file given by descriptor) andlutimes()(does not dereference a symbolic link).1Bạn gặp nó ở đâuWhere you meet it Mỗi lầntar -xhaycp -pkhôi phục “ngày sửa” gốc của tệp, bên dưới là một lời gọi thuộc họutime.1 Khi giải nén mà tệp vẫn giữ ngày cũ, đó là nhờ những lệnh đặt thời gian này. Every timetar -xorcp -prestores a file's original modification date, a call from theutimefamily is at work underneath.1 -
utimensat() & futimens()
utimensat() — nano giây, đặt từng mốc riêng Lệnh đặt thời gian hiện đại nhất utimensat() — nanoseconds, each time independently
Từ nhân 2.6, Linux hỗ trợ độ phân giải nano giây cho ba dấu thời gian; SUSv3 không quy định điều này, nhưng SUSv4 thì có.18
utimensat()(có từ nhân 2.6.22) và hàm thư việnfutimens()(từ glibc 2.6) khai thác điều đó: dùng mảngstruct timespec times[2](giây + nano giây), cho phép đặt hai mốc độc lập.18 Đặttv_nsec = UTIME_NOWđể lấy giờ hiện tại cho một mốc, hoặcUTIME_OMITđể giữ nguyên mốc đó mà không cần đọc trước.18Bản mới nhất là
utimensat: chính xác tới một phần tỉ giây, và quan trọng hơn, cho phép đổi một mốc mà không đụng mốc kia — chỉ cần ghiUTIME_OMITvào mốc muốn giữ nguyên. Nhớ nhé: utimensat vừa chính xác hơn, vừa cho sửa từng mốc riêng.Since kernel 2.6, Linux supports nanosecond resolution for the three timestamps; SUSv3 doesn't specify this, but SUSv4 does.18
utimensat()(since kernel 2.6.22) and the library functionfutimens()(since glibc 2.6) exploit it: they use astruct timespec times[2]array (seconds + nanoseconds) and allow the two times to be set independently.18 Settv_nsec = UTIME_NOWto take the current time for one, orUTIME_OMITto leave that one unchanged without reading it first.18Vì sao UTIME_OMIT quan trọng
Với các API cũ, muốn đổi một mốc phải gọi
stat()để lấy mốc kia trước — tạo khe hở cho một tiến trình khác chen vào đổi mốc ấy giữa hai bước (một race condition).UTIME_OMITxóa bỏ khe hở này bằng cách báo nhân giữ nguyên mốc, không cần đọc lại.18Không phải hệ tệp nào cũng có nano giây
Sách lưu ý: các hệ như JFS, XFS, ext4, Btrfs hỗ trợ dấu thời gian nano giây, còn ext2, ext3, Reiserfs thì không. Vậy độ chính xác thực tế phụ thuộc hệ tệp bên dưới, không chỉ phụ thuộc lời gọi.1
-
chown() & new-file rules
chown() và luật chủ sở hữu của tệp mới Chủ của tệp được định ra sao chown() & the rules for a new file's owner
Khi một tệp được tạo, UID của nó lấy từ effective user ID của tiến trình; còn GID lấy từ effective group ID của tiến trình (kiểu System V) hoặc từ GID của thư mục cha (kiểu BSD), tùy tùy chọn mount và bit set-group-ID của thư mục.1 Về sau,
chown(),lchown()(không dereference link) vàfchown()(qua file descriptor) đổi chủ/nhóm.14 Chỉ tiến trình có đặc quyền (CAP_CHOWN) mới đổi được UID của tệp; một tiến trình thường chỉ đổi được GID của tệp nó sở hữu, và chỉ sang một nhóm nó là thành viên.14Tệp mới sinh ra sẽ thuộc về người tạo nó. Muốn đổi chủ sang người khác thì phải có quyền quản trị (root). Người thường chỉ được đổi nhóm của tệp mình, và chỉ đổi sang nhóm mà mình thuộc về. Nhớ nhé: đổi chủ cần quyền root; người thường chỉ đổi được nhóm của tệp mình.
When a file is created, its UID is taken from the process's effective user ID; its GID comes either from the process's effective group ID (System V behavior) or from the parent directory's GID (BSD behavior), depending on mount options and the directory's set-group-ID bit.1 Later,
chown(),lchown()(no link dereference) andfchown()(via descriptor) change owner/group.14 Only a privileged (CAP_CHOWN) process may change a file's UID; an unprivileged process can change only the GID of a file it owns, and only to a group of which it is a member.14Bạn gặp nó ở đâuWhere you meet it Khi đổi chủ hoặc nhóm của một tệp, nhân tự tắt cả hai bit set-user-ID và set-group-ID — một biện pháp an ninh ngăn kẻ xấu “biến” tệp của mình thành tệp mang đặc quyền của người khác.14 Đổi chủ tệp thì máy tự gỡ các “công tắc đặc quyền”, để không ai lợi dụng đoạt quyền người khác. When a file's owner or group changes, the kernel turns off both the set-user-ID and set-group-ID bits — a security precaution stopping an attacker from turning their file into one that carries someone else's privilege.14
Cụm khái niệm 03 Concept cluster 03
Quyền truy cập, bit đặc biệt, umask và i-node flags Quyền đọc/ghi/chạy và các công tắc đặc biệt Permissions, special bits, umask and i-node flags
12 bit thấp của st_mode định quyền cho tệp. 3 bit đầu là các bit đặc biệt — set-user-ID, set-group-ID và sticky; 9 bit còn lại chia thế giới thành ba nhóm — chủ (owner/user), nhóm (group) và những người khác (other) — mỗi nhóm có ba quyền đọc (read), ghi (write), thực thi (execute).13 Với thư mục, ba quyền ấy mang nghĩa khác: đọc là liệt kê tên tệp, ghi là tạo/xóa mục trong thư mục, còn thực thi là quyền “tìm kiếm” — được đi xuyên qua thư mục để tới tệp bên trong.1
Phần quyền của tệp chia người dùng làm ba nhóm: chủ tệp, nhóm của tệp, và tất cả người khác. Mỗi nhóm được cấp ba quyền: đọc, ghi, và chạy. Với thư mục thì ba quyền ấy đổi nghĩa một chút: đọc là xem danh sách tên, ghi là thêm/bớt tệp, chạy là được đi vào trong. Nhớ nhé: ba nhóm người × ba quyền (đọc, ghi, chạy) = mô hình quyền của UNIX.
The low 12 bits of st_mode define a file's permissions. The first 3 are the special bits — set-user-ID, set-group-ID and sticky; the remaining 9 divide the world into three categories — owner (user), group, and other — each with three permissions: read, write, execute.13 For a directory those three mean something different: read lists the filenames, write creates/removes entries in the directory, and execute is "search" permission — being allowed to traverse the directory to reach files within.1
Định nghĩa. Khi kiểm tra quyền trên một đường dẫn, nhân dùng effective user ID, effective group ID và các supplementary group ID của tiến trình, lần lượt theo bốn quy tắc: (1) tiến trình có đặc quyền thì được mọi quyền; (2) nếu là chủ tệp, áp quyền của nhóm owner; (3) nếu thuộc nhóm của tệp, áp quyền group; (4) còn lại áp quyền other.19 Kiểm tra dừng ngay khi gặp quy tắc áp dụng được — nên về lý thuyết nhóm có thể được nhiều quyền hơn chính chủ.1
Nói đơn giản. Khi bạn đụng vào một tệp, máy hỏi theo thứ tự: bạn là quản trị? bạn là chủ? bạn cùng nhóm? hay bạn là người ngoài? Gặp đúng vai nào thì áp quyền của vai đó, rồi dừng. Nhớ nhé: máy xét vai của bạn theo thứ tự chủ → nhóm → người ngoài, gặp vai nào thì theo vai đó.
Definition. Checking permission on a pathname, the kernel uses the process's effective user ID, effective group ID and supplementary group IDs, in four rules: (1) a privileged process gets all access; (2) if it is the file owner, apply the owner permissions; (3) if it is in the file's group, apply group; (4) otherwise apply other.19 Checking stops at the first applicable rule — so in theory a group can be granted more than the owner.1
S_ISUID 04000 set-user-ID
S_ISGID 02000 set-group-ID
S_ISVTX 01000 sticky
rwx rwx rwx → user group other
umask mặc định 022 → rw-r--r--
04000 → set-user-ID
02000 → set-group-ID
01000 → sticky
r = đọc, w = ghi, x = chạy
theo thứ tự: chủ · nhóm · người khác
S_ISUID 04000 set-user-ID
S_ISGID 02000 set-group-ID
S_ISVTX 01000 sticky
rwx rwx rwx → user group other
default umask 022 → rw-r--r--
-
access()
access() — kiểm tra quyền theo real ID access() — hỏi “tôi có được phép không?” access() — checking by the real IDs
Khác với lúc truy cập tệp (dùng effective ID),
access(pathname, mode)kiểm tra khả năng truy cập dựa trên real user/group ID của tiến trình — hữu ích cho một chương trình set-user-ID muốn biết người thật sự chạy nó có quyền hay không.17 Tham sốmodelà bit mask gộp từF_OK(tệp có tồn tại?),R_OK(đọc được?),W_OK(ghi được?),X_OK(chạy được?); trả về 0 nếu mọi quyền được cấp, ngược lại −1.17Lệnh
accessgiúp hỏi trước: “tệp này tôi đọc được không? ghi được không? có tồn tại không?”. Nó xét theo danh tính thật của người chạy, chứ không theo danh tính tạm mượn. Nhớ nhé: access hỏi trước “tôi có quyền không”, theo người thật đang chạy.Unlike actual file access (which uses the effective IDs),
access(pathname, mode)checks accessibility by the process's real user/group IDs — useful for a set-user-ID program wanting to know whether the person really running it has permission.17 Themodeargument is a bit mask ofF_OK(does it exist?),R_OK(readable?),W_OK(writable?),X_OK(executable?); it returns 0 if all permissions are granted, otherwise −1.17Bạn gặp nó ở đâuWhere you meet it Nhiều script và công cụ dùngaccess()để “thử trước khi làm”; nhưng Kerrisk cảnh báo nên tránh nó vì khe hở thời gian giữa kiểm tra và thao tác thật (xem phần ngộ nhận).17 Nhiều chương trình dùng access để kiểm tra trước, nhưng sách khuyên nên cẩn thận (xem phần ngộ nhận). Many scripts and tools useaccess()to "test before doing"; but Kerrisk warns against it because of the time gap between the check and the real operation (see the misconceptions).17 -
special bits
Set-UID, set-GID và sticky — ba bit đổi hành vi Ba công tắc đặc biệt Set-UID, set-GID and sticky — three behavior bits
Ba bit đặc biệt là set-user-ID (04000), set-group-ID (02000) và sticky (01000).13 Set-UID/set-GID trên một chương trình khiến nó chạy với danh tính của chủ/nhóm tệp — nền tảng để tạo chương trình có đặc quyền (Chương 9). Trên UNIX hiện đại, bit sticky đặt trên một thư mục biến nó thành cờ “xóa hạn chế”: trong thư mục đó, một tiến trình thường chỉ được
unlink()/rename()những tệp mà nó sở hữu.13 Vì thế thư mục/tmpthường được đặt sticky.1Có ba “công tắc” đặc biệt. Hai cái đầu (set-UID, set-GID) cho một chương trình tạm mượn quyền của chủ nó khi chạy. Cái thứ ba (sticky) đặt lên thư mục dùng chung như
/tmp: ai tạo tệp thì chỉ người đó mới xóa được tệp của mình. Nhớ nhé: sticky trên thư mục nghĩa là “chỉ chủ tệp mới xóa được tệp của mình”.The three special bits are set-user-ID (04000), set-group-ID (02000) and sticky (01000).13 Set-UID/set-GID on a program make it run with the identity of the file's owner/group — the basis for privileged programs (Chapter 9). On modern UNIX, the sticky bit on a directory turns it into a "restricted deletion" flag: within it, an unprivileged process may only
unlink()/rename()files it owns.13 That is why/tmpis usually set sticky.1 -
umask() & chmod()
umask() đặt quyền mặc định, chmod() đổi quyền Quyền mặc định và cách sửa quyền umask() sets defaults, chmod() changes them
Khi tạo tệp, các bit quyền yêu cầu còn bị umask của tiến trình che bớt: umask quy định những bit nào luôn bị tắt. Giá trị mặc định thường là 022 (bát phân), nên tệp tạo với
mode0666 thực tế thànhrw-r--r--.16umask(mask)đặt umask mới và luôn trả về giá trị cũ.16 Để đổi quyền của một tệp đã có, dùngchmod(pathname, mode)hoặcfchmod(fd, mode); muốn đổi, tiến trình phải có đặc quyền (CAP_FOWNER) hoặc effective user ID khớp chủ tệp.15Khi bạn tạo tệp mới, hệ thống tự gỡ bớt vài quyền theo một “bộ lọc” gọi là umask (thường là 022), nên tệp mới mặc định người khác chỉ đọc được. Muốn sửa quyền tệp có sẵn thì dùng
chmod— nhưng bạn phải là chủ tệp. Nhớ nhé: umask lọc quyền lúc tạo, chmod sửa quyền về sau — và chỉ chủ tệp được sửa.On file creation, the requested permission bits are further masked by the process umask: it specifies which bits are always turned off. The default is usually 022 (octal), so a file created with
mode0666 ends uprw-r--r--.16umask(mask)sets a new umask and always returns the old value.16 To change an existing file's permissions usechmod(pathname, mode)orfchmod(fd, mode); to do so, the process must be privileged (CAP_FOWNER) or its effective user ID must match the file's owner.15Bạn gặp nó ở đâuWhere you meet it Lệnhumaskcủa shell và lệnhchmodđều là lớp vỏ quanh hai lời gọi này; đổi umask trong file khởi tạo shell sẽ đổi quyền mặc định của mọi tệp bạn tạo sau đó.1 Lệnh umask và chmod bạn gõ trong terminal chính là hai lời gọi này. The shell'sumaskcommand and thechmodcommand are both wrappers around these two calls; changing the umask in a shell startup file changes the default permissions of every file you create afterward.1 -
i-node flags
I-node flags — thuộc tính mở rộng kiểu ext2 Các “công tắc” mở rộng của tệp I-node flags — ext2 extended attributes
Một số hệ tệp Linux cho đặt i-node flags trên tệp và thư mục — một mở rộng phi chuẩn, lần đầu có ở
ext2(nên còn gọi là ext2 extended file attributes), sau lan sang ext3, ext4, Btrfs, Reiserfs, XFS, JFS.110 Từ shell, đặt/xem bằngchattrvàlsattr; trong chương trình, dùngioctl()vớiFS_IOC_GETFLAGS/FS_IOC_SETFLAGS.110 Hai flag nổi bật:FS_APPEND_FL(chỉ cho mở ghi khi cóO_APPEND— hợp cho tệp log) vàFS_IMMUTABLE_FL(bất biến — ngay cả tiến trình đặc quyền cũng không sửa được nội dung lẫn siêu dữ liệu khi cờ còn bật).110Ngoài quyền thường, Linux còn có vài “công tắc” mở rộng gắn vào tệp. Ví dụ: cờ “chỉ nối thêm” (không cho ghi đè, hợp với tệp nhật ký) và cờ “bất biến” (khóa cứng, không ai sửa được cho tới khi gỡ cờ). Gõ
chattrđể bật,lsattrđể xem. Nhớ nhé: chattr +i khóa cứng một tệp, đến root cũng chịu cho tới khi gỡ cờ.Some Linux file systems allow i-node flags on files and directories — a nonstandard extension, first in
ext2(hence ext2 extended file attributes), later in ext3, ext4, Btrfs, Reiserfs, XFS, JFS.110 From the shell, set/view them withchattrandlsattr; in a program, useioctl()withFS_IOC_GETFLAGS/FS_IOC_SETFLAGS.110 Two notable flags:FS_APPEND_FL(can be opened for writing only withO_APPEND— good for a log file) andFS_IMMUTABLE_FL(immutable — even a privileged process can't change content or metadata while the flag is set).110
Ba ngộ nhận thường gặp Three common misconceptions
“st_ctime là thời gian tạo tệp” “st_ctime is the file's creation time”
Ngộ nhận: chữ “c” trong st_ctime là “creation” — thời điểm tạo tệp.
Thực tế: đó là change — thời điểm đổi trạng thái i-node gần nhất (đổi quyền, chủ, số link…). UNIX truyền thống (kể cả Linux với stat()) không lưu thời gian tạo tệp; sách lưu ý một số hệ BSD mới ghi thời điểm này ở trường st_birthtime.13
Myth: the "c" in st_ctime is "creation" — when the file was made.
Reality: it is change — the time of the last i-node status change (permissions, owner, link count…). Traditional UNIX (including Linux via stat()) does not record a creation time; the book notes some recent BSD systems store it in a st_birthtime field.13
“access() an toàn để kiểm tra trước khi mở” “access() is safe for a check-before-open”
Ngộ nhận: gọi access() rồi mới open() là cách kiểm tra quyền an toàn.
Thực tế: giữa access() và thao tác sau tồn tại khe thời gian; kẻ xấu có thể đổi link để tệp bị thao tác không còn là tệp đã kiểm tra — một race condition kiểu TOCTOU. Kerrisk khuyên tránh access(), thay bằng cách tạm đổi danh tính rồi thử thao tác thật.17
Myth: calling access() then open() is a safe way to check permission.
Reality: a time gap exists between access() and the later operation; an attacker can swap a link so the operated-on file isn't the one checked — a TOCTOU race. Kerrisk advises avoiding access(), instead temporarily changing identity and attempting the real operation.17
“sticky bit làm chương trình chạy nhanh hơn” “the sticky bit makes programs run faster”
Ngộ nhận: đặt sticky để giữ chương trình trong bộ nhớ cho chạy nhanh hơn.
Thực tế: đó là nghĩa cũ (“saved-text”) nay đã lỗi thời. Trên UNIX hiện đại, sticky trên một thư mục là cờ xóa hạn chế — chỉ chủ của tệp (hoặc tiến trình có CAP_FOWNER) mới xóa/đổi tên được tệp trong đó.13
Myth: set the sticky bit to keep a program in memory so it runs faster.
Reality: that is the old ("saved-text") meaning, now obsolete. On modern UNIX, sticky on a directory is a restricted-deletion flag — only a file's owner (or a process with CAP_FOWNER) may delete/rename files within it.13
Tự kiểm Self-check
Năm câu hỏi để biết bạn đã nắm thuộc tính tệp Năm câu hỏi kiểm tra nhanh Five questions to check you grasped file attributes
Trả lời được những câu này nghĩa là bạn đã theo trọn mạch từ stat() đọc i-node, qua các lời gọi đổi thời gian và chủ, tới mô hình quyền và các bit đặc biệt.
Nếu trả lời được, nghĩa là bạn đã hiểu chương này.
Answering these means you followed the whole line from stat() reading the i-node, through the calls that change times and ownership, to the permission model and the special bits.
-
stat(),lstat()vàfstat()khác nhau ở điểm nào, vàlstat()đặc biệt hữu ích với loại tệp nào?Gợi ý sâu hơn: cặp trường nào trong
struct statđịnh danh một tệp là duy nhất, và vì sao điều đó hữu ích khi so hai đường dẫn? -
Vì sao
st_blockscó thể không khớp vớist_size, và lệnh nào tận dụng khác biệt đó?Gợi ý sâu hơn: đơn vị của
st_blockslà bao nhiêu byte, và vì sao đó là lựa chọn lịch sử? -
utimensat()hơnutime()ở hai điểm nào, vàUTIME_OMITgiải quyết vấn đề gì?Gợi ý sâu hơn: vì sao việc đặt riêng một mốc thời gian với API cũ lại tạo ra một race condition?
-
Nhân xét quyền trên một đường dẫn theo bốn quy tắc nào, và theo thứ tự ra sao?
Gợi ý sâu hơn: vì sao về lý thuyết nhóm có thể được nhiều quyền hơn chính chủ tệp?
-
Ba bit đặc biệt là gì, và bit sticky trên một thư mục như
/tmpcó tác dụng gì?Gợi ý sâu hơn: vì sao đổi chủ một tệp lại tự tắt các bit set-user-ID/set-group-ID?
Lệnh
stat()dùng để làm gì?Ba mốc thời gian của một tệp là ba lần gì?
Muốn đổi chủ của một tệp sang người khác thì cần quyền gì?
Một tệp có ba nhóm người dùng nào, và mỗi nhóm có ba quyền nào?
Đặt sticky lên thư mục
/tmpđể làm gì?
How do
stat(),lstat()andfstat()differ, and for what kind of file islstat()especially useful?Why can
st_blocksdisagree withst_size, and which command exploits that difference?In what two ways does
utimensat()improve onutime(), and what problem doesUTIME_OMITsolve?By what four rules, and in what order, does the kernel check permission on a pathname?
What are the three special bits, and what does the sticky bit do on a directory like
/tmp?
Khép lại chương Closing the chapter
Thuộc tính của một tệp — kiểu, kích thước, chủ, quyền, thời gian — cư trú trong i-node;
stat()đọc chúng ra, còn một họ lời gọi (utime,chown,chmod,umask) đặt lại từng nhóm. Hiểu mô hình quyền và các quy tắc ai được sửa gì là nền cho mọi lập trình hệ thống về sau.1Lý lịch của tệp nằm trong i-node; có một lệnh để xem hết và nhiều lệnh để sửa từng mục, kèm luật rõ ràng ai được sửa. Nhớ nhé: nắm được lý lịch tệp và luật quyền thì học các chương sau mới vững.
A file's attributes — type, size, owner, permissions, times — live in the i-node;
stat()reads them out, and a family of calls (utime,chown,chmod,umask) resets each group. Grasping the permission model and the rules of who may change what is the foundation for all later systems programming.1