[Linux環境編程學習筆記_1]:I/O-文件與目錄

来源:https://www.cnblogs.com/sleep-at-11/archive/2022/05/02/15756552.html
-Advertisement-
Play Games

文件和目錄 1. 文件系統 我們可以把一個磁碟分成一個或多個分區,每個分區包含一個文件系統,這個文件系統由很多柱面組成,而柱面中有一個非常重要的概念叫做 i 節點。 i 節點包含了文件的大部分信息,如文件類型,文件訪問許可權位,文件大小和指向文件數據的指針等,大多數信息都存在st_mode成員中,有兩 ...


目錄

文件和目錄


1. 文件系統

我們可以把一個磁碟分成一個或多個分區,每個分區包含一個文件系統,這個文件系統由很多柱面組成,而柱面中有一個非常重要的概念叫做 i 節點。

i 節點包含了文件的大部分信息,如文件類型,文件訪問許可權位,文件大小和指向文件數據的指針等,大多數信息都存在st_mode成員中,有兩個重要參數存放在目錄中,文件名和 i 節點編號。

2. 文件屬性結構體

struct stat
{
    mode_t    st_mode;    //文件類型和文件許可權
    uid_t     st_uid;     //用戶ID
    gid_t     st_gid;     //組ID
    nlink_t   st_nlink;   //連到該文件的硬連接數目,剛建立的文件值為1
    off_t     st_size;    //文件位元組數,即文件大小,鏈接文件的話是所指路徑名的長度
    blksize_t st_blksize; //文件系統上進行I/O操作時的最優塊大小
    blkcnt_t  st_blocks;  //塊數,du命令就是blocks
    time_t    st_atime;   //最後一次訪問時間,access
    time_t    st_mtime;   //最後一次修改時間,modify
    time_t    st_ctime;   //最後一次文件屬性改變時間,chmod
    ino_t     st_ino;     //文件inode結點號
    dev_t     st_dev;     //文件系統的設備號,該文件系統包含了文件名及對應的i節點
    dev_t     st_rdev;    //針對字元設備和塊設備,實際的設備號,主/次設備號
};

可以通過以下函數獲取文件的信息結構:

#include <sys/stat.h>
int stat(const char *restrict pathname, struct stat *restrict buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *restrict pathname, struct stat *restrict buf);
int fstatat(int fd, const char *restrict pathname, struct stat *restrict buf, int flag);
  • stat函數可以根據 pathname,獲取對應文件的信息結構;
  • fstat函數,則可以根據文件描述符fd,獲取對應文件的信息結構;
  • lstat函數,針對鏈接文件,lstat函數返回的是鏈接文件本身的信息,而不是鏈接文件指向的文件的信息;
  • fstatat函數,可以根據文件描述符 fd 和 pathname 來確定要獲取的文件信息,如果獲取的是鏈接文件,flag 參數用來決定獲取的是鏈接文件本身的信息還是鏈接文件所指向的文件信息。

i-node 表和文件數據的映射關係如下圖:

i-node表和文件數據的映射關係圖

3. 文件信息

st_mode是 16位的,用二進位表示時,包含三部分信息:

文件類型  設置位  文件許可權
****     ***    *** *** ***

3.1 文件類型

最常見的文件類型就是普通文件目錄文件了,其次還有一些其他類型的文件:

  • 普通文件 (-);
  • 目錄文件(d);
  • 符號鏈接(l):類似於 windows 中的快捷方式;
  • 字元特殊文件(c):這種類型的文件提供了對設備不帶緩衝的訪問,每次訪問長度可變;
  • 塊特殊文件(b):這種類型的文件提供對設備(如磁碟)帶緩衝的訪問,每次訪問以固定長度為單位進行;
  • FIFO(f):有時也叫管道,用於進程間通信;
  • 套接字(s):用於進程間的網路通信。

可以通過以下函數來判斷文件類型:

#include <sys/stat.h>
S_ISREG();  //普通文件,是的話,返回1,參數傳入st_mode
S_ISDIR();  //目錄文件
S_ISLNK();  //符號鏈接文件
S_ISCHR();  //字元設備文件
S_ISBLK();  //塊設備文件
S_ISFIFO(); //FIFO文件
S_ISSOCK(); //套接字

