a亚洲精品_精品国产91乱码一区二区三区_亚洲精品在线免费观看视频_欧美日韩亚洲国产综合_久久久久久久久久久成人_在线区

首頁 > 系統 > Unix > 正文

UNIX高級環境編程(4)Files And Directories

2024-06-28 13:21:48
字體:
來源:轉載
供稿:網友
UNIX高級環境編程(4)Files And Directories - umask、chmod、文件系統組織結構和鏈接

本篇主要介紹文件和文件系統中常用的一些函數,文件系統的組織結構和硬鏈接、符號鏈接。

通過對這些知識的了解,可以對linux文件系統有更為全面的了解。

?

1 umask函數

之前我們已經了解了每個文件與權限相關的9個位(bit),我們現在來了解一下當每個進程創建文件時默認會設置該文件的文件權限(the file mode creation mask)。

umask函數設置該進程默認創建文件的權限掩碼(the file mode creation mask),并且返回之前的權限掩碼值。

#include <sys/stat.h>

mode_t umask(mode_t cmask);

the file mode creation mask的作用:當進程創建新文件時,會根據這個掩碼值創建文件,在掩碼值中打開的位,對應的新文件的權限位會被關閉。

umask的功能簡單地說就是創建新文件時屏蔽掉用戶不希望生效的權限位。

Example:

#include "apue.h"

#include <fcntl.h>

?

#define RWRWRW (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)

?

int

main(void)

{

? ? umask(0);

? ? if (creat("foo", RWRWRW) < 0)

? ? ? ? err_sys("creat error for foo");

? ? umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);

? ? if (creat("bar", RWRWRW) < 0)

? ? ? ? err_sys("creat error for bar");

? ? exit(0);

}

運行截圖:

NewImage

結果說明:

shell的umask命令顯示當前文件創建權限掩碼。

0022表示創建出來的新文件,組用戶和other用戶沒有寫該文件的權限。

在程序中,首先用默認的權限設置創建了文件foo,它的權限位666,至于為什么當前用戶也沒有執行權限,我還沒搞清楚,留一個坑 @suzhou。

然后修改umask值為0077,這樣就屏蔽掉了組用戶和其他用戶的讀寫權限位,因此再創建新文件bar,它的權限位是600。

?

2 chmod、fchmod和fchmodat函數

函數作用:修改已有文件的權限位。

函數聲明:

#include <sys/stat.h>

int chmod(const char *pathname, mode_t mode);

int fchmod(int fd, mode_t mode);

int fchmodeat(int fd, const char *pathname, mode_t mode, int flag);

?區別:

  • chmod:作用在某個指定的文件上
  • fchmod:作用在某個指定的已經打開的文件上
  • fchmodat:類似之前的函數,當pathname為絕對路徑,或者fd取值為AT_FDCWD并且pathname為相對路徑時,fchmodat函數的作用和chmod相同;否則,工作目錄為fd指定父目錄路徑加上pathname為父目錄的子目錄。參數flag用來指定fchmodeat的行為:取值為AT_SYMLINK_NOFOLLOW時,該權限修改并不追蹤符號鏈接,只修改該符號鏈接文件的權限。

權限要求:要修改一個文件的權限,需要當前進程的effective user ID和文件的所有者ID相同,或者進程有超級用戶權限。

代表各個權限位的常量如下表所示:

NewImage

Example:

修改在上例中創建的兩個文件foo和bar的權限。

#include "apue.h"

?

int

main(void)

