七、文件与目录管理

本文详细介绍了Unix系统中文件和目录的管理,包括stat函数、权限设置、所有权变更、扩展属性的操作、目录的创建与移除、读取目录内容、链接管理、复制和移动文件、设备节点以及文件事件监视等。重点讲解了各种系统调用和函数的使用,如chown、chmod、stat、readdir、unlink、硬链接和软链接等。
一切皆文件。每一个文件或者目录都有一个inode节点,通过stat结构体来管理。

stat函数

Unix提供了一组获取文件元数据的函数:

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *path, struct stat *buf);
int fstat(iny fd, struct stat *buf);
int lstat(const char *path, struct stat *buf);

这些函数均返回文件的信息。path参数指明文件信息。fstat用文件描述符。lstat与stat类似,但是对于符号链接,lstat返回链接本身而非目标文件。

这些函数在结构stat中存储了获取的文件信息

#include <sys/stat.h>
struct stat
{
    dev_t st_dev;   // 文件位于什么设备节点上
    ino_t st_ino;   // inode号
    mode_t st_mode;  // 权限
    nlink_t st_nlink;  // 硬链接的个数
    uid_t st_uid;  //用户ID
    gid_t st_gid;  //组ID
    dev_t st_rdev; // 设备ID
    off_t st_size; //总的字节
    blksize_t st_blksize; //I/O文件的块大小
    blkcnt_t st_blocks; //分配的块数
    time_t st_atime; //上次访问的时间
    time_t st_mtime; //上次修改的时间
    time_t st_ctime; //状态最后改变的时间,文件元数据被修改的最后时间
};

错误返回-1设置错误码
这里写图片描述

权限

修改文件权限

int chmod(const char *path, mode_t mode);
int fchmod(int fd, mode_t mode);

改变文件权限的进程必须具有CAP_FOWNER的权利错误返回-1设置错误码
这里写图片描述

//ex
int ret;
ret = chmod("./map.png", S_IRUSR | S_IWUSR);
if(ret)
    perror("chmod");

所有权

#include <sys/types.h>
#include <unistd.h>

int chown(const char *path, uid_t owner, gid_t group);
int lchown(const char *path, uid_t owner, gif_t group);
int fchown(int fd, uid_t owner, gid_t group);

设置路径指定文件的所有权。chown沿符号链接改变链接目标的所有权,而lchown只改变符号链接文件。
具有CAP_CHOWN能力的进程修改文件所有者和所有组。
失败返回-1设置错误码
这里写图片描述
这里写图片描述

扩展属性

提供一种永久地把文件与键/值对相关联的机制。扩展属性是与文件系统无关的,应用程序使用标准接口操作它们。

键与值

每个扩展属性对应一个唯一地键。user.mime_type;该键的命名空间是user,属性名为mime_type

对于不同的文件类型我们需要解释,Unix系统倾向于检查文件并解释其类型。该进程被称作MIME类型监听。

扩展属性命名空间

与扩展属性相关联的命名空间不仅仅是组织的工具。更依赖于命名空间,内核执行不同的访问策略。
Linux当前定义四种扩展属性命名空间。

system:利用扩展属性实现内核特性,例如访问控制列表ACLs。
security:实现安全模块。
trysted:存储用户空间限制信息。
user:为普通进程使用的标准命名空间。

扩展属性操作

定义四种应用程序对给定文件扩展属性执行的操作:
(1)给定文件和键,返回相应的值。
(2)给定文件,键与值,对键赋值
(3)给定文件,返回文件所有已指派的扩展属性键的列表。
(4)给定文件与键,从文件中移除该扩展属性。
对每个操作,POSIX提供三个系统调用。
(1)操作给定路径名的系统调用:如果路径指向符号链接,链接的目标文件会被操作。
(2)操作给定路径名的系统调用:如果路径指向符号链接,链接本身会被操作。
(3)操作文件描述符的系统调用。

检索扩展属性:返回文件扩展属性给定键的值

#include <sys/types.h>
#include <attr/xattr.h>
ssize_t getxattr(const char *path, const char *key, void *value, size_t size);
ssize_t lgetxattr(const char *path, const *key, void *value, size_t size);
ssize_t fgetxattr(int fd, const char *key, void *value, size_t size);