3.2 設置位

設置位分為三種:

設置位 功能
S_ISUID 執行時會將有效用戶ID設置為用戶ID
S_ISGID 執行時會將有效組ID設置為組ID
S_ISVTX 粘著位

粘著位只對目錄有效,起限制刪除的作用,shell中可以通過chmod +t filename來設置粘著位。設置了粘著位的目錄,只有當操作用戶擁有該文件,或擁有該目錄,或者是超級用戶,且對該目錄具有寫許可權,才能刪除或重命名該目錄下的文件。

3.3 文件所有權

文件的許可權可以分為讀,寫,執行三種許可權,根據不同的所有者可以分為以下三類:

訪問許可權 說明
S_IRUSR 用戶讀
S_IWUSR 用戶寫
S_IXUSR 用戶執行
S_IRGRP 組讀
S_IWGRP 組寫
S_IXGRP 組執行
S_IROTH 其他讀
S_IWOTH 其他寫
S_IXOTH 其他執行

一般用u表示用戶,用g表示組,用o表示其他。

在檢查文件許可權時,會按照以下順序執行:

  • 對於特權級進程,授予其所有訪問許可權;
  • 若進程 ID 與文件的用戶 ID 相同,那麼就是文件的屬主許可權;
  • 上述條件不滿足的話,會判斷進程的組 ID 和文件的組 ID 是否匹配,匹配就賦予文件的屬組許可權;
  • 若以上三點都不滿足,內核會根據 other 許可權,授予進程相應許可權。

對於許可權需要註意的點:

  • 對於目錄來說,目錄具備讀許可權,指的是可以獲取該目錄下的文件列表名;而目錄具備執行許可權,指的是能通過目錄,所有目錄下的文件,舉個例子,如果要打開 /usr/include/stdio.h文件,需要對目錄 /,/usr,/usr/include 有執行許可權。一般目錄都需要具備執行許可權。
  • 如果要在一個目錄下創建或刪除一個文件,那麼該目錄需要具備寫和執行許可權;
  • 新文件的用戶 ID 設置為進程的有效用戶 ID,組 ID 可以使進程的有效組 ID ,也可以是所在目錄的組 ID。

3.4 文件許可權操作介面

3.4.1 文件許可權屏蔽字 umask

在進程創建一個新文件或新目錄時,就一定會使用文件模式創建屏蔽字,對應位為 1 的話說明屏蔽對應許可權:

屏蔽位 含義
0400 用戶讀
0200 用戶寫
0100 用戶執行
0040 組讀
0020 組寫
0010 組執行
0004 其他讀
0002 其他寫
0001 其他執行

函數如下:

#include <sys/stat.h>
mode_t umask(mode_t cmask); //返回之前的文件模式創建屏蔽字

這個參數系統預設就有的,可以通過 shell 命令 umask查看

umask

3.4.2 文件許可權測試 access 和 faccessat

進程打開一個文件時,預設是以進程有效用戶 ID 和有效組 ID 來訪問的, 但是有時候可能想測試實際用戶 ID 和實際組 ID的許可權,可以通過這兩個函數來實現:

#include <unistd.h>
int access(const char *pathname, int mode);
int faccessat(int fd, const char *pathname, int mode, int flag);
參數說明:
    mode: R_OK=測試讀許可權; 
          W_OK=測試寫許可權; 
          X_OK=測試執行許可權;
		  F_OK=有這個文件嗎
返回值:
    成功=0; 失敗=-1

但因為不是原子的,所以這函數不安全。

3.4.3 文件許可權修改函數 chmod,fchmod,fchmodat

#include <sys/stat.h>
int chmod(const char *pathname, mode_t mode);
int fchmod(int fd, mode_t mode);
int fchmodat(int fd, const char *pathname, mode_t mode, int flag);

該函數還可以設置執行時用戶 ID 和 組 ID,mode參數如下:

mode 說明
S_ISUID 執行時設置用戶 ID
S_ISGID 執行時設置組 ID
S_ISVTX 粘著位,對目錄使用
S_IRWXU 用戶讀,寫,執行
S_IRUSR 用戶讀
S_IWUSR 用戶寫
S_IXUSR 用戶執行
S_IRWXG 組讀,寫,執行
S_IRGRP 組讀
S_IWGRP 組寫
S_IXGRP 組執行
S_IRWXO 其他讀,寫,執行
S_IROTH 其他讀
S_IWOTH 其他寫
S_IXOTH 其他執行

粘著位:對目錄使用,主要是用來限制刪除或重命名目錄下的文件的。

只有對該目錄具備寫許可權的用戶,且滿足以下條件之一的,才能刪除或重命名該目錄下的文件:

  • 擁有此文件;
  • 擁有此目錄;
  • 是超級用戶

4. 文件所有者

st_uidst_gid分別指明瞭文件所屬主和所屬組。

每個文件都有有一個文件所有者和組所有者,我們把當前的所有者和組所有者分別稱為有效用戶 ID有效組 ID,有的情況下可能還有個附屬組ID

一般文件新創建時,其用戶 ID 就是創建它的進程的有效用戶 ID,文件的組 ID 就是該進程的有效組 ID。

一般情況下,有效用戶 ID 就是實際用戶 ID,有效組 ID 就是實際組 ID,這兩個參數保存在st_uidst_gid中,可以通過函數S_ISUIDS_ISGID測試。

與一個進程相關的 ID 梳理如下:

ID 種類 說明
實際用戶 ID 我們實際是誰
實際組 ID
有效用戶 ID 當前有效的所有者
有效組 ID
附屬組 ID
保存的設置用戶 ID 由 exec 函數保存
保存的設置組 ID

文件用戶 ID 和組 ID 可以通過以下函數修改,chownfchownfchownatlchown

#include <unistd.h>
int chown(const char *pathname, uid_t owner, gid_t group); 
int fchown(int fd, uid_t owner, gid_t group);
int fchownat(int fd, const char *pathname, uid_t owner, gid_t group, int flag);
int lchown(const char *pathname, uid_t owner, gid_t group);
函數功能:
    改變對應文件的用戶ID和組ID.

只有特權進程才能使用chown函數來改變文件的用戶 ID,對於非特權進程,如果進程的有效用戶 ID 與文件的用戶 ID 相匹配,那麼可使用chown函數將文件的組 ID 更換為其從屬的任一屬組的 ID。

參數owner或者group為 -1 時表明不變。

5. 文件鏈接

結構成員變數st_nlink代表了文件的硬鏈接數。

文件鏈接類型分為硬鏈接符號鏈接,兩種區別在於:

  • 硬鏈接通常要求鏈接和文件在同一文件系統中;
  • 只有超級用戶才能創建指向目錄的硬鏈接(主要是為了防止在文件系統中引入迴圈)。

5.1 硬鏈接

硬鏈接通常要求鏈接和文件在同一文件夾,一般也不允許鏈接到目錄,因為鏈接到目錄可能會出現迴圈的情況,如:

有文件 /temp

在 /temp 中創建一個硬鏈接文件 link 和普通文件 a,那麼遍歷文件的時候,可能會出現以下情況:

/temp/a

/temp/link/a

/temp/link/link/a

...

上述情況下,如果是符號鏈接造成的迴圈,可以通過unlink函數解除,因為unlink不跟隨符號鏈接。

硬鏈接文件的創建函數如下:

#include <unistd.h>
int link(const char *existingpath, const char *newpath);
int linkat(int efd, const char *exitingpath, int nfd, const char *newpath, int flag);
參數說明:
    existingpath: 要創建硬鏈接的源文件的路徑,不應該是符號鏈接文件的路徑;
    newpath:      創建的硬鏈接的文件路徑,如果該路徑已存在,會報錯;
    flag:         通過該標誌位控制是否解引用鏈接文件.

硬鏈接就是為文件創建一個名字,多個文件名通過相同的 inode 編號,指向同一文件,操作文件時,隨便用哪一個都行。

5.2 符號鏈接