{

? ? struct stat ? ? statbuf;

?

? ? /* turn on set-group-ID and turn off group-execute */

?

? ? if (stat("foo", &statbuf) < 0)

? ? ? ? err_sys("stat error for foo");

? ? if (chmod("foo", (statbuf.st_mode & ~S_IXGRP) | S_ISGID) < 0)

? ? ? ? err_sys("chmod error for foo");

?

? ? /* set absolute mode to "rw-r--r--" */

?

? ? if (chmod("bar", S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0)

? ? ? ? err_sys("chmod error for bar");

?

? ? exit(0);

}

運行結果:

NewImage

結果說明:

  • 執行程序之前,foo的權限為666,程序中首先使用stat函數獲取文件的信息存在statbuf中,置組用戶執行位(S_IXGRP)為0,并置該位為S(S_ISGID)。
  • 執行程序之前,bar的權限位600,程序中設置該文件的組用戶和其他用戶有讀權限,設置成功之后,可以看到bar的文件權限位644.

高級權限(SUID/SGID/Sticky Bit )檔案特殊權限 說明:

  • SUID (Set UID): 會制作出 s 的權限,權限數字是 4 。這個權限只能作用于可執行文件,不可作用于目錄,shell script也不行。使一般使用者在執行文件時,暫時具有該文件擁有者的權限。
  • SGID (Set GID): 制作出 s 權限,權限數字是 2 。這個權限可以作用于目錄和文件。作用于目錄時,在該目錄下建立的所有文件和目錄的group都會是該目錄的group;作用于文件時,則不論使用者是誰,在執行該程序的時候, 他的有效群組 (effective group) 將會變成該程序的群組所有人 (group id)。
  • Sticky Bit: 制作出 t 權限,權限數字是 1 。這個權限只能作用于目錄。在該權限作用的目錄里,如果使用者具有w,x權限,那么他所創建的文件和目錄,只有檔案的擁有者和root才能刪除。
  • 注意:在字符表示權限時,s和t是出現在x的位置的,意思是在可執行的文件和目錄時,它們才是有意義的。如果文件或目錄本身u、g或o就沒有可執行權限,那么它所在的權限組出現的是S或者T,表示空的意思。用數字表示權限,就是在rwx前面加上4、2、1的組合就是了,如1777。

?

3 Sticky Bit(S_ISVTX)

Sticky bit有一個冗長的歷史,這里并不贅述。

作用:如果文件夾的sticky bit被打開,則文件夾中的文件被刪除或者重命名需要滿足一下的條件之一:當前用戶對該文件夾有寫權限;當前用戶是該文件的所有者;當前用戶是該文件夾的所有者;當前用戶是超級用戶。

典型的打開了sticky bit的兩個文件夾是/tmp 和/var/tmp,這兩個文件夾對所有用戶都有讀寫和執行權限,這樣所有用戶都可以在該文件夾下創建文件,但是并不能刪除由其他用戶所有的文件。

NewImage

NewImage

?

4 chown、fchown、fchownat和lchown函數

函數作用:修改文件所屬的用戶ID和組ID,如果參數owner和group有一個的值為-1,對應的ID值不變。

#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 grou, int flag);

int lchown(const char* pathname, uid_t owner, gid_t group);

區別:

當目標文件不是一個符號鏈接時,上面的四個函數的作用基本相同。

當目標文件是一個符號鏈接文件時,并且函數lchown和fchownat的flag設置為AT_SYMLINK_NOFOLLOW時,這兩個函數修改符號鏈接文件本身的所有者,而不是符號鏈接指向的文件。

fchown函數修改fd代表的已打開文件的所有者。因為fchown操作的是已經打開的文件,所以它不可以用來修改符號鏈接的所有者。

fchownat函數的行為:當參數pathname是絕對路徑,或者參數fd的值為AT_FDCWD并且pathname為相對路徑時,fchownat函數的行為和chown和lchown類似;具體來說,flag取值AT_SYMLINK_NOFOLLOW時,行為和lchown類似;否則行為和chown類似。當fd代表已打開的文件夾,pathname為一個相對路徑時,目標文件為以fd為父目錄,pathname為相對的子目錄所指的文件。

?

5 文件大小(File Size)

stat結構體中的成員st_size表示文件大小字節數。

字段st_size只對常規文件(regular files)、目錄文件(directories)和符號鏈接文件(symbolic links)。

更多的細節:

  • 常規文件大小允許為0;
  • 目錄文件的大小往往是整數,如16或512;
  • 符號鏈接文件的大小是被鏈接文件名的長度,如符號鏈接lib—>usr/lib,大小為7,正好是字符串“usr/lib”長度,并不需要像C風格字符串一樣+1。

?

6 文件截斷(File Truncation)

有時候我們需要通過丟棄文件結尾一部分來截短文件。

函數聲明:

#include <unistd.h>

int truncate(const char* pathname, off_t length);

itn ftruncate(int fd, off_t length);

函數作用:把目標文件截斷到參數length指定的長度。

參數:如果參數length的值比指定文件的長度小,則文件超過length長度的部分不可讀;如果length的值比指定文件的長度大,文件長度擴大到length,擴充部分填充0(可能是一個hole)

?

7 文件系統(File Systems)

為了理解文件鏈接,我們需要對Unix文件系統的結構有一個概念性地認識。

一個磁盤(disk drive)可以被分為多個分區(partition),每個分區都可以包含一個文件系統(file system)。

inode是一個定長的索引,包含了一個文件的大部分信息。

NewImage

關于 i-nodes和data blocks的內存布局如下圖所示:

NewImage

說明:

  • 兩個目錄索引(directory entry)指向同一個i-node索引(i-node entry)。即多個文件夾包含同一個文件(可能通過硬鏈接或軟鏈接),每一個inode都由一個字段link count,包含指向該i-node的目錄索引數。只有當該數值(link count)為0時,該文件才會被刪除(釋放文件所占用的data blocks)。索引后面介紹的移除目錄的函數不叫delete,而是unlink。在stat數據結構中,link count對應的字段為st_nlink。這里提到的鏈接為硬鏈接。
  • 另外一種鏈接為符號鏈接。符號鏈接文件對應的data block中存有該鏈接指向文件的文件名(之前有提到)。對應的i-node中文件類型字段的值應為S_IFLINK,告訴文件系統當前文件為符號鏈接。
  • i-node包含了對應文件的所有信息:如文件類型,指向文件數據塊的指針,文件權限位,文件大小等。stat數據結構中得大部分字段都是從i-node中獲取。有兩個字段值存在目錄索引中:文件名(file name)和i-node數(i-node number)。i-node數目對應的數據類型為ino_t。

我們再了解一下目錄文件的link count字段。

當我們執行下面的命令:

mkdir testdir

?內存中數據組織結構如下所示:

NewImage

從上圖可以看到,目錄下每多加一個文件或文件夾,link count都會加1。

圖中顯示的都很清楚,不做過多贅述(其實是我懶了,哈哈...)

?

8 link、linkat、unlink、unlinkat和remove函數

從前面可以知道,多個目錄索引可以指向同一個文件,即多個目錄可以包含同一個文件。

link和linkat的作用是創建一個已存在文件的鏈接。

函數聲明:

#include <unistd.h>

int link(const char* existingpath, const char* newpath);

int linkat(int efd, const char* existingpath, int nfd, const char* newpath, int flag);

函數細節:

  • 函數創建一個新目錄索引newpath,關聯到已存在文件existingpath;
  • 如果newpath已經存在,返回錯誤;
  • newpath只有最后一個文件名會被創建,路徑其他部分必須已存在;
  • linkat的參數的作用和前面xxxat函數介紹過的類似,這里不再贅述;
  • 如果被鏈接的文件是一個符號鏈接文件,參數flag用來控制當前新建的鏈接是關聯到該符號鏈接(默認行為),還是該鏈接指向的文件(AT_SYMLINK_FOLLOW);
  • 一般來說不可以擴文件系統創建鏈接,防止文件系統間的硬鏈接循環;
下面再了解一下unlink函數。

函數聲明:

#include <unistd.h>

int unlink(const char* pathname);

int unlinkat(int fd, const char* pathname, int flag);

函數細節:

  • 函數會刪除目錄索引,并且對應文件的inode的link count減1,如果link count不為0,則說明還有其他目錄索引指向該文件,不刪除該文件,否則刪除該文件;
  • 進程必須對該目錄有寫權限和執行權限;
  • 如果pathname是一個符號鏈接,unlink移除該符號鏈接文件,而不是該符號鏈接指向的實體文件。沒有函數實現直接刪除實體文件這個功能。

和unlink類似功能的函數remove:

#include <stdio.h>

int remove(const char* pathname);

函數細節:

  • remove一個文件相當于調用unlink,remove一個文件夾相當于調用rmdir(后面會介紹)。

?

9 rename和renameat函數

函數聲明:

#include <stdio.h>

int rename(const char* oldname, const char* newname);

int renameat(int oldfd, const char* oldname, int newfd, const char* newname);

函數細節:

  • 如果oldname不是一個目錄路徑,說明我們正在重命名一個文件或符號鏈接。這里newname已存在,則不可以是一個已存在目錄的名字。如果newname已存在并且不是目錄名,則刪除已有文件newname,將oldname所指文件重命名為newname。權限要求:我們必須有包含oldname和newname文件的目錄的寫權限。
  • 如果oldname是一個目錄路徑,則我們在重命名一個目錄。如果newname已存在,它必須是一個目錄的名字,并且該目錄必須是空目錄(只有. 和 ..索引)。這種情況下,刪除已存在目錄newname,將待重命名目錄oldname重命名為newname。還需要注意的,newname不能包含oldname。例如:不能將目錄/usr/foo重命名為/usr/foo/testdir,因為/usr/foo是新目錄名的前綴,無法被刪除。
  • 如果oldname或newname為一個符號鏈接名,那么被重命名的時符號鏈接本身,不是符號鏈接指向的文件。
  • 我們不能重命名dot(.)和dot-dot(..),更確切地說,這兩個特殊名不能作為路徑名的最后一部分。
  • 如果oldname和newname相同,則函數返回成功,不做任何事情。

對renameat函數的參數含義不再贅述。

?

10 符號鏈接(Symbolic Links)

符號鏈接可以認為是文件的非直接指針,而硬鏈接(hard link)可以看做是文件的直接指針,因為它直接指向文件的inode。

  • 硬鏈接通常不可以跨文件系統;
  • 只有超級用戶可以對一個目錄創建硬鏈接。

符號鏈接并沒有文件系統間的限制。

由于符號鏈接的存在,我們在操作文件或目錄時,總是需要知道該文件是否是一個符號鏈接,然后判斷該函數的操作對象是符號鏈接指向的文件還是符號鏈接本身。

下圖總結了一些常用函數是否追蹤鏈接,僅供參考:

NewImage

Example:

NewImage

鏈接情況如下圖所示:

NewImage

可以發現,由于符號鏈接而出現了一個環。

再來試驗一下:

NewImage

我們發現,這樣會無限循環下去。

解除循環的方法就是使用unlink刪除該符號鏈接。需要注意的一點是,如果這里出現的鏈接是硬鏈接,則刪除這個循環鏈接會困難得多。因此只有管理員權限才可以用link函數對一個文件建立硬鏈接。

有時候符號鏈接會造成一些迷惑的事情,如果使用者對符號鏈接不熟悉的話。

如下所示:

NewImage

符號鏈接可以鏈接一個不存在的文件,如果使用者對符號鏈接不熟悉,則很容易被這種情況迷惑。

這時,可以使用ls的”-l"命令看到有 “->"符號,或者前面符號位第一位的”l”表示符號鏈接,或者用ls得“-F”命令,符號鏈接文件后面會跟一個@符。

?

11 創建和讀取符號鏈接(Symbolic Links)

函數作用:創建符號鏈接

函數聲明:

#include <unistd.h>

int symlink(const char *actualpath, const char* sympath);

int symlinkat(const char* actualpath, int fd, const char* sympath);

參數說明:

  • 函數并不要求actualpath一定存在,在上面的例子中也看到了這一點。

因為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);

