APUE02 - 文件系统

stat

arduino 复制代码
int stat(const char *path, struct stat *struct_stat);
int lstat(const char *path,struct stat *struct_stat);
int fstat(int fdp, struct stat *struct_stat);

以上三个函数都将文件信息存放到一个结构体中:

arduino 复制代码
struct stat {
        mode_t     st_mode;       //文件对应的模式,文件,目录等
        ino_t      st_ino;       //inode节点号
        dev_t      st_dev;        //
        dev_t      st_rdev;       //
        nlink_t    st_nlink;      //文件的连接数
        uid_t      st_uid;        //文件所有者
        gid_t      st_gid;        //文件所有者对应的组
        off_t      st_size;       //普通文件,对应的文件字节数
        time_t     st_atime;      //文件最后被访问的时间
        time_t     st_mtime;      //文件内容最后被修改的时间
        time_t     st_ctime;      //文件状态改变时间
        blksize_t st_blksize;    //文件内容对应的块大小
        blkcnt_t   st_blocks;     //伟建内容对应的块数量
};

有两个比较相似的字段:

arduino 复制代码
dev_t     st_dev;         /* ID of device containing file */
dev_t     st_rdev;        /* Device ID (if special file) */

这两个存的都是主次设备号,都要通过宏majorminor来取出来

  • st_dev 存的是文件本身存储设备的设备号,也就是硬盘的设备号
  • st_rdev 只有字符特殊设备和块特殊设备才会有st_rdev值。此值包含实际设备的设备号。

空洞文件

st_size 表示文件的字节数,但是并不表示文件占用的实际大小。有个空洞文件的概念,就是我们可以创建一个非常大的文件,但是实际上文件里面没有东西,利用下面这两个函数,就可以在距离文件很远的位置,写一个字符,然后关闭文件,这样就得到一个非常大的,但是实际上只写入了一个字符的文件。

Java 里面使用 RandomAccessFile 可以做到同样的效果。

scss 复制代码
fopen()
lseek()
write()
close()

由于实际上只写入了一个字符,linux的文件系统很聪明,它就只会分配你实际上占用的磁盘空间,4K大小。

所以,计算磁盘占用空间,需要使用:st_blksize * st_blocks

文件属性

st_mode

0~8 bit : 文件访问权限

9~11 bit : 文件访问权限控制

12~15 bit:文件类型

umask

langzi989.github.io/2017/09/13/...

若没有文件掩码时,文件的默认权限为0666,文件夹的默认权限为0777。

原因:创建文件一般是用来读写,所以默认情况下所有用户都具有读写权限,但是没有可执行权限,所以文件创建的默认权限为0666而文件夹的x权限表示的是打开权限,所以这个权限必须要有,所以文件夹的默认权限为0777。

上述的权限是在没有umask情况下的默认权限。但是系统为了保护用户创建文件和文件夹的权限,此时系统会有一个默认的用户掩码(umask),大多数的Linux系统的默认掩码为022。用户掩码的作用是用户在创建文件时从文件的默认权限中去除掩码中的权限。所以文件创建之后的权限实际为:

bash 复制代码
#文件创建权限
默认权限(文件0666,文件夹0777)-umask

所以在用户不修改umask的情况下,创建文件的权限为:0666-0022=0644。创建文件夹的权限为:0777-0022=0755

粘住位

www.cnblogs.com/MrListening...

是 st_mode 的 T 位。

当目录被设置了粘住位之后,即便用户对该目录有写入权限,也不能删除该目录中其他用户的文件数据,只有该文件的所有者以及root用户才能删除。

设置了该位之后,可以保持一种动态平衡:允许所有用户在该目录写入/删除文件,但是不能删除其他用户文件。

/tmp 目录就设置了该位。

chdir/fchdir

chdir 是一个用于改变当前工作目录的系统调用和命令。在 UNIX 和类 UNIX 系统(如 Linux)中,chdir 允许用户或程序切换到指定的目录,这对于文件操作和脚本执行非常重要。

arduino 复制代码
#include <stdio.h>
#include <unistd.h>

int main() {
    if (chdir("/usr/local/bin") != 0) {
        perror("chdir failed");
        return 1;
    }
    printf("Changed directory to /usr/local/bin\n");
    return 0;
}

越权问题:

假设有一个 Linux 系统,用户 alice 有权限访问自己的主目录 /home/alice,但没有权限访问另一个用户 bob 的主目录 /home/bob

  1. 正常权限设置

    • alice 的权限:drwx------(只有 alice 有读、写和执行权限)
    • bob 的权限:drwx------(只有 bob 有读、写和执行权限)
  2. 越权操作

    • 如果 alice 通过某种方式(比如程序漏洞或错误配置)尝试使用 chdir 命令切换到 /home/bob,例如:

      bash 复制代码
      bash
      复制代码
      cd /home/bob
    • 由于 alice 没有权限访问 /home/bob,系统会返回一个权限拒绝的错误:

      bash 复制代码
      bash
      复制代码
      bash: cd: /home/bob: Permission denied
  3. 潜在的越权风险

    • 如果某个程序以 alice 的身份运行,但由于程序的设计缺陷,允许用户以某种方式"切换"到 bob 的目录(例如通过不当的符号链接),则可能导致 alice 访问 bob 的敏感信息,这就是一种越权行为。