符號鏈接在使用上的關鍵就是看函數能不能處理符號鏈接,一般情況下對符號鏈接的處理都是處理鏈接到的文件,而非處理鏈接文件本身。

5.2.1 符號鏈接創建函數 symlink,symlinkat

#include <unistd.h>
int symlink(const char *pathname, const car *sympath);
int symlinkat(const char *pathname, int fd, const char *sympath);

pathname可以不存在,因為即使存在,也不能保證它不會被刪除,就會使該符號鏈接變為懸空鏈接,這時,對該符號鏈接解引用時都會出錯。

這裡要註意的是symlink的目標文件的路徑不是根據當前路徑計算的,而是根據符號鏈接文件的路徑計算的,如:

有目錄dir1,dir1中有文件a.txt,現在想在dir1目錄下創建a.txt文件的符號鏈接文件b.txt,
並不是
    symlink("./dir1/a.txt", "./dir1/b.txt");  -- 1
而是
    symlink("./a.txt", "./dir1/b.txt");       -- 2
1 中寫法的話會從 ./dir1/dir1/a.txt 中找 a.txt 文件,這樣明顯是不正確的
2 中寫法的話就是從 ./dir1/a.txt 中找 a.txt 文件

看上去會有點繞,shell 中的ls -s,命令創建符號鏈接文件也是這樣子的,否則會找不到。

符號鏈接的長度指的是指向的目標文件的文件名長度。

5.2.2 符號鏈接讀取函數 readlink,readlinkat

常用的open函數打開鏈接文件時是跟隨符號鏈接的,所以需要一種方法打開鏈接文件本身,並讀該鏈接中的名字,就有了以下函數:

#include <unistd.h>
ssize_t readlink(const char *restrict pathname, char *restrict buf, size_t bufsize);
ssize_t readlinkat(int fd, const char *restrict pathname, char *restrict buf, size_t bufsize);

鏈接文件的刪除必須滿足以下兩個條件:

  • 文件的鏈接數為 0;
  • 當前沒有進程打開該文件。

可通過以下函數刪除:

#include <unistd.h>
int unlink(const char *pathname); 
int unlinkat(int fd, const char *pathname, int flag);

要註意,unlink無法刪除目錄文件,刪除目錄文件要用rmdirremove函數。

6. 文件大小

stat結構成員st_size表示以位元組為單位的文件的長度,只對普通文件,目錄文件和鏈接文件有效。

對於普通文件,文件長度可以是 0,在讀這類文件時,將得到文件結束(end-of-file)指示;對於目錄,文件長度通常是一個數的整數倍,如 16 的整數倍或 512 的整數倍;對於符號鏈接,文件長度是在文件名中的位元組數,對於共用記憶體對象,該欄位表示對象的大小,如:

lib 文件是個鏈接文件,鏈接到 usr/lib
那麼連接文件 lib 的文件長度為 7,也就是名字長度

st_blksize是對文件 I/O 較合適的塊長度,一般是 4095。

st_blocks是所分配的實際 512 位元組塊塊數,並不是所有的 OS 都是 512 位元組,這個要根據實際情況區分。

對於文件長度,有幾種特殊情況:

6.1 文件空洞

文件空洞是文件的偏移量超過文件尾端,並寫入了某些數據後造成的。

6.2 文件截斷

有時候需要在文件尾端截去一些數據以縮短文件,這種情況下啊就需要用到文件截斷,相關函數如下:

#include <unistd.h>
int truncate(const char *pathname, off_t length);
int ftruncate(int fd, off_t length);

如果文件當前長度大於參數length,將會截斷;如果當前長度小於參數length,會在文件尾部添加一系列空位元組或是一個文件空洞。

du命令顯示的是文件實際所占的塊的大小,所以對於有空洞文件,可能會出現st_sizest_blocks大的情況,因為st_size 是包含空洞文件的空洞的。

7. 文件的時間

文件的時間可以分為以下三種:

  • 文件數據最後訪問時間:st_atime;
  • 文件數據最後修改時間:st_mtime;
  • 文件狀態最後改變時間:st_ctime。

