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

相关文章
相关标签/搜索