如果調用成功,函數從符號鏈接中讀取buf長的內容。

?

?

參考資料:

《Advanced PRogramming in the UNIX Envinronment 3rd》

?


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 美国一级黄色片 | 可以在线观看的黄色 | 色老头av| 成人精品网站在线观看 | 中文字幕av一区二区三区 | www久久久 | 免费在线黄色电影 | 99日韩 | 99re在线| 最新日韩av | 国产伦精品一区二区三区四区视频 | 欧美日韩1区2区3区 www.日韩精品 | 久久人人爽av | 黄色一级毛片 | 欧美日韩在线视频一区二区 | 中文字幕亚洲欧美日韩在线不卡 | 亚洲精品一区中文字幕乱码 | 久久久国产精品 | 毛片毛片毛片毛片毛片毛片 | 性国产xxxx乳高跟 | 久久人| 5060毛片| 久久精品黄色 | 亚洲+变态+欧美+另类+精品 | 一区二区视频 | 欧美日韩国产一区二区三区不卡 | 国产精品不卡视频 | 国产精品精品视频一区二区三区 | av中文字幕网 | 中文字幕一二三区有限公司 | 久久草视频 | 亚洲欧洲精品成人久久奇米网 | 日韩在线观看成人 | 成人午夜av | 色婷婷狠狠 | 国产精品久久久久久久久免费软件 | 国产亲子乱弄免费视频 | 亚洲最黄视频 | 国产精品三级在线 | 成人欧美一区二区三区色青冈 | 亚洲日韩欧美一区二区在线 |