需要註意修改時間和文件狀態改變的時間的區別,修改指的是文件內容最後一次被修改的時間,狀態更改指的是該文件 i 節點最後一次被修改的時間,如文件訪問許可權,用戶ID的更改等。

系統預設按照修改時間排序,也可以通過以下命令修改排序方式:

ls -u //按訪問時間排序
ls -c //按狀態更改時間排序

文件的訪問時間和修改時間是可以通過函數修改的,但是文件狀態不行,文件狀態發生變化時,會自動更新該欄位數據。utime函數調用成功後會將狀態最後改變時間設置為當前時間。

#include <utime.h>
int utime(const char *pathname, const struct utimbuf *buf);
參數說明:
    pathname: 文件所在路徑;
    buf:      要修改的時間值,為NULL的話,設置為當前時間;
返回說明:
    成功=0; 失敗=-1

utimbuf結構體定義如下:

struct utimbuf {
    time_t actime;  //訪問時間
    time_t modtime; //修改時間
};

Linux 還提供了源於 BSD 的utimes系統調用,其功能類似於utime

#include <sys/time.h>
int utimes(const char *pathname, const struct timeval tv[2]);

utimesutime的最大區別在於提供了微秒級別的時間。

還可以使用futimes函數使用文件描述符來指定文件,對於符號鏈接文件,可以使用lutimes函數來對符號鏈接本身做操作:

#include <sys/time.h>
int futimes(int fd, const struct timeval tv[2]);
int lutimes(const char *pathname, const struct timeval tv[2]); //不會對符號鏈接文件解引用

還可以使用納秒級別的時間修改函數:

#include <sys/stat.h>
int futimens(int fd, const struct timespec times[2]);
int utimensat(int fd, const char *path, const struct timespec times[2], int flag);
參數說明:
    times 數組的第一個是訪問時間,第二個是修改時間

結構體timespec定義如下:

struct timespec {
    time_t tv_sec;  //秒
    long   tv_nsec; //納秒
};

有以下幾種情況:

  • times 參數是一個空指針,那麼訪問時間和修改時間都設置為當前時間;
  • times 參數任意一個元素的tv_nsec欄位是 UTIME_NOW,相應的時間戳設置為當前時間(註意,不是tv_sec欄位);
  • times 參數任意一個元素的tv_nsec欄位是 UTIME_OMIT,相應的時間戳保持不變;
  • 如果既不是 UTIME_NOW,也不是 UTIME_OMIT,相應的時間戳設置為相應的tv_sectv_nsec欄位的值。

修改的條件是進程對該文件具備寫許可權,且進程的有效用戶 ID 等於 該文件的所有者 ID,或者是超級用戶。

對於utimensat函數,如果fd指定為AT_FDCWD,此時對path參數的解讀與utimes類似。flags參數一般為 0,但是對於符號鏈接文件,如果想對鏈接本身操作,可以將flags參數設置為AT_SYMLINK_NOFOLLOW

8. 設備特殊文件

每個文件系統所在的存儲設備都由其主、次設備號表示,可以使用major()minor()分別訪問主、次設備號。

系統中與每個文件名關聯的st_dev值是文件系統的設備號,st_ino欄位則該包含文件的 i 節點。利用以上兩者,可在所有文件系統中唯一標識某個文件。

只是有字元特殊文件和塊特殊文件才有st_rdev值,此值包含了實際設備的設備號。

9. 通用文件操作函數

9.1 文件重命名 rename/renameat

當不更換文件系統為一個文件重命名時,該文件的實際內容並未移動,只需要構造一個指向現有 i 節點的新目錄項,並刪除老的目錄項。鏈接計數並不會改變。

#include <stdio.h>
int rename(const char *oldname, const char *newname);
int renameat(int oldfd, const char *oldname, int newfd, const char *newname);

如果newname已存在,那麼會將其覆蓋,如果oldnamenewname一樣,則不發生變化。

以下幾點需要註意:

  • 如果 oldname 或 newname 指代的是符號鏈接,那麼處理的是符號鏈接本身,而非鏈接的文件;
  • 如果是對目錄文件重命名,要保證newname要麼不存在,要麼存在的目錄文件是個空目錄,因為rename是不會移動數據的,且要註意,newname不能包含oldname作為路徑名首碼;
  • rename只能用於同一文件系統。

