第四章 文件目录

stat,fstat,fstatat,lstat

#include 
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);//link有关信息,比stat多检测link文件
int fstatat(int fd,const char *restrict pathname,struct stat *restrict buf,int flag);//

struct stat{
    mode_t          st_mode;i节点 中文件访问权限
    ino_t           st_ino;目录项中 i节点编号
    dev_t           st_dev;
    dev_t           st_rdev;
    nlink_t         st_nlink;有链接计数
    uid_t           st_uid;
    gid_t           st_gid;
    off_t           st_size;
    struct timespec st_atime;
    struct timespec st_mtime;
    struct timespec st_ctime;
    blksize_t       st_blksize;
    blkcnt_t        st_blocks;
}
struct timespec{
    time_t tv_sec;
    long   tv_nsec;
    ...
}

//附一份macos 10.13.4
truct stat {
    dev_t       st_dev;     /* [XSI] ID of device containing file */
    ino_t       st_ino;     /* [XSI] File serial number */
    mode_t      st_mode;    /* [XSI] Mode of file (see below) */
    nlink_t     st_nlink;   /* [XSI] Number of hard links */
    uid_t       st_uid;     /* [XSI] User ID of the file */
    gid_t       st_gid;     /* [XSI] Group ID of the file */
    dev_t       st_rdev;    /* [XSI] Device ID */
#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
    struct  timespec st_atimespec;  /* time of last access */
    struct  timespec st_mtimespec;  /* time of last data modification */
    struct  timespec st_ctimespec;  /* time of last status change */
#else
    time_t      st_atime;   /* [XSI] Time of last access */
    long        st_atimensec;   /* nsec of last access */
    time_t      st_mtime;   /* [XSI] Last data modification time */
    long        st_mtimensec;   /* last data modification nsec */
    time_t      st_ctime;   /* [XSI] Time of last status change */
    long        st_ctimensec;   /* nsec of last status change */
#endif
    off_t       st_size;    /* [XSI] file size, in bytes */
    blkcnt_t    st_blocks;  /* [XSI] blocks allocated for file */
    blksize_t   st_blksize; /* [XSI] optimal blocksize for I/O */
    __uint32_t  st_flags;   /* user defined flags for file */
    __uint32_t  st_gen;     /* file generation number */
    __int32_t   st_lspare;  /* RESERVED: DO NOT USE! */
    __int64_t   st_qspare[2];   /* RESERVED: DO NOT USE! */
};

文件类型

  1. 普通文件
  2. 目录文件
  3. 块特殊文件 FreeBSD不再支持
  4. 字符特殊文件
  5. FIFO 命令管道,用于进程间通信,第十五章会说明
  6. 套接字 socket,用于进程间的网络通信
  7. 符号链接

文件类型信息包含在stat结构的st_mode成员中,上面有定义

//文件类型宏  st_mode 对应上面七种文件
S_ISREG()
S_ISDIR()
S_ISBLK()
S_ISCHR()
S_ISFIFO()
S_ISSOCK()
S_ISLNK()
//POSIX.1 允许实现将IPC对象说明为文件,但是有四种unix系统不将这些对象表示为文件,蛋疼,标准不遵守
S_TYPEISMQ()  消息队列
S_TYPEISSEM() 信号量
S_TYPEISSHM() 共享存储对象

设置用户ID和设置组ID

一个进程关联的ID有6种

实际用户id,组id就是有效用户id,组id

执行一个程序文件的时候,如果该文件设置了下面的S_ISUID权限,那么进程执行这个文件的时候就会将进程有效用户id设置成文件所有者用户id,类似S_ISGID,下面有chmod的条件二的特例

s权限位

典型场景,普通用户可以修改密码,而密码记录文件/etc/passwd和/etc/shadow普通用户都是无法修改的,没有权限,但是passwd命令的权限是-rwsr-xr-x,这就以为着,普通用户使用这个命令时,在执行的进程中,实际就以属主root身份执行,当然前提是要有相应的执行权限x

安全问题,第八章讨论

文件访问权限

st_mode 值包含了这些权限。

用户(拥有者)读写执行,同组读写执行,其他组读写执行

0400 0200 0100 0040 0020 0010 0004 0002 0001

S_IRUSR S_IWUSR S_IXUSR S_IRGRP S_IWGRP S_IXGRP S_IROTH S_IWOTH S_IXOTH