这种情况提醒我们在系统中进行严格的权限管理,确保用户不能访问或更改不属于自己的资源。

glob

glob 函数用于文件名模式匹配。它可以根据指定的模式查找符合条件的文件路径名,常用于脚本编程和命令行操作。

在 C 语言中,glob 的函数原型通常如下:

arduino 复制代码
#include <glob.h>

int glob(const char *pattern, int flags, int (*errfunc)(const char *epath, int eerrno), glob_t *pglob);

参数说明

  • pattern: 要匹配的文件名模式,例如 *.c
  • flags: 控制匹配行为的标志
  • errfunc: 出错时调用的回调函数。
  • pglob: 用于存储匹配结果的结构体,类型为 glob_t

glob_t 结构体包含以下字段:

  • gl_pathc: 匹配的路径数量。
  • gl_pathv: 匹配的路径数组。
  • gl_offs: 数组偏移量,主要用于设置 gl_pathv 数组的偏移量。它的作用是允许你在匹配的路径数组中从指定的位置开始存储匹配结果。这对于在多次调用 glob 时,可以在同一个数组中存储结果而不会覆盖之前的匹配结果。
arduino 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <glob.h>

int main() {
    glob_t globbuf;
    
    // 初次调用
    globbuf.gl_offs = 0; // 从数组开始位置
    if (glob("*.c", 0, NULL, &globbuf) == 0) {
        for (size_t i = 0; i < globbuf.gl_pathc; i++) {
            printf("C files: %s\n", globbuf.gl_pathv[i]);
        }
    }

    // 设置偏移量,存储结果
    globbuf.gl_offs = globbuf.gl_pathc; // 从已存在结果后开始
    if (glob("*.h", 0, NULL, &globbuf) == 0) {
        for (size_t i = 0; i < globbuf.gl_pathc; i++) {
            printf("Header files: %s\n", globbuf.gl_pathv[i]);
        }
    }

    globfree(&globbuf); // 释放资源
    return 0;
}

glob 函数接收一个回调函数作为参数,主要是为了处理在匹配过程中可能遇到的错误情况。glob 函数的回调参数中的错误码与函数返回值的错误码并不完全相同,但它们都是用于表示错误情况。比如在某些情况下,glob 的返回值为非零(例如,GLOB_ERR),这可能是因为在处理某个特定文件时发生了错误,而这个错误可以通过回调函数捕获到。

尽管两者都涉及错误处理,但它们的用途和范围不同。函数返回值提供了匹配结果的总体状态,而回调函数中的错误码则提供了更细致的错误信息。

opendir

除了 opendir,还有其他一些常用的操作目录的 API。以下是一些主要的函数及其用途:

  • readdir 用途:读取目录中的下一个条目。
  • closedir用途:关闭一个打开的目录流。
  • mkdir用途:创建一个新目录。
  • rmdir用途:删除一个空目录。
  • rename 用途:重命名文件或目录。
  • unlink 用途:删除文件。
arduino 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>

int main() {
    // 创建目录
    mkdir("test_dir", 0755);

    // 打开目录
    DIR *dir = opendir("test_dir");
    if (dir == NULL) {
        perror("无法打开目录");
        return EXIT_FAILURE;
    }

    // 读取目录(此时应该为空)
    struct dirent *entry;
    while ((entry = readdir(dir)) != NULL) {
        printf("目录条目: %s\n", entry->d_name);
    }
    closedir(dir);

    // 重命名目录
    rename("test_dir", "new_test_dir");

    // 删除空目录
    rmdir("new_test_dir");

    return EXIT_SUCCESS;
}
相关推荐
苏言の狗31 分钟前
Pytorch中关于Tensor的操作
人工智能·pytorch·python·深度学习·机器学习
寻找沙漠的人34 分钟前
前端知识补充—CSS
前端·css
GISer_Jing1 小时前
2025前端面试热门题目——计算机网络篇
前端·计算机网络·面试
m0_748245521 小时前
吉利前端、AI面试
前端·面试·职场和发展
理想不理想v1 小时前
webpack最基础的配置
前端·webpack·node.js
pubuzhixing1 小时前
开源白板新方案:Plait 同时支持 Angular 和 React 啦!
前端·开源·github
2401_857600951 小时前
SSM 与 Vue 共筑电脑测评系统:精准洞察电脑世界
前端·javascript·vue.js
2401_857600951 小时前
数字时代的医疗挂号变革:SSM+Vue 系统设计与实现之道
前端·javascript·vue.js
GDAL1 小时前
vue入门教程:组件透传 Attributes
前端·javascript·vue.js
2402_857583491 小时前
基于 SSM 框架的 Vue 电脑测评系统:照亮电脑品质之路
前端·javascript·vue.js