9.2 文件刪除 remove,rmdir

rmdir函數可以刪除一個空目錄。

#include <unistd.h>
int rmdir(const char *pathname);

remove函數可以用來解除對一個文件或目錄的鏈接,對於文件來說,remove功能和unlink一樣,對於目錄來說,remove功能和rmdir一樣。

#include <stdio.h>
int remove(const char *pathname);

10. 目錄文件

10.1 目錄的創建和刪除

可以使用mkdirmkdirat函數創建目錄文件:

#include <sys/stat.h>
int mkdir(const char *pathname, mode_t mode);
int mkdirat(int fd, const char *pathname, mode_t mode);

目錄文件的創建所指定的文件訪問許可權 mode 由進程的文件模式創建屏蔽字修改(umask)。

常見的錯誤是指定與文件相同的 mode(只具備讀,寫許可權),但是,對於目錄,通常需要執行許可權,以允許訪問該目錄中的文件名。

可以使用rmdir函數刪除目錄文件:

#include <unistd.h>
int rmdir(const char *pathname);

10.2 讀目錄

#include <dirent.h>
DIR *opendir(const char *pathname);//路徑名方式打開一個目錄
DIR *fopendir(int fd);             //以文件描述符的方式打開一個目錄,可以將文件描述符轉換為 DIR 結構
struct dirent *readdir(DIR *dp);   //讀目錄,一次返回一條目錄
void rewinddir(DIR *dp);           //將目錄流重新移動到原點
int closedir(DIR *dp);             //關閉目錄
long telldir(DIR *dp);
void seekdir(DIR *dp, long loc);

opendir函數在執行時會為目錄相關聯的文件描述符設置close_on_exec標誌,以確保在執行exec時自動關閉該文件描述符。

dirent定義如下:

struct dirent {
	ino_t d_ino;       //inode節點號
	char  d_name[256]; //目錄文件名
};

10.3 文件樹遍歷 nftw()

#define _XOPEN_SOURCE 500
#include <ftw.h>

int nftw(const char *dirpath, int (*func)(const char *pathname, const struct stat *statbuf, int typeflag, struct FTW *ftwbuf), int nopenfd, int flags);
參數說明:
    dirpath: 目錄路徑;
    func:    目錄樹中每個文件要執行的函數;
    nopenfd: 可使用的文件描述符的最大值,一層一個;
    flags:   操作標誌;
函數功能:
    預設以前序遍歷方式遍歷文件樹,併為每個文件調用一個func函數

參數flags含義如下:

  • FTW_CHDIR:在處理目錄之前會先調用chdir函數進入該目錄,主要是配合func函數使用;
  • FTW_DEPTH:使用後序遍歷;
  • FTW_MOUNT:不會越界進入另一個文件系統;
  • FTW_PHYS:對符號鏈接文件不會解引用。

函數func用的是stat函數,所以對鏈接文件,預設是解引用的。

typeflag參數定義如下:

  • FTW_D:目錄文件;
  • FTW_DNR:是一個不能讀取的目錄文件;
  • FTW_DP:正在對一個目錄進行後序遍歷,當前項是一個目錄,其所包含的文件和子目錄已完成處理;
  • FTW_F:該文件的類型是除目錄和符號鏈接以外的任何類型;
  • FTW_NS:對該文件調用stat失敗;
  • FTW_SL:是一個符號鏈接文件;
  • FTW_SLN:是一個懸空的符號鏈接文件。

參數ftwbuf是一個FTW結構型的,定義如下:

struct FTW {
    int base;  //文件名在fpath中的偏移地址
    int level; //目錄樹的層次,也就是深度
};

每次調用func都需要返回一個整型值,如果返回 0,那麼nftw函數還會繼續對樹進行遍歷,若返回非 0 值,則通知nftw停止對樹的遍歷,nftw函數的返回值與func的返回值相同。

nftw內部實現會動態分配記憶體,所以如果直接通過longjmp函數跳轉出去,至少會引起記憶體泄露問題。