进程中操作文件,其实开这个进程的实际用户id,组id就是进程的有效用户id,组id

  1. 打开一个文件就拥有了上目录的执行权限,对目录执行权限位被称为搜索位。这就是为什么对于目录其执行权限位常被称为搜索位的原因。
  2. 读权限
  3. 写权限
  4. open中指定O_TRUNC,必须要有写权限
  5. 创建新文件,必须要有当前目录的写和执行权限
  6. 删除文件,必须要有当前目录的写和执行权限,对该文件本身不需要读、写权限,如果设置t权限那么要检测是否是文件或者目录拥有者或者root
  7. 7个exec函数任何执行某个文件,都需要具有该文件的执行权限,而且必须是普通文件

新文件和目录的所有权

新文件和新目录所有权规则相同

用户ID为进程有效用户ID;组ID实现两种:进程有效组ID,所在目录的组ID

#include 
int access(const char *pathname,int mode);
int faccessat(int fd,const char *pathname,int mode,int flag);//成功0

mode 值

R_OK W_OK X_OK

umask

#include 
mode_t umask(mode_t cmask);//返回值是之前文件模式创建屏蔽字,少数几个没有出错返回

作用就是无法设置权限
常用002(其他用户写),022(),027()
2018-4-10 23:31

chmod,fchmod,fchmodat

#include 
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:上面9中再加下面六种

组合权限:S_IRWXU S_IRWXG S_IRWXO

特殊权限三种:

s权限:S_ISUID(执行时设置用户ID) S_ISGID(执行时设置组ID)

t权限:粘着位,S_ISVTX(保存正文saved-text) ,现在只针对目录,删除文件的权限

chmod("file",(buf.st_mode & ~S_IXGRP) | S_ISGID)

s权限配合执行权限(用户和组必须设置x权限)可以以root身份权限执行

vtx权限也就是t权限:给目录和文件设置t权限是完全不同目的,目录拥有t权限,那么目录下的文件只有该目录拥有者或者文件拥有者和root才能删除和更名

chmod 会自动清除两个权限位的条件

  1. 一些unix系统,不允许除root用户以外的用户设置文件S_ISVTX,防止用户恶意设置,影响系统性能(linux系统没有这种限制)
  2. 新文件的组ID不等于进程有效组ID,而且没有root权限,那么设置组ID会被关闭

chown,fchown,fchownat,lchown

#include 
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);
AT_FDCWD 相对path = 绝对path 很多函数用到

基于BSD系统规定只有root用户可以更改一个文件的所有者

System V 则允许任一用户更改他们所拥有文件的所有者

文件长度

st_size

st_blksize

st_blocks

文件空洞

原因是偏移量超过文件尾端,并写入数据造成的

文件截断

第三章open函数第二个参数中有个O_TRUNC

#include 
int truncate(const char *pathname,off_t length);
int ftruncate(int fd,off_t length);

文件系统

solaris支持多种不同类型的磁盘文件系统

基于传统bsd的ufs,读写dos的pcfs,cd的hsfs

macos大小写不敏感

2018-4-12 23:55

文件系统的构成,下面

  • 磁盘:各个分区
  • 文件系统:自举块,超级块,柱面0...
  • 柱面:超级块的副本,配置信息,i节点图,块位图,i节点,数据块
  • i节点:各个i节点...

目录和文件块:i节点数组数据块目录块

目录块(项)中有文件名i节点编号(数据类型是ino_t),其中i节点指向数据块

每个i节点有个链接计数,就是指向该节点的目录项数,只有减少至0,才可以删除文件,就是解除对一个文件的链接并不是释放该文件占用的磁盘块,就是删除一个目录项是unlink而不是delete

  1. 这个链接计数在stat结构中是st_nlink的成员,其基本系统数据类型是nlink_t,这是硬链接,这个计数初始文件1,叶目录2(本身+./),创建一个硬链接就+1,无法给目录创建或者条件很苛刻

  2. 软链接S_IFLINK,符号链接symbolic link,它的实际类容:在数据块中包含了该符号链接指向的文件名字,比如

    ls lib 
    lib -> usr/lib
    实际类容就是 usr/lib 这7个字节
    

i节点,stat大多信息来自i节点:文件类型、文件访问权限、文件长度、指向文件数据块的指针

目录项,一个目录项不能指向另一个文件系统

link,linkat,unlink,unlinkat,remove

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

创建新目录项和添加链接计数是一个原子操作

#include 
int unlink(const char *existingpath,const char *newpath);
int unlinkat(int fd,const char *pathname,int flag);
#include 
int remove(const char *pathname)

rename,renameat

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

符号链接

符号链接是一个文件的间接指针,硬链接直接指向文件的i节点

引入符号链接是因为劈开硬链接的一些限制

  1. 不要求链接和文件位于同一个文件系统中
  2. root才能创建(底层文件系统支持的情况下,因为很多还是不支持,mac和linux)

