apue chapter 4 文件和目錄

1.文件信息結構體node

struct stat{
    mode_t st_mode; //file type and permissions
    ino_t st_ino;         //i-node number (serial number)
    dev_t st_dev;       //device number (file system)
    dev_t st_rdev;      //device number for special files
    nlink_t st_nlink;   //number of links
    uid_t st_uid;        //user ID of owner
    gid_t st_gid;        //group ID of owner
    off_t st_size;      //size in bytes,for regular files
    struct timespec st_atime;//time of last access
    struct timespec st_mtime;//time of last modification
    struct timespec st_ctime;//time of last file status change
    blksize_t st_blksize;       //best I/O block size
    blkcnt_t st_blocks;         //number of disk blocks allocated
};

 

2.文件類型ios

(1)普通文件。這是最經常使用的文件類型,這種文件包含了某種形式的數據。bash

(2)目錄文件。這種文件包含了其餘文件的名字以及指向與這些文件有關信息的指針。網絡

(3)塊特殊文件。這種類型的文件提供對設備帶緩衝的訪問,每次訪問以固定長度爲單位進行。app

(4)字符特殊文件。這種類型的文件提供對設備不帶緩衝的訪問,每次訪問長度可變。系統中的全部設備要麼是字符特殊文件,要麼是塊特殊文件。socket

(5)FIFO。這種類型的文件用於進程間通訊,有時也稱爲命名管道。函數

(6)套接字。這種類型的文件用於進程間的網絡通訊。套接字也可用於在一臺宿主機上進程之間的非網絡通訊。測試

(7)符號連接。這種類型的文件指向另外一個文件。ui

 示例程序:判斷路徑對應文件的類型:spa

#include "apue.h"
#include <iostream>
using namespace std;
int main(int argc,char *argv[]){
    int i;
    struct stat buf;
    char *ptr;
    for (int i=1;i<argc;i++){
        cout<<argv[i]<<" :";
        if(lstat(argv[i],&buf)<0){
            cout<<"latat error!"<<endl;
        }
        if(S_ISREG(buf.st_mode))
            ptr="regular";
        else if(S_ISDIR(buf.st_mode))
            ptr="directory";
        else if(S_ISCHR(buf.st_mode))
            ptr="character special";
        else if(S_ISBLK(buf.st_mode))
            ptr="block special";
        else if(S_ISFIFO(buf.st_mode))
            ptr="fifo";
        else if(S_ISLNK(buf.st_mode))
            ptr="symbolic link";
        else if(S_ISSOCK(buf.st_mode))
            ptr="socket";
        else
            ptr="* * unknown! * *";
        cout<<ptr<<endl;
    }
    return 0;
}

運行輸出:

/etc/passwd :regular
/etc :directory
/dev/log :socket
/var/lib :directory
/dev/sr0 :block special
/dev/tty :character special
/dev/cdrom :symbolic link

3.文件訪問權限:

  每一個文件有9個訪問權限位,可將它們分紅3類。

st_mode 屏蔽 含義
S_IRUSR 用戶讀
S_IWUSR 用戶寫
S_IXUSR 用戶執行
S_IRGRP 組讀
S_IWGRP 組寫
S_IXGRP 組執行
S_IROTH 其餘讀
S_IWOTH 其餘寫
S_IXOTH 其餘執行

 

 

 

 

 

  

 

 

 

 

文件訪問權限:

  1. 咱們用名字打開任一類型的文件時,對該名字中包含的每個目錄,包括它可能隱含的當前工做目錄都應該具備執行權限。
  2. 對於一個文件的讀權限決定了咱們是否可以打開現有文件進行讀操做。
  3. 對於一個文件的寫權限決定了咱們是否可以打開現有文件進行寫操做。
  4. 爲了在一個目錄中建立一個新文件,必須對該目錄具備寫權限和執行權限。
  5. 爲了刪除一個現有文件,必須對包含該文件的目錄具備寫權限和執行權限,對該文件自己不須要讀、寫權限。

進程每次打開、建立或者刪除一個文件的時候,內核就進行文件訪問權限測試,以下:

(1)若進程的有效用戶ID爲0(超級用戶),則容許訪問。

(2)若進程的有效用戶ID等於文件的全部者ID,那麼若是全部者適當的訪問權限位被設置,則容許訪問。訪問權限與文件中該用戶對應的權限設置有關。

(3)若進程的有效ID或者進程的附屬組ID之一等於文件的組ID,那麼若是組適當的訪問權限位被設置,則容許訪問。

(4)若其餘用戶適當的訪問權限被設置,則容許訪問。

4.新文件和目錄的全部權

  新文件的用戶ID設置爲進程的有效用戶ID;

  新文件的組ID能夠設置爲進程的有效組ID或者是它所在的目錄的組ID。

5.函數access和faccessat

  函數access和faccessat按照實際用戶ID和實際組ID進程訪問的權限測試。

#include <unistd.h>
int access(const char *pathname ,int mode);
int faccessat(int fd,const char *pathname,int mode ,int flag);
//flag用於改變faccessat函數的行爲,若是flag設置爲AT_EACCESS,訪問檢查用的是調用進程的有效用戶ID和有效組ID,而不是實際用戶ID和實際組ID。
//兩個函數的返回值:若成功,返回0,不然返回-1

示例程序:

#include <iostream>
#include "apue.h"
#include <fcntl.h>
using namespace std;

int main(int argc ,char *argv[] ) {
    if(argc !=2){
        cout<<"usage: apue <pathname>"<<endl;
        return -1;
    }
    if(access(argv[1],R_OK)<0){
        cout<<"access error for "<<argv[1]<<endl;
    }
    else
        cout<<"read access ok!"<<endl;
    if(open(argv[1],O_RDONLY)<0){
        cout<<"open error for " <<argv[1]<<endl;
    }
    else
        cout<<"open reading ok!"<<endl;
    return 0;
}

程序運行:

apue apue
read access ok!
open for reading ok!

apue /etc/shadow
read access error for /etc/shadow
open error fro /etc/shadow

6.函數umask

  至此,咱們說明了與每一個文件相關聯的9個訪問權限位,在此基礎上咱們能夠說明與每一個進程相關聯的文件模式建立屏蔽字。

  umask函數爲進程設置文件模式建立屏蔽字,並返回以前的值。

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

  其中,參數cmask是由3中表格列出的9個常量(S_IRUSR , S_IWUSR)中的若干位按照或構成的。

示例程序:

#include "apue.h"
#include <iostream>
#include <fcntl.h>
using namespace std;
#define PWRWRW (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)//禁止全部用戶的執行權限
int main(){
    umask(0);
    if(creat("foo",PWRWRW)<0){
        cout<<"creat error for foo"<<endl;
    }
    umask(S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
    if(creat("bar",PWRWRW)<0)
        cout<<"creat error for bar"<<endl;

    return 0;
}

測試生成的文件的權限:

ll foo
ll bar

測試結果:

-rw-rw-rw-  1 zhouyang  staff  0  3 25 23:34 foo
-rw-------  1 zhouyang  staff  0  3 25 23:34 bar

  因此,爲了使得全部的用戶對文件都有訪問權限,那麼應該設置umask值爲0。可見,最終文件的權限爲PWRWRW設定的權限 - umask設定的權限。也就是說umask設定的是須要屏蔽的權限。

  在bash中,umask的值也設定了建立文件的時候屏蔽的權限位,以下所示:

zhouyangdeMacBook-Pro:Debug zhouyang$ umask -S
u=rwx,g=rx,o=rx
zhouyangdeMacBook-Pro:Debug zhouyang$ umask 0002
zhouyangdeMacBook-Pro:Debug zhouyang$ umask -S
u=rwx,g=rwx,o=rx

7.函數chmod,fchmod和fchmodat

  chmod,fchmod和fchmodat函數使咱們能夠更改現有文件的訪問權限。

#include <sys/stat.h>
int chmod(const char *pathname);
int fchmod(int fd,mode_t mode);
int fchmodat(int fd,const char * pathname,mode_t mode,int flag);
//成功返回0,出錯返回-1.

  爲了改變一個文件的權限位,進程的有效用戶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 其餘執行

 

 

 

 

 

 

 

 

 

 

  

 

 

  

  

  注意,咱們增長了6項,其中9個是3中表格中的權限。咱們另外增長了6個,分別是兩個設置ID常量(S_ISUID,S_ISGID)、保存正文常量(S_ISVTX),以及3個組合常量。

  程序示例:修改6中程序生成的文件的權限:

#include "apue.h"
#include <iostream>
#include <fcntl.h>
using namespace std;
int main(){
    struct stat statbuf;
    if(stat("foo",&statbuf)<0){
        cout<<"stat error for foo"<<endl;
    }
    //foo.mode_t = ( rw-rw-rw- & ~ S_IXGRP )|S_ISGID
    //turn on set-group-id and turn off group-execute
    //S_ISGID 執行時設置組ID
    if(chmod("foo",(statbuf.st_mode & ~S_IXGRP)|S_ISGID)<0){
        cout<<"chmod error for foo \n";
    }
    //set absolute mode to "rw-r--r--"
    if(chmod("bar",S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)<0)
        cout<<"chmod error for bar"<<endl;
    return 0;

}

最終文件foo和bar的權限分別爲:

rw-rwSrw-

-rw-r--r--

8.粘着位

  S_ISVTX位有一段有趣的歷史。在UNIX還沒有使用請求分頁式技術的早期版本中,S_ISVTX位被稱爲粘着位。若是一個可執行程序文件的這一位置被設置了,那麼當改程序第一次被執行的時候,在其終止的時候,程序正文的一個副本仍然板寸在交換區。這使得下次執行改程序時可以較快地將其載入內存。

9.函數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 group,int flag)
int lchown(const char *pathname,uid_t owner,gid_t group)
//成功返回0,不然返回-1

 10.文件長度

 stat結構成員st_size 表示以字節爲單位的文件的長度。此字段只對普通的文件、目錄文件和符號連接有意義。

  現今,大多數現代的UNIX系統提供字段st_blksize和st_blocks。其中,第一個是對文件I/O較爲合適的塊長度,第二個是所分配的實際的512字節塊塊數目。

11.文件截斷                                                                             

#include <unistd.h>
int truncate (const char *pathname,off_t length);
int ftruncate(int fd,off_t length);
//兩個函數的返回值;若成功,返回0,若出錯,則返回-1

  這兩個函數將一個現有文件長度截斷位length。若是該文件之前的長度大於length,則超過length之外的數據就不能再訪問。若是之前的長度小於length,文件長度將增長,在之前的文件尾端和新的文件尾端之間的數據將讀做0。

12.文件系統

                        文件系統結構圖

