《Unix环境高级编程》chapter04 文件和目录(一)

chapter04 文件和目录(一)

函数stat,fstat,fstatat和lstat

通过stat,fstat,fstatatstat四个函数可以返回文件的详细信息:

#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 fstatst(int fd,const char* restrict pathname,struct stat* restrict buf);

lstat函数类似于stat,但是当命名的文件是一个符号链接时,lstat返回给符号链接的有关信息,而不是由该符号链接引用的文件的信息.

fstat函数传入文件描述符,在Unix中很多以f开头的函数相比于不是f开头的函数的区别就是传入的参数是不是文件描述符fd,比如fchmodfchown,ftruncate

上面四个函数通过填充由buf指向的结构来返回文件信息,该结构的实际定义可能随具体实现有所不同,但其基本形式是:

(***)stat结构

struct stat{
    mode_t      st_mode;        //记录文件类型+读写权限
    ino_t       st_ino;         //记录i节点编号
    dev_t       st_dev;         //记录设备号
    dev_t       st_rdev;        //记录实际设备号,只有当文件是块设备或字符设备时才有
    nlink_t     st_nlink;       //指向i节点的链接数
    uid_t       st_uid;         //用户ID
    gid_t       std_gid;        //组ID
    off_t       st_size;        //size in bytes, for regular files
    struct timespec st_atime;   //文件最后访问时间
    struct timespec st_mtime;   //文件最后修改时间
    struct timespce st_ctime;   //文件最后状态更新时间(记录在i节点上)
    blksize_t   st_blksize;     //best I/O block size
    blkcnt_t    st_blocks;      //number of disk blocks allocated
};

其中timespec结构类型按照秒和纳秒定义了时间,至少包括下面两个字段:

time_t tv_sec;
long tv_nsec;
文件类型

(***)UNIX中的文件类型包括以下几种:

  1. 普通文件(regular file)
  2. 目录文件(directory file)
  3. 块特殊文件(block specila file)
  4. 字符特殊文件(character special file)
  5. FIFO(也被称为命名通道)
  6. 套接字(socket)
  7. 符号链接
实例:通过定义的宏判断文件类型
#include<stdio.h>
#include<sys/stat.h>

#define oops(m,x) { perror(m); exit(x); }

int main(int ac,char* av[])
{
    int i;
    struct stat buf;
    char *ptr;
    for(i=1;i<ac;++i)
    {
        if(lstat(av[i], &buf)<0)
            oops("lstat",1);
        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="char";
        else if(S_ISBLK(buf.st_mode))
            ptr="block";
        else if(S_ISFIFO(buf.st_mode))
            ptr="FIFO";
        else if(S_ISLNK(buf.st_mode))
            ptr="link";
        else if(S_ISSOCK(buf.st_mode))
            ptr="socket";
        else
            ptr="unkown";
        printf("%s\n",ptr);
    }
    return 0;
}
设置用户ID和设置组ID

与一个进程相关联的ID有6个或更多:

在这里插入图片描述

通常,有效用户ID等于实际用户ID,有效组ID等于实际组ID。当执行一个程序文件时,进程的有效用户ID通常就实际用户ID,有效组ID通常就是实际组ID.

(***) 但是可以在文件模式字(st_mode)中设置一个特殊标志,其含义是”执行此文件时,将进程的有效用户ID设置为文件所有者的用户ID(st_uid)“。与此类似,在文件模式字中可以设置另一位,它将执行此文件的进程的有效组ID设置文件的组所有者ID(st_gid)。在文件模式字中的这两位被称为设置用户ID(set-user-ID)位设置组ID(set-group-ID)位.

文件访问权限

所有文件类型(目录、字符特别文件等)都有访问权限(access permission)。很多人认为只要普通文件有访问权限,这是一种误解。

每个文件有9个访问权限位,可将它们分为3类:

在这里插入图片描述

(***)进程每次打开、创建或删除一个文件时,内核就进行文件访问权限测试,内核进行的测试具体如下:

  1. 若进程的有效用户是0(超级用户),则允许访问。
  2. 若进程的有效用户ID等于文件的所有者(也就是进程拥有此文件),那么如果所有者适当的访问权限位被设置,则允许访问(比如要对文件进行写操作,则该进程有效用户应该有写权限)
  3. 若进程的有效组ID或进程的附属组ID之一等于文件的组ID,那么如果组适当的访问权限位被设置,则允许访问;否则拒绝访问

注意,上述测试顺序执行,即如果进程拥有此文件(第2步),则按用户权限访问批准或拒绝该进程对文件的访问—不查看组访问权限。

新文件和目录的所有权

新文件的用户ID设置为进程的有效用户ID。关于组ID,POSIX.1允许实现选择下列之一作为新文件的组ID。

  1. 新文件的组ID可以是进程的有效组ID
  2. 新文件的组ID可以是它所在目录的组ID
函数access和faccessat

有时,进程也希望按其实际用户ID和实际组ID来测试其访问能力。例如,当一个进程使用设置用户ID或设置组ID功能作为另一个用户(或组)运行时,就可能会有这种需要。

通过accessfaccessat可以判断实际用户是否有相应权限:

#include<unistd.h>

int access(const char* pathname,int mode);

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

其中,如果测试文件是否存在,mode就为F_OK;否则mode是下表所列常量的按位或:

mode说明
R_OK测试读权限
W_OK测试写权限
X_OK测试执行权限
实例:access函数实例
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>

#define oops(m,x) { perror(m); exit(x); }

int main(int ac,char* av[])
{
    if(ac!=2){
        fprintf(stderr,"usage: a.out <pathname>");
        exit(1);
    }
    if(access(av[1],R_OK)<0){           //测试读权限
        printf("read access ERROR\n");
    }
    else{
        printf("read access OK\n");
    }
    if(open(av[1],O_RDONLY)<0){
        oops("open",2);
    }
    else{
        printf("open for reading OK\n");
    }
    exit(0);
}
函数umask

umask函数为进程设置文件模式创建屏蔽字,并返回之前的值。

#include<sys/stat.h>

mode_t umask(mode_t cmask);
实例:umask函数实例
#include<stdio.h>
#include<unistd.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)
        printf("creat error\n");
    umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);   //umask值禁止所有组和其他用户的读写权限
    if(creat("bar",RWRWRW)<0)
        printf("creat error\n");
    exit(0);
}

需要注意的是,更改进程的文件模式创建屏蔽字并不影响其父进程(常常是shell)的屏蔽字

函数chmod、fchmod和fchmodat

chmod,fchmodfchmodat这3个函数使我们可以更改现有文件的访问权限

#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);

其中参数mode是前面的文件权限位。且为了改变一个文件的权限位,进程的有效用户ID必须等于文件的所有者ID,或者进程必须具有超级用户权限。

粘着位

S_ISVTX位被称为粘着位(sticky bit).后来的UNIX版本称它为保存正文位.

现在的系统扩展了粘着位的使用范围,Single UNIX Specification允许对目录设置粘着位。如果对一个目录设置了粘着位,只有对该目录具有写权限的用户并且满足下列条件之一,才能删除或重命名该目录下的文件:

  • 拥有此文件
  • 拥有此目录
  • 是超级用户

目录/tmp/var/tmp是设置粘着位的典型候选者————任何用户都可在这两个目录中创建文件。任一用户(用户、组合其他)对这两个目录的权限通常都是读、写和执行。但是用户不应能删除或重命名属于其他人的文件,为此在这两个目录的文件模式中都设置了粘着位。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值