必须考虑函数是否可以处理符号链接

Mkdir,mkinfo,mknod,rmdir

避免子目录指向父目录造成死循环,而且软链接可以unlink,但是硬链接很难消除,这就是为什么硬链接有限制

创建和读取符号链接

#include 
int symlink(const char *actualpath,const char *sympath);
int symlinkat(const char *actualpath,int fd,const char *sympath);
//open函数跟随符号链接,所有下面方法可以打开链接本身
ssize_t readlink(const char* restrict pathname,char* restrict buf,size_t bufsize);
ssize_t readlinkat(int fd,cosnt char* restrict pathname,char* restrict buf,size_t bufsize);

两个函数组合了open、read、close的所有操作

文件的时间

每个文件会维护三个时间

ls命令选项
st_atime 最后访问时间-文件内容 read -u
st_mtime 最后修改时间-文件内容 write 默认
st_ctime i节点最后更改时间 chown,chmod -c

i节点所有信息都是与文件的实际内容分开存放的

st_ctime 系统不会维护,没有函数可以去自定义修改这个

access、stat函数并不更改这3个时间中的任何一个

ps 创建一个新文件影响到包含次新文件的目录,页影响该文件的i节点,但是读或写只影响该文件的i节点

futimens,utimensat,utimes

POSIX.1中

#include 
int futimens(int fd,const struct timespec times[2]);
int utimensat(int fd,const char *path,const struct timespec times[2],int flag);

tv_nsec 字段:UTIME_NOW,UTIME_OMIT

times[2]:第一个包含访问时间st_atime,第二个包含修改时间st_mtime

Single UNIX Specification的XSI扩展中

#include 
int utimes(const char *pathname,const struct timeval times[2]);
struct timeval{
    time_t tv_sec;//s
    long tv_usec;//ms
}

场景:touch命令使用这些函数的某一个,tar和cpio也会可选地调用这些函数

Ps: macos中有些问题

mkdir,mkdirat,rmdir

#include 
int mkdir(const char *pathname,mode_t mode);
int mkdirat(int fd,const char *pathname,mode_t mode);

对应目录来说,通常至少要设置一个x权限位,允许访问该目录中的文件名

#include 
int rmdir(const char *pathname);

读目录

一个目录的w和x权限决定能否在目录下new和del文件(当然前提是该目录的t权限位没有设置),但是这并不代表能否写目录本身

#include 
DIR *opendir(const char *pathname);//success:返回指针 fail:返回NULL
DIR *fdopendir(int fd);//success:返回指针 fail:返回NULL
以上两个函数返回的指针由下面5个函数使用
struct dirent *readdir(DIR *dp);//success:返回指针 fail:返回NULL。返回下一个目录的指针

void rewinddir(DIR *dp);//将偏移量放在最开始
int closedir(DIR *dp);//关闭目录

long telldir(DIR *dp);//返回值与dp关联的目录中的当前位置。查询偏移量

void seekdir(DIR *dp,long loc);//设置偏移量
DIR是一个内部结构

fig4.22实现了遍历目录

chdir,fchdir,getcwd

chdir,fchdir可以更改当前工作目录

#include 
int chdir(const char *pathname);
int fchdir(int fd);
#include 
char *getcwd(char *buf,size_t size);//success:返回buf,fail:返回NULL

设备特殊文件

st_dev,st_rdev

小结

S_ISUID S_ISGID

设置用户ID 设置组 ID

执行时设置有效用户 ID

若组执行位设置,则执行时设置有效 组 ID;否则使强制性锁起作用(若支持)

(未使用)

将在目录中创建的新文件的组 ID 设置为目录的组 ID

S_ISVTX

粘着位

在交换区缓存程序正文(若支持)

限止在目录中删除和重命名文件

S_IRUSR S_IWUSR S_IXUSR

用户读 用户写 用户执行

许可用户读文件

许可用户写文件

许可用户执行文件

许可用户读目录项 许可用户在目录中删除和创建文件 许可用户在目录中搜索给定路径名

S_IRGRP S_IWGRP S_IXGRP

组读 组写 组执行

许可组读文件

许可组写文件

许可组执行文件

许可组读目录项

许可组在目录中删除和创建文件

许可组在目录中搜索给定路径名

S_IROTH S_IWOTH S_IXOTH

其他读 其他写 其他执行

许可其他读文件

许可其他写文件

许可其他执行文件

许可其他读目录项 许可其他在目录中删除和创建文件 许可其他在目录中搜索给定路径名

你可能感兴趣的:(第四章 文件目录)