调用成功会将路径为path的文件中的名字为key的扩展属性保存到缓冲区value中,该缓冲区的长度为size个字节。
错误:
这里写图片描述

设置扩展属性

#include <sys/types.h>
#include <attr/xattr.h>
int setxattr(const char *path, const char *key, const void *value, size_t size, int flags);
int lsetxattr(const char *path, const char *key, const void *value, size_t size, int flags);
int fsetxattr(int fd, const char *key, const void *value, size_t size, int flags);

设置扩展属性key为value,value的长度为size字节。字段flags修改调用的行为,当flags是XATTR_CREATE,当扩展属性已存在时调用将是被。如果是XATTR_REPLACE,当扩展属性不存在时调用将失败。默认行为是当flags为0是执行-同时允许创建和替换。

列出文件的扩展属性

ssize_t listxattr(const char *path, char *list, size_t size);
ssize_t llistxattr(const char *path, char *list, size_t size);
ssize_t flistxattr(int fd, char *list, size_t size);

返回一个与路径指定文件相关联的扩展属性键列表,存储在list指向的长度为size的缓冲区中

删除扩展属性

int removexattr(const char *path, const char *key);
int lremovexattr(const char *path, const char *key);
int fremovexattr(int fd, const char *key);

删除文件key属性

目录

获取当前目录

char *getcwd(char *buf, size_t size);
char *get_current_dir_name();

成功调用getcwd()会以绝对路径名形式复制当前工作目录值buf指向的长度为size的缓冲区,失败返回NULL

更改当前目录

int chdir(const char *path);
int fchdir(int fd);

创建目录

int mkdir(const char *path, mode_t mode);

当前umask以通常方式修改参数mode,并和操作系统特定的模式位进行计算,新目录的权限位是(mode & ~umask & 01777).

移除目录

int rmdir(const char *path);

读取目录内容

DIR目录流

在开始读取目录内容前,你需要创建一个由DIR对象指向的目录流:

#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);

调用成功会创建name所指向目录的目录流。
目录流比指向打开目录的文件描述符保存的内容多了一些,主要增加的是一些元数据和保存目录内容的缓冲区。可以在给定目录流中获取该目录的文件描述符。

#define _BSD_SOURCE
#include <sys/types.h>
#include <dirent.h>
int dirfd(DIR *dir);

返回目录流的文件描述符。错误返回-1。dir是BSD扩展,不是POSIX标准函数。

从目录流读取

struct dirent *readdir(DIR *dir);

struct dirent
{
    ino_t d_ino; // inode号
    off_t d_off; //下一个目录的偏移量
    unsigned short d_reclen;  // 这个记录的长度
    unsigned char d_type; //文件的类型
    char d_name[256]; //文件的名字
}

程序连续调用readdir,获取目录中的每个文件,知道发现它们搜索的文件或者整个目录读完。此时readdir返回NULL;失败时也返回NULL,将errno设置为0。
POSIX字需要字段d_name,该字段是目录内单个文件名。其他字段是可选的,或Linux特有。希望将程序移植应之访问d_name
失败是readdir返回NULL。

关闭目录流

int closedir(DIR *dir);

关闭有dir指向的目录流,包括目录的文件描述符。

//查找在目录下的文件
int find_file_in_dir(const char *path, const char *file)
{
    struct dirent *entry;
    int ret = 1;
    DIR *dir;
    dir = opendir(path);
    errno = 0;
    while((entry = readdir(dir)) != NULL)
    {
        if(!strcmp(entry->d_name, file))
        {
            ret = 0;
            break;
        }
    }
    if(errno && !entry)
        perror("readdir");
    closedir(dir);
    return dir;
}

读取目内容的系统调用

#include <unistd.h>
#include <linux/types.h>
#include <linux/dirent.h>
#incldue <linux/unistd.h>
#include <errno.h>

int readdir(unsigned int fd, struct dirent *dirp, unsigned int count);
int getdents(unsigned int fd, struct dirent *dirp, unsigned int count);

一般来讲不会使用这些系统调用,它们不可移植。

链接

目录中每个名字至inode的映射称为链接。链接本质上不过是列表中一个指向inode的名字。