10.4 工作路徑

可以通過chdirfchddir函數來修改當前工作路徑:

#include <unistd.h>
int chdir(const char *pathname);
int fchdir(int fd);

需要註意的是工作路徑是進程的一個屬性,所以它隻影響調用chdir的進程本身,而不影響其他進程。

可以通過getcwd函數來獲取當前工作路徑:

#include <unistd.h>
char *getcwd(char *buf, size_t size);
參數說明:
	buf:  保存返回的當前工作路徑;
    size: 緩衝區大小,超過會報錯

如果buf為 NULL 的話,且size為 0 的話,系統會按需分配一個緩衝區,並將指向該緩衝區的指針作為函數的返回值。但是此時需要註意用完要釋放,否則會造成記憶體泄漏。

10.5 修改進程的根目錄 chroot()

#define _BSD_SOURCE
#include <unistd.h>
int chroot(const char *pathname);

chroot函數可以修改當前進程的根目錄,主要功能就在於限制當前進程訪問其他文件,只能訪問當前根目錄下的文件。

但是這個機制並不是完全安全的:

對於特權級程式來說,可以使用mknod函數創建一個記憶體設備文件,然後可以通過這個記憶體設備文件訪問 RAM 的內容;

對於非特權級程式來說,也要註意以下幾種情況:

  • 調用chroot修改完根目錄後,沒有立即將當前工作目錄更新過來,此時還是可以訪問其他文件的;
  • 如果在調用chroot之前有打開一個監禁區外的文件,那麼在調用chroot後可以利用這個文件描述符實現越獄;
  • 利用域套接字也是可以實現越獄的。

10.6 解析路徑名 realpath()

#include <stdlib.h>
char *realpath(const char *pathname, char *resolved_path);
參數說明:
    pathname:      要解析的路徑;
    resolved_path: 保存解析結果;
返回說明:
    成功=返回指向結果的指針; 失敗=NULL

會對pathname一 一解析,最後生成絕對路徑名。

10.7 解析路徑名字元串 dirname()和basename()

#include <libgen.h>
char *dirname(char *pathname);  //返回一個路徑的目錄部分
char *basename(char *pathname); //返回一個路徑的文件名部分

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • JUC學習 1.什麼是JUC java.util 工具包、包、分類 業務:普通的線程代碼 Thread Runnable 沒有返回值、效率相比入 Callable 相對較低! 2.線程和進程 線程、進程,如果不能使用一句話說出來的技術,不扎實! 進程:一個程式,QQ.exe Music.exe 程式 ...
  • 拓撲排序 簡介 拓撲排序是將偏序的數據線性化的一種排序方法。複習下偏序和全序的概念: 全序關係是偏序關係的一個子集。 全序是集合內任何一對元素都是可比較的,比如數軸上的點都具有一個線性的數值,因此根據數值就可以進行比較。 偏序是集合內不是所有元素都是可以比較的,比如平面內的點由橫坐標和縱坐標組成,是 ...
  • 背景 之前有文章提供了springboot多數據源動態註冊切換的整合方案,在後續使用過程中,發現在事務控制中有多種bug發生,決定對此問題進行分析與解決 前情提要 多數據源切換流程結構圖如下所示,包含幾個組成元素 自定義的數據源配置處理,通過DruidDataSource對象動態註冊到系統中 自定義 ...
  • 1.在pom.xml加入SpringSecurity的依賴 <!-- SpringSecurity對Web應用進行許可權管理 --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-secu ...
  • 使用Metalama為VisualStudio "重構"\ LiveTemplate 菜單中動態添加功能 ...
  • C#自定義配置文件教程,C#App.Config自定義配置文件教程,C#web.Config自定義配置節點 ...
  • 我前面幾篇隨筆介紹了關於幾篇關於SqlSugar的基礎封裝,已經可以直接應用在Winform項目開發上,並且基礎介面也通過了單元測試,同時測試通過了一些Winform功能頁面;本篇隨筆繼續深化應用開發,著手在在.net6框架的Web API上開發應用,也就是基於.net core的Web API應用... ...
  • 本文講講 Ubuntu 18 及以上版本配置 IP 的方法,為什麼它值得一講,因為以 Ubuntu 16 為首的版本的配置方法已經不適用了,如果你還不知道,那本文正好 get 一個新技能。 Ubuntu 18 之後版本配置方法 需要使用 netplan 工具。 對應配置文件: /etc/netpla ...