(1)硬連接和軟連接

  在Linux中,多個文件名指向同一索引節點是存在的。通常這種鏈接就是硬連接。

  軟連接文件的實際內容包含了該符號連接所指向的文件的名字。(符號連接就是軟連接

(2)i結點

  i結點包含了文件有關的全部信息:文件類型、文件訪問權限位、文件長度和指向文件數據塊的指針等。stat結構中的大多數信息來自結點。只有兩項重要數據存放在目錄項中:文件名和結點編號。

13.函數link、linkat、unlink、unlinkat和remove

建立硬連接:

#include <unistd.h>
int link(const char * existpath,const char * newpath);
int linkat(int efd,const char *existingpath,int nfd,const char *newpath,int flag);
//成功後返回0,不然返回-1

刪除硬連接:

#include <unistd.h>
int unlink(const char *pathname);
int unlinkat(int fd,const char *pathname,int flag);
//成功後返回0,不然返回-1

示例程序:

#include "apue.h"
#include <iostream>
#include <fcntl.h>
using namespace std;
int main(){
    //是否打開文件出錯
    if(open("tempfile",O_RDWR)<0){
        cout<<"open error!"<<endl;
    }
    //刪除tempfile的硬連接
    if(unlink("tempfile")<0)
        cout<<"unlink error!"<<endl;
    cout<<"file unlinked!"<<endl;
    //此時文件佔有的空間仍然沒有釋放
    sleep(15);
    cout<<"done!"<<endl;
    return  0;
}
//此時文件tempfile已經徹底刪除

14.重命名文件

#include <stdio.h>
int rename(const char *oldname,const char * newname);
int renameat(int oldfd,const char *oldname,int newfd,const char *newname);
//成功返回0,失敗返回-1

 15.建立和讀取符號連接

能夠用symlink或者symlinkat函數建立一個符號連接:

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

使用下述函數能夠打開符號連接自己:

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

16.文件的時間

對每個文件維護如下3個時間字段,以下所示:

字段 說明 例子 ls(1)選項
st_atim 文件數據的最後訪問時間 read -u
st_mtim 文件數據的最後修改時間 write 默認
st_ctim i節點狀態的最後更改時間 chmod、chown -c

 

 

 

17.函數mkdir、mkdirat和rmdir

用mkdir和mkdirat函數建立目錄,用rmdir函數刪除目錄。

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

使用rmdir能夠刪除一個空的目錄,空的目錄是隻包含.和..這兩項的目錄。

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

18.讀目錄

#include <dirent.h>
DIR *opendir(const char *pathname);//返回路徑對應的DIR結構
DIR * fdopendir(int fd);//返回文件描述符對應的DIR結構
//  成功返回指針,出錯返回NULL
struct dirent *readdir(DIR *dp);
//若成功,返回指針,若在目錄尾或者出錯,返回NULL
void rewinddir(DIR * dp);
void closedir(DIR*dp) ;
//成功返回0,失敗返回-1
long telldir(DIR *dp);
void seekdir(DIR *dp,long loc);
//返回與dp關聯的目錄中的當前位置

下面是一個示例程序,能夠遞歸降序遍歷文件的層次結構: 

#include "apue.h"
#include <iostream>
#include <dirent.h>
#include <limits.h>
using namespace std;
typedef  int Myfunc(const char *,const struct stat *,int );
static  Myfunc myfunc;
static int myftw(char *,Myfunc *);
static int dopath(Myfunc *);
static long nreg,ndir,nblk,nchr,nfifo,nslink,nsock,ntot;


int main(int argc,char **argv){
    int ret;
    //保證被正確執行
    if(argc !=2){
        cout<<"usage: apue <starting-pathname>";
        exit(-1);
    }
    //調用函數myftw
    ret = myftw(argv[1],myfunc);
    ntot=nreg+ndir+nblk+nchr+nfifo+nslink+nsock;
    if(ntot==0)//避免除0
        ntot=1;
    //輸出各類文件所佔的比率
    printf("regular files = %7ld,%5.2f %%\n",nreg,nreg*100.0/ntot);
    printf("directories = %7ld,%5.2f %%\n",ndir,ndir*100.0/ntot);
    printf("block special = %7ld,%5.2f %%\n",nblk,nblk*100.0/ntot);
    printf("char special = %7ld,%5.2f %%\n",nchr,nchr*100.0/ntot);
    printf("FIFOs = %7ld,%5.2f %%\n",nfifo,nfifo*100.0/ntot);
    printf("symbolic links = %7ld,%5.2f %%\n",nslink,nslink*100.0/ntot);
    printf("sockets = %7ld,%5.2f %%\n",nsock,nsock*100.0/ntot);
    exit(ret);
}
/*
 * Descend through the hierarchy,starting at "pathname".
 *  the caller's func() is called for every file.
 */
#define FTW_F 1   //file other than directory
#define FTW_D 2   //directory
#define FTW_DNR 3 //directory that can't be read
#define FTW_NS 4  //file that we can't stat
static char *fullpath; //contains full func() returns
static size_t pathlen;
static int myftw(char *pathname,Myfunc *func){
    fullpath=path_alloc(&pathlen);//malloc PATH_MAX+1 bytes
    if(pathlen<= strlen(pathname)){
        pathlen=strlen(pathname)*2;
        if((fullpath=(char *)realloc(fullpath,pathlen))==NULL){
            cout<<"realloc error!"<<endl;
        }
    }
    strcpy(fullpath,pathname);
    return dopath(func);
}
/*
 * Descend through the hierarchy,starting at "fullpath".
 * if "fullpath" is anything other than a directory,we lstat() it, //不是目錄
 * call func(),and return.for a directory,we call ourself
 * recursively for each name in the directory
 */
static int dopath(Myfunc *func){//we return whatever func() returns
    struct stat statbuf;
    struct dirent *dirp;
    DIR *dp;
    int ret ,n;
    if(lstat(fullpath,&statbuf)<0){//stat error
        return (func(fullpath,&statbuf,FTW_NS));
    }
    if(S_ISDIR(statbuf.st_mode)==0){//not a directory
        return (func(fullpath,&statbuf,FTW_F));
    }
    if((ret =func(fullpath,&statbuf,FTW_D))!=0){
        return ret;
    }
    n=strlen(fullpath);
    if(n+NAME_MAX+2>pathlen){//expand path buffer
        pathlen*=2;
        if((fullpath=(char *)realloc(fullpath,pathlen))==NULL){
            cout<<"realloc error!"<<endl;
        }
    }
    fullpath[n++]='/';
    fullpath[n]=0;
    if((dp=opendir(fullpath))==NULL){// 不能打開目錄
        return func(fullpath,&statbuf,FTW_DNR);
    }
    while((dirp=readdir(dp))!=NULL){
        if(strcmp(dirp->d_name,".")==0||strcmp(dirp->d_name,"..")==0){
            continue;
        }//忽略. 和..
        strcpy(&fullpath[n],dirp->d_name);//append name after /
        if((ret=dopath(func))!=0)//recursive
            break;
    }
    fullpath[n-1] =0;
    if(closedir(dp)<0){
        cout<<"can't close directory "<<fullpath<<endl;
    }
    return ret;
}
static  int myfunc(const char *pathname,const struct stat *statptr,int type){
    switch (type){
        case FTW_F:
            switch (statptr->st_mode &S_IFMT){
                case S_IFREG:nreg++;break;
                case S_IFBLK:nblk++;break;
                case S_IFCHR:nchr++;break;
                case S_IFIFO:nfifo++;break;
                case S_IFLNK:nslink++;break;
                case S_IFSOCK:nsock++;break;
                case S_IFDIR:
                {
                    cout<<"for S_IFDIR for "<<pathname<<endl;
                    exit(-1);
                }

            }
        break;
        case FTW_D:
            ndir++;
            break;
        case FTW_DNR:
            cout<<"can't read directory "<<pathname<<endl;
            break;
        case FTW_NS:
            cout<<"stat error for "<<pathname<<endl;
            break;
        default:
            cout<<"unknown type "<<type <<" for pathname "<<pathname<<endl;
            exit(-1);

    }
    return 0;
}

運行示例:

19.函數chdir,fchdir和getcwd

  每一個進程都有一個當前的工做目錄,這個目錄是搜索全部相對路徑名的起點。當用戶登陸到UNIX系統的時候,其當前的工做目錄一般是用戶的home directory。

  調用函數chdir或者函數fchdir能夠更改當前的工做目錄。

#include <unistd.h>
int chdir(const char *pathname);
int fchdir(int fd);
//成功返回0,失敗返回-1.

  經過函數getcwd獲取當前工做路徑。

#include <unistd.h>
char *getcwd(char *buf,size_t size);
//成功返回buf,失敗返回NULL

示例程序:

#include "apue.h"
#include <iostream>

using namespace std;
int main(){
    char *ptr;
    size_t size;
    if(chdir("/Users/zhouyang")<0){
        cout<<"chdir failed!"<<endl;
    }
    ptr=path_alloc(&size);
    if(getcwd(ptr,size)==NULL){
        cout<<"getcwd failed!"<<endl;
    }
    cout<<"cwd= "<<ptr<<endl;
    return 0;
}

運行輸出:

cwd= /Users/zhouyang

相關文章
相關標籤/搜索