硬链接

#include <unistd.h>
int link(const char *oldpath, const char *newpath);

会为已存在oldpath建立路径newpath下的新链接,并返回0。失败返回-1,设置errno。
这里写图片描述

符号链接

软连接和硬链接的一点很重要的区别是它可以能跨越文件系统。符号链接能指向已存在或不存在的文件。

int symlink(const char *oldpath, const char *newpath);

创建oldpath的符号链接newpath

解除链接

int unlink(const char *pathname);

成功调用unlink从文件系统中删除pathname,并返回0。如果该路径指向文件的最后一个连接,会从文件系统中删除该文件。如果进程打开文件,在进程关闭文件前,内核不会从文件系统中删除文件。错误返回-1。
这里写图片描述
unlink不会删除目录。
为了简化各种类型的文件删除,C语言提供

int remove(const char *path);

复制和移动文件

复制

这里写图片描述

移动

int rename(const char *oldpath, const char *newpath);

将oldpath重命名为newpath。文件内容和inode保持不变。成功返回0,失败返回-1。
这里写图片描述

不同类型文件相互移动的结果
这里写图片描述

设备节点

设备节点是应用程序与设备驱动交互的特殊文件。当应用程序在设备节点上执行一般的Unix I/O,内核以不同于普通文件I/O方式处理这些请求。
每个设备节点都具有两个数值属性,分别是主设备号和次设备号。主次设备号与对应的设备驱动映射表已载入内核。

特殊设备节点

空设备位于/dev/null,主设备号是1,次设备号是3。该设备文件的所有者是root但所有用户均可读写。内核忽略所有对该设备的写请求,对该设备的读请求则返回文件终止符。
零设备为与/dev/zero,主设备号为1,次设备号为5。位空设备号一样,内核忽略所有对零设备的写请求。读会返回无线null字符流。
满设备位于/dev/full,主设备号是1,次设备号为7,读返回null支付流。写请求触发ENOSPC错误,表明设备已满。

随机数生成器

生成设备位于/dev/random和/dev/urandom主设备号1,次设备号分别是8和9。
随机数生成器从设备驱动和其他源中收集噪声,内核将收集的噪声连结并做单向哈希,将其结果存储在内核熵池中。内核保存池中预算和数量的熵比特数。
读取/dev/random时,返回池中的熵。

外带通信

有时候需要需基本数据流外的文件通信,具体百度。

#include <sys/ioctl.h>
int ioctl (int fd, int request);

监视文件事件

Linux提供为监视文件接口inotify。利用它监控文件的移动,读取,写入或删除操作。

初始化inotify

#include <inotify.h>
int inotify_init(void);

初始化返回实例指向的文件描述符

监视

初始化后,设置监视。监视由监视描述符表示,由一个标准Unix路径和一个与之相关的监视掩码组成。该掩码通知内核,该进程关系何种时间。
可以监视文件和目录。监视目录时,会报告目录本身和该目录下所有文件。
新增监视

int inotify_add_watch(int fd, const char *path, uint32_t mask);

成功时,调用返回新建的监视描述符。失败返回-1
这里写图片描述

监视掩码有以下运算
这里写图片描述
这里写图片描述

//ex
int wd;
wd = inotify_add_watch(fd, "/etc", IN_ACCESS | IN_MODIFY);
if(wd == -1)
{
    perror("inotify_add_watch");
    exit(EXIT_FAILURE);
}

对目录/etc所有读写增加监视。如果/etc下所有文件被读取或写入,inotify发送时间至inotify文件描述符fd,该fd由监视描述符wd提供。

inotify事件

struct inotify_event
{
    int wd;   //监视描述符
    uint32_t mask;  //监视事件
    uint32_t cookie;  //唯一地cookie
    uint32_t len;   //name的长度
    char name[]; //空字符填充
}

如果wd标识目录且该目录下文件发生监视事件,name保存对应的文件名。这种情况下,len不为0。
这里写图片描述

读取inotify事件
读取inotify事件很简单:你仅需读取与inotify实例相关联的文件描述符即可。inotify提供slurping特性,该特性允许你以单个读请求读取多个事件。

监视相关见P250

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值