一周排行
    -Advertisement-
    Play Games
  • Github / Gitee QQ群(1群) : 813100564 / QQ群(2群) : 579033769 視頻教學 介紹 MiniWord .NET Word模板引擎,藉由Word模板和數據簡單、快速生成文件。 Getting Started 安裝 nuget link : https:// ...
  • Array.Sort Array類中相當實用的我認為是Sort方法,相比起冗長的冒泡排序,它的出現讓排序更加的簡化 結果如下: 還可以聲明一個靜態方法用來專門調用指定數組排序,從名為 array 的一維數組中 a 索引處開始,到 b 元素 從小到大排序。 註意: a + b 不能大於 array 的 ...
  • 前言 在上一篇文章CLR類型系統概述里提到,當運行時掛起時, 垃圾回收會執行堆棧遍歷器(stack walker)去拿到堆棧上值類型的大小和堆棧根。這裡我們來翻譯BotR里一篇專門介紹Stackwalking的文章,希望能加深理解。 順便說一句,StackWalker在中文里似乎還沒有統一的翻譯,J ...
  • 使用過 nginx 的小伙伴應該都知道,這個中間件是可以設置跨域的,作為今天的主角,同樣的 反向代理中間件的 YARP 毫無意外也支持了跨域請求設置。 有些小伙伴可能會問了,怎樣才算是跨域呢? 在 HTML 中,一些標簽,例如 img、a 等,還有我們非常熟悉的 Ajax,都是可以指向非本站的資源的 ...
  • 什麼是Git Git 是一個開源的分散式版本控制系統,用於敏捷高效地處理任何或小或大的項目。 Git 是 Linus Torvalds 為了幫助管理 Linux 內核開發而開發的一個開放源碼的版本控制軟體。 Git 與常用的版本控制工具 CVS, Subversion 等不同,它採用了分散式版本庫的 ...
  • 首先CR3是什麼,CR3是一個寄存器,該寄存器內保存有頁目錄表物理地址(PDBR地址),其實CR3內部存放的就是頁目錄表的記憶體基地址,運用CR3切換可實現對特定進程記憶體地址的強制讀寫操作,此類讀寫屬於有痕讀寫,多數驅動保護都會將這個地址改為無效,此時CR3讀寫就失效了,當然如果能找到CR3的正確地址... ...
  • 說明 onlyoffice為一款開源的office線上編輯組件,提供word/excel/ppt編輯保存操作 以下操作均基於centos8系統,officeonly鏡像版本7.1.2.23 鏡像下載地址:https://yunpan.360.cn/surl_y87CKKcPdY4 (提取碼:1f92 ...
  • 二叉樹查找指定的節點 前序查找的思路 1.先判斷當前節點的no是否等於要查找的 2.如果是相等,則返回當前節點 3.如果不等,則判斷當前節點的左子節點是否為空,如果不為空,則遞歸前序查找 4.如果左遞歸前序查找,找到節點,則返回,否繼續判斷,當前的節點的右子節點是否為空,如果不為空,則繼續向右遞歸前 ...
  • ##Invalid bound statement (not found)出現原因和解決方法 ###前言: 想必各位小伙伴在碼路上經常會碰到奇奇怪怪的事情,比如出現Invalid bound statement (not found),那今天我就來分析以下出現此問題的原因。 其實出現這個問題實質就是 ...
  • ###一、背景知識 爬蟲的本質就是一個socket客戶端與服務端的通信過程,如果我們有多個url待爬取,只用一個線程且採用串列的方式執行,那隻能等待爬取一個結束後才能繼續下一個,效率會非常低。 需要強調的是:對於單線程下串列N個任務,並不完全等同於低效,如果這N個任務都是純計算的任務,那麼該線程對c ...