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) */
这两个存的都是主次设备号,都要通过宏major
和minor
来取出来
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
若没有文件掩码时,文件的默认权限为0666,文件夹的默认权限为0777。
原因:创建文件一般是用来读写,所以默认情况下所有用户都具有读写权限,但是没有可执行权限,所以文件创建的默认权限为0666而文件夹的x权限表示的是打开权限,所以这个权限必须要有,所以文件夹的默认权限为0777。
上述的权限是在没有umask情况下的默认权限。但是系统为了保护用户创建文件和文件夹的权限,此时系统会有一个默认的用户掩码(umask),大多数的Linux系统的默认掩码为022。用户掩码的作用是用户在创建文件时从文件的默认权限中去除掩码中的权限。所以文件创建之后的权限实际为:
bash
#文件创建权限
默认权限(文件0666,文件夹0777)-umask
所以在用户不修改umask的情况下,创建文件的权限为:0666-0022=0644。创建文件夹的权限为:0777-0022=0755
粘住位
是 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
。
-
正常权限设置:
-
alice
的权限:drwx------
(只有alice
有读、写和执行权限)bob
的权限:drwx------
(只有bob
有读、写和执行权限)
-
越权操作:
-
-
如果
alice
通过某种方式(比如程序漏洞或错误配置)尝试使用chdir
命令切换到/home/bob
,例如:bashbash 复制代码 cd /home/bob
-
由于
alice
没有权限访问/home/bob
,系统会返回一个权限拒绝的错误:bashbash 复制代码 bash: cd: /home/bob: Permission denied
-
-
潜在的越权风险:
-
- 如果某个程序以
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;
}