Linux基础 文件和目录
文件和目录
前言
本章讨论文件属性和文件系统内容。除了上一章讨论的普通文件,Linux的文件概念还包括:目录、设备等。在Linux系统中,文件的种类包括:普通文件、目录、符号链接、块设备、字符设备、管道、套接字。
本章讨论的主要内容为普通文件、目录和符号链接。它们的公共特点是,真实的保存在了硬盘中,而其它类型的文件是内核产生的文件,在硬盘中并不存在。
文件属性
通过stat函数或者stat命令可以获得文件属性。
-
实际账户和有效账户
| 账户 | 解释 |
|---|---|
| 实际账户 | 登陆系统时的账户 |
| 有效账户 | 决定进程的访问资源的账户 |
3.5 文件访问权限
文件使用了9个位来表示访问权限,和文件类型一起,保存在st_mode中。此9位分成3组,每组3个位,分别表示读/写/执行权限,而三个组分别表示拥有该文件的账户,拥有该文件的组,其它用户组的权限。如果对应位是1,表示有权限,如果是0表示没有权限。
| 1 | 1 | 1 | 1 | 0 | 1 | 1 | 0 | 1 |
|---|
文件访问权限经常用8进制来表示,比如上表的权限位可以表示为0755,意思是拥有它的账户对这个文件有读/写/执行权限,而拥有它的组有读/执行权限,其它账户对它有读/执行权限。
Linux提供一些宏,来测试文件的权限位
-
可以通过access函数来测试程序是否有访问某文件的权限。
-
创建文件时,可以指定文件的访问权限位,但是会收到umask位影响。
-
可以通过chmod来修改文件的权限位
3.6 其它权限位
3.6.1 SUID
只能对文件设置,如果文件设置了该位,那么该文件被运行时,对应的进程的权限,不是运行该程序账户的权限,而是拥有该用户的权限。
在对文件未设置SUID的情况下:
如果对文件设置了SUID,那么:
可以通过chmod u+s或者chmod u-s来设置获取去除SUID。
设置SUID可以让一个普通账户拥有它不该有的权限,容易产生安全漏洞。
3.6.2 SGID
可以对文件和目录设置,如果对文件设置,那么它的作用类似SUID,不过影响的是组。
如果对目录设置,那么拷贝到该目录下的文件都会被置位,除非拷贝是带
-p参数。 在Ubuntu下测试并不如此。
在Ubuntu下设置了目录的SGID之后,在那个目录下创建的文件,拥有者是有效账户,而拥有组是该目录的拥有组。
命令:chmod g+schmod g-s
3.6.3 StickyBit
可以对文件或者目录设置,如果对文件设置,那么当这个文件运行并退出后,系统依旧保留该文件对应的映象,这样这个程序再次运行时,就不需要加载再加载了。这个属性的作用并不大,因为它占用了内存。
如果对目录设置,那么表示在该目录下,账户只能删除和修改它自己创建的文件,对于其它账户创建的文件,它不能修改和删除。这个位作用比较大,在一些公共目录,往往有这个属性,比如/tmp
命令:chmod o+tchmod o-t
总结:
| 位 | 设置对象 | 设置方法 | 查看 | 效果 |
|---|---|---|---|---|
| SUID | 文件 | chmod u+s | 如果用户执行权限位为s或者S,则表示SUID有设置 | 当该文件被执行时,进程所拥有的权限是拥有该文件的账户权限 |
| SUID | 目录 | 不可设置 | ||
| SGID | 文件 | chmod g+s | 如果组执行权限位为s或者S,则表示GUID有设置 | 当执行该文件时,进程所属组是该拥有该文件的组 |
| SGID | 目录 | chmod g+s | 同上 | 在该目录中创建文件时,该文件的所属组是目录的所属组 |
| StickyBit | 文件 | chmod o+t | 如果其他执行权限位为t或者T,那么该文件有设置StickyBit | 执行该文件并退出后,系统保留该文件占用的一些内存,以便加快下一次的加载运行 |
| StickyBit | 目录 | chmod o+t | 同上 | 账户只能修改和删除该目录下属于该账户的文件,不能修改该目录下其他账户创建的文件 |
3.7 文件长度
st_size保存文件的长度,write函数会修改该属性,也可以通过truncate修改文件大小,truncate可以扩大文件或者缩小文件,缩小文件时,文件内容会被删减。
文件大小可以通过ls,wc -c,stat命令获取。
也可以通过fseek和ftell函数配合获取,或者直接通过stat函数获取文件长度。
3.8 文件系统
3.8.1 文件管理
文件系统描述文件在硬盘中的组织,保存在硬盘中的文件只有普通文件、目录、软链接。
为了更加方便的管理持久性文件存储,操作系统一般对硬盘进行有规划的管理,规划包括:
-
分区
-
格式化
文件系统指一个分区内,文件存储的组织方式。
3.9 文件删除
使用unlink命令和函数可以删除一个文件。
如果此时文件已经打开,那么该文件也可以被unlink,但是删除操作不会立即执行,而会被保留到文件关闭时执行。
3.10 文件时间
对文件的访问,会导致文件时间发生变化。系统会自动记录用户对文件的操作的时间戳,以便将来可以查询文件修改时间。
如果需要故意修改,那么可以通过utime函数,修改文件的访问时间和修改时间。
touch命令也可以将文件的时间修改为当前时间。touch命令的副作用是,如果参数所指文件不存在,那么创建一个空文件。
当用户进行大规模拷贝时,cp操作会修改文件的访问时间,如果想提高效率,可以使用-p选项,避免文件属性的修改,同时加快速度。
#include#include int main() { struct utimbuf buf; buf.actime = 0; buf.modtime = 0; utime("a.out", &buf); }
利用utime来修改文件的访问时间和修改时间
#include "../h.h" int main() { struct utimbuf buf; buf.actime = 0; buf.modtime = 0; utime("testfile", &buf); struct timeval tv[2]; tv[0].tv_sec = 100000; tv[0].tv_usec = 10000; tv[1].tv_sec = 100000; tv[1].tv_usec = 10000; utimes("testfile", tv); }3.11 目录操作
3.11.1 创建和删除目录
mkdir和rmdir
3.11.2 遍历目录
opendir,closedir,readdir,rewinddir,telldir,seekdir
遍历目录
#include "../h.h" int rm(const char* path) { // 判断path是个文件还是目录 // 如果是文件,直接unlink然后返回 struct stat stat_buf; int ret = stat(path, &stat_buf); if(ret < 0) { perror("stat"); return -1; } if(!S_ISDIR(stat_buf.st_mode)) { unlink(path); return 0; } // 如果path是目录,遍历目录中的所有目录项 char buf[1024]; DIR* dir = opendir(path); if(dir == NULL) return -1; struct dirent* entry = readdir(dir); while(entry) { // 通过entry得到文件信息 sprintf(buf, "%s/%s", path, entry->d_name); if(entry->d_type == DT_REG || entry->d_type == DT_LNK) { unlink(buf); } if(entry->d_type == DT_DIR) { // 忽略.和..目录 if(strcmp(entry->d_name, ".") == 0 ||strcmp( entry->d_name, "..") == 0) { entry = readdir(dir); continue; } rm(buf); } entry = readdir(dir); } closedir(dir); rmdir(path); return 0; } int main(int argc, char* argv[]) { if(argc == 1) { printf("usage: %s [pathname]\n", argv[0]); return 0; } rm(argv[1]); return 0; }seekdir和telldir
#include "../h.h" int main() { long loc; DIR* dir = opendir("testdir"); // no // seekdir(dir, 2); // struct dirent* entry; while(1) { loc = telldir(dir); entry = readdir(dir); if(entry == NULL) break; if(strcmp(entry->d_name, "a") == 0) { // 记录文件a的位置 break; } } seekdir(dir, loc); while(1) { entry = readdir(dir); if(entry == NULL) break; printf("loc is %d, entry->d_name=%s\n", (int)telldir(dir), entry->d_name); } // 将文件指针回到a的位置 // seekdir(dir, loc); }#include#include #include #include int main(int argc, char* argv[]) { DIR* dir = opendir(argv[1]); struct dirent* entry; while(1) { entry = readdir(dir); if(entry == NULL) break; // linux下,.开头是隐藏文件 if(entry->d_name[0] == '.') continue; printf("%s\n", entry->d_name); } closedir(dir); }
3.12 练习
3.12.1 实现文件拷贝,保留文件属性
#include "../h.h" int main(int argc, char* argv[]) { if(argc != 3) { printf("usage: %s [srcfile] [dstfile]\n", argv[0]); return -1; } const char* filesrc = argv[1]; const char* filedst = argv[2]; FILE* fp_src = fopen(filesrc, "r"); FILE* fp_dst = fopen(filedst, "w"); char buf[4096]; while(1) { int ret = fread(buf, 1, sizeof(buf), fp_src); if(ret <= 0) break; fwrite(buf, ret, 1, fp_dst); } fclose(fp_src); fclose(fp_dst); // 获取源文件属性 struct stat src_stat; stat(filesrc, &src_stat); // 修改目标文件时间 struct utimbuf timbuf; timbuf.actime = src_stat.st_atime; timbuf.modtime = src_stat.st_mtime; utime(filedst, &timbuf); // 修改权限 chmod(filedst, src_stat.st_mode); // 修改所有者 int ret = chown(filedst, src_stat.st_uid, src_stat.st_gid); if(ret < 0) { perror("chown"); } return 0; }3.12.2 实现目录打包到文件,将文件解包成目录
#include "../h.h" #include #include3.13 函数和命令
3.13.1 函数
stat/lstat:查看文件属性
chmod:修改文件权限
chown:修改文件的所有者
utime:修改文件时间
unlink:删除文件
link:创建硬链接
symlink:创建软链接
rmdir:删除空目录
mkdir:创建空目录
opendir:打开目录
closedir:关闭目录
readdir:读取一个目录项,并且将目录项指针移到下一项
seekdir:修改目录项指针
rewainddir:重置目录项指针
telldir:获得当前目录向指针
判断权限位宏 S_IRUSR(stat.st_mode)
判断文件类型宏S_ISDIR(stat.st_mode)
3.13.2 命令
stat:查看文件属性
chmod:修改文件权限
chown
unlink:删除文件(不会跟随)
ln:创建链接
mkdir
rmdir
rm
cp
dd:拷贝数据(可以拷贝文件,也拷贝块设备)
wc:计算文件内容的行数、单词数、字节数
which:查找非内置的命令位置
fdisk:查看磁盘信息、分区
mkfs:在分区中创建文件系统(ext2,ext3,ext4, fat32, ntfs, xfs,nfs)
mount:挂载
umount:取消挂载