UNIX高级环境编程(4)Files And Directories - umask、chmod、文件系统组织结构和链接
本篇主要介绍文件和文件系统中常用的一些函数,文件系统的组织结构和硬链接、符号链接。
通过对这些知识的了解,可以对Linux文件系统有更为全面的了解。
1 umask函数
之前我们已经了解了每个文件与权限相关的9个位(bit),我们现在来了解一下当每个进程创建文件时默认会设置该文件的文件权限(the file mode creation mask)。
umask函数设置该进程默认创建文件的权限掩码(the file mode creation mask),并且返回之前的权限掩码值。
#include <sys/stat.h>
mode_t umask(mode_t cmask);
the file mode creation mask的作用:当进程创建新文件时,会根据这个掩码值创建文件,在掩码值中打开的位,对应的新文件的权限位会被关闭。
umask的功能简单地说就是创建新文件时屏蔽掉用户不希望生效的权限位。
Example:
#include "apue.h"
#include <fcntl.h>
#define RWRWRW (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
int
main(void)
{
umask(0);
if (creat("foo", RWRWRW) < 0)
err_sys("creat error for foo");
umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
if (creat("bar", RWRWRW) < 0)
err_sys("creat error for bar");
exit(0);
}
运行截图:
结果说明:
shell的umask命令显示当前文件创建权限掩码。
0022表示创建出来的新文件,组用户和other用户没有写该文件的权限。
在程序中,首先用默认的权限设置创建了文件foo,它的权限位666,至于为什么当前用户也没有执行权限,我还没搞清楚,留一个坑 @suzhou。
然后修改umask值为0077,这样就屏蔽掉了组用户和其他用户的读写权限位,因此再创建新文件bar,它的权限位是600。
2 chmod、fchmod和fchmodat函数
函数作用:修改已有文件的权限位。
函数声明:
#include <sys/stat.h>
int chmod(const char *pathname, mode_t mode);
int fchmod(int fd, mode_t mode);
int fchmodeat(int fd, const char *pathname, mode_t mode, int flag);
区别:
- chmod:作用在某个指定的文件上
- fchmod:作用在某个指定的已经打开的文件上
- fchmodat:类似之前的函数,当pathname为绝对路径,或者fd取值为AT_FDCWD并且pathname为相对路径时,fchmodat函数的作用和chmod相同;否则,工作目录为fd指定父目录路径加上pathname为父目录的子目录。参数flag用来指定fchmodeat的行为:取值为AT_SYMLINK_NOFOLLOW时,该权限修改并不追踪符号链接,只修改该符号链接文件的权限。
权限要求:要修改一个文件的权限,需要当前进程的effective user ID和文件的所有者ID相同,或者进程有超级用户权限。
代表各个权限位的常量如下表所示:
Example:
修改在上例中创建的两个文件foo和bar的权限。
#include "apue.h"
int
main(void)
{
struct stat statbuf;
/* turn on set-group-ID and turn off group-execute */
if (stat("foo", &statbuf) < 0)
err_sys("stat error for foo");
if (chmod("foo", (statbuf.st_mode & ~S_IXGRP) | S_ISGID) < 0)
err_sys("chmod error for foo");
/* set absolute mode to "rw-r--r--" */
if (chmod("bar", S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0)
err_sys("chmod error for bar");
exit(0);
}
运行结果:
结果说明:
- 执行程序之前,foo的权限为666,程序中首先使用stat函数获取文件的信息存在statbuf中,置组用户执行位(S_IXGRP)为0,并置该位为S(S_ISGID)。
- 执行程序之前,bar的权限位600,程序中设置该文件的组用户和其他用户有读权限,设置成功之后,可以看到bar的文件权限位644.
高级权限(SUID/SGID/Sticky Bit )档案特殊权限 说明:
- SUID (Set UID): 会制作出 s 的权限,权限数字是 4 。这个权限只能作用于可执行文件,不可作用于目录,shell script也不行。使一般使用者在执行文件时,暂时具有该文件拥有者的权限。
- SGID (Set GID): 制作出 s 权限,权限数字是 2 。这个权限可以作用于目录和文件。作用于目录时,在该目录下建立的所有文件和目录的group都会是该目录的group;作用于文件时,则不论使用者是谁,在执行该程序的时候, 他的有效群组 (effective group) 将会变成该程序的群组所有人 (group id)。
- Sticky Bit: 制作出 t 权限,权限数字是 1 。这个权限只能作用于目录。在该权限作用的目录里,如果使用者具有w,x权限,那么他所创建的文件和目录,只有档案的拥有者和root才能删除。
- 注意:在字符表示权限时,s和t是出现在x的位置的,意思是在可执行的文件和目录时,它们才是有意义的。如果文件或目录本身u、g或o就没有可执行权限,那么它所在的权限组出现的是S或者T,表示空的意思。用数字表示权限,就是在rwx前面加上4、2、1的组合就是了,如1777。
3 Sticky Bit(S_ISVTX)
Sticky bit有一个冗长的历史,这里并不赘述。
作用:如果文件夹的sticky bit被打开,则文件夹中的文件被删除或者重命名需要满足一下的条件之一:当前用户对该文件夹有写权限;当前用户是该文件的所有者;当前用户是该文件夹的所有者;当前用户是超级用户。
典型的打开了sticky bit的两个文件夹是/tmp 和/var/tmp,这两个文件夹对所有用户都有读写和执行权限,这样所有用户都可以在该文件夹下创建文件,但是并不能删除由其他用户所有的文件。
4 chown、fchown、fchownat和lchown函数
函数作用:修改文件所属的用户ID和组ID,如果参数owner和group有一个的值为-1,对应的ID值不变。
#include <unistd.h>
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 grou, int flag);
int lchown(const char* pathname, uid_t owner, gid_t group);
区别:
当目标文件不是一个符号链接时,上面的四个函数的作用基本相同。
当目标文件是一个符号链接文件时,并且函数lchown和fchownat的flag设置为AT_SYMLINK_NOFOLLOW时,这两个函数修改符号链接文件本身的所有者,而不是符号链接指向的文件。
fchown函数修改fd代表的已打开文件的所有者。因为fchown操作的是已经打开的文件,所以它不可以用来修改符号链接的所有者。
fchownat函数的行为:当参数pathname是绝对路径,或者参数fd的值为AT_FDCWD并且pathname为相对路径时,fchownat函数的行为和chown和lchown类似;具体来说,flag取值AT_SYMLINK_NOFOLLOW时,行为和lchown类似;否则行为和chown类似。当fd代表已打开的文件夹,pathname为一个相对路径时,目标文件为以fd为父目录,pathname为相对的子目录所指的文件。
5 文件大小(File Size)
stat结构体中的成员st_size表示文件大小字节数。
字段st_size只对常规文件(regular files)、目录文件(directories)和符号链接文件(symbolic links)。
更多的细节:
- 常规文件大小允许为0;
- 目录文件的大小往往是整数,如16或512;
- 符号链接文件的大小是被链接文件名的长度,如符号链接lib—>usr/lib,大小为7,正好是字符串“usr/lib”长度,并不需要像C风格字符串一样+1。
6 文件截断(File Truncation)
有时候我们需要通过丢弃文件结尾一部分来截短文件。
函数声明:
#include <unistd.h>
int truncate(const char* pathname, off_t length);
itn ftruncate(int fd, off_t length);
函数作用:把目标文件截断到参数length指定的长度。
参数:如果参数length的值比指定文件的长度小,则文件超过length长度的部分不可读;如果length的值比指定文件的长度大,文件长度扩大到length,扩充部分填充0(可能是一个hole)
7 文件系统(File Systems)
为了理解文件链接,我们需要对Unix文件系统的结构有一个概念性地认识。
一个磁盘(disk drive)可以被分为多个分区(partition),每个分区都可以包含一个文件系统(file system)。
inode是一个定长的索引,包含了一个文件的大部分信息。
关于 i-nodes和data blocks的内存布局如下图所示:
说明:
- 两个目录索引(directory entry)指向同一个i-node索引(i-node entry)。即多个文件夹包含同一个文件(可能通过硬链接或软链接),每一个inode都由一个字段link count,包含指向该i-node的目录索引数。只有当该数值(link count)为0时,该文件才会被删除(释放文件所占用的data blocks)。索引后面介绍的移除目录的函数不叫delete,而是unlink。在stat数据结构中,link count对应的字段为st_nlink。这里提到的链接为硬链接。
- 另外一种链接为符号链接。符号链接文件对应的data block中存有该链接指向文件的文件名(之前有提到)。对应的i-node中文件类型字段的值应为S_IFLINK,告诉文件系统当前文件为符号链接。
- i-node包含了对应文件的所有信息:如文件类型,指向文件数据块的指针,文件权限位,文件大小等。stat数据结构中得大部分字段都是从i-node中获取。有两个字段值存在目录索引中:文件名(file name)和i-node数(i-node number)。i-node数目对应的数据类型为ino_t。
我们再了解一下目录文件的link count字段。
当我们执行下面的命令:
mkdir testdir
内存中数据组织结构如下所示:
从上图可以看到,目录下每多加一个文件或文件夹,link count都会加1。
图中显示的都很清楚,不做过多赘述(其实是我懒了,哈哈...)
8 link、linkat、unlink、unlinkat和remove函数
从前面可以知道,多个目录索引可以指向同一个文件,即多个目录可以包含同一个文件。
link和linkat的作用是创建一个已存在文件的链接。
函数声明:
#include <unistd.h>
int link(const char* existingpath, const char* newpath);
int linkat(int efd, const char* existingpath, int nfd, const char* newpath, int flag);
函数细节:
- 函数创建一个新目录索引newpath,关联到已存在文件existingpath;
- 如果newpath已经存在,返回错误;
- newpath只有最后一个文件名会被创建,路径其他部分必须已存在;
- linkat的参数的作用和前面xxxat函数介绍过的类似,这里不再赘述;
- 如果被链接的文件是一个符号链接文件,参数flag用来控制当前新建的链接是关联到该符号链接(默认行为),还是该链接指向的文件(AT_SYMLINK_FOLLOW);
- 一般来说不可以扩文件系统创建链接,防止文件系统间的硬链接循环;
函数声明:
#include <unistd.h>
int unlink(const char* pathname);
int unlinkat(int fd, const char* pathname, int flag);
函数细节:
- 函数会删除目录索引,并且对应文件的inode的link count减1,如果link count不为0,则说明还有其他目录索引指向该文件,不删除该文件,否则删除该文件;
- 进程必须对该目录有写权限和执行权限;
- 如果pathname是一个符号链接,unlink移除该符号链接文件,而不是该符号链接指向的实体文件。没有函数实现直接删除实体文件这个功能。
和unlink类似功能的函数remove:
#include <stdio.h>
int remove(const char* pathname);
函数细节:
- remove一个文件相当于调用unlink,remove一个文件夹相当于调用rmdir(后面会介绍)。
9 rename和renameat函数
函数声明:
#include <stdio.h>
int rename(const char* oldname, const char* newname);
int renameat(int oldfd, const char* oldname, int newfd, const char* newname);
函数细节:
- 如果oldname不是一个目录路径,说明我们正在重命名一个文件或符号链接。这里newname已存在,则不可以是一个已存在目录的名字。如果newname已存在并且不是目录名,则删除已有文件newname,将oldname所指文件重命名为newname。权限要求:我们必须有包含oldname和newname文件的目录的写权限。
- 如果oldname是一个目录路径,则我们在重命名一个目录。如果newname已存在,它必须是一个目录的名字,并且该目录必须是空目录(只有. 和 ..索引)。这种情况下,删除已存在目录newname,将待重命名目录oldname重命名为newname。还需要注意的,newname不能包含oldname。例如:不能将目录/usr/foo重命名为/usr/foo/testdir,因为/usr/foo是新目录名的前缀,无法被删除。
- 如果oldname或newname为一个符号链接名,那么被重命名的时符号链接本身,不是符号链接指向的文件。
- 我们不能重命名dot(.)和dot-dot(..),更确切地说,这两个特殊名不能作为路径名的最后一部分。
- 如果oldname和newname相同,则函数返回成功,不做任何事情。
对renameat函数的参数含义不再赘述。
10 符号链接(Symbolic Links)
符号链接可以认为是文件的非直接指针,而硬链接(hard link)可以看做是文件的直接指针,因为它直接指向文件的inode。
- 硬链接通常不可以跨文件系统;
- 只有超级用户可以对一个目录创建硬链接。
符号链接并没有文件系统间的限制。
由于符号链接的存在,我们在操作文件或目录时,总是需要知道该文件是否是一个符号链接,然后判断该函数的操作对象是符号链接指向的文件还是符号链接本身。
下图总结了一些常用函数是否追踪链接,仅供参考:
Example:
链接情况如下图所示:
可以发现,由于符号链接而出现了一个环。
再来试验一下:
我们发现,这样会无限循环下去。
解除循环的方法就是使用unlink删除该符号链接。需要注意的一点是,如果这里出现的链接是硬链接,则删除这个循环链接会困难得多。因此只有管理员权限才可以用link函数对一个文件建立硬链接。
有时候符号链接会造成一些迷惑的事情,如果使用者对符号链接不熟悉的话。
如下所示:
符号链接可以链接一个不存在的文件,如果使用者对符号链接不熟悉,则很容易被这种情况迷惑。
这时,可以使用ls的”-l"命令看到有 “->"符号,或者前面符号位第一位的”l”表示符号链接,或者用ls得“-F”命令,符号链接文件后面会跟一个@符。
11 创建和读取符号链接(Symbolic Links)
函数作用:创建符号链接
函数声明:
#include <unistd.h>
int symlink(const char *actualpath, const char* sympath);
int symlinkat(const char* actualpath, int fd, const char* sympath);
参数说明:
- 函数并不要求actualpath一定存在,在上面的例子中也看到了这一点。
因为open函数会打开符号链接指向的实际文件,所以需要一个函数打开符号链接本身。
函数声明:
#include <unistd.h>
ssize_t readlink(const char* restrict pathname, char* restrict buf, size_t bufsize);
ssize_t readlinkat(int fd, const char* restrict pathname, char* restrict buf, size_t bufsize);
如果调用成功,函数从符号链接中读取buf长的内容。
参考资料:
《Advanced Programming in the UNIX Envinronment 3rd》
UNIX高级环境编程(4)Files And Directories - umask、chmod、文件系统组织结构和链接的更多相关文章
- UNIX高级环境编程1
UNIX高级环境编程1 故宫角楼是很多摄影爱好者常去的地方,夕阳余辉下的故宫角楼平静而安详. 首先,了解一下进程的基本概念,进程在内存中布局和内容. 此外,还需要知道运行时是如何为动态数据结构(如链表 ...
- UNIX高级环境编程(14)文件IO - O_DIRECT和O_SYNC详解 < 海棠花溪 >
春天来了,除了工作学习,大家也要注意锻炼身体,多出去运动运动. 上周末在元大都遗址公园海棠花溪拍的海棠花. 进入正题. O_DIRECT和O_SYNC是系统调用open的flag参数.通过指定o ...
- Unix高级环境编程
[07] Unix进程环境==================================1. 进程终止 atexit()函数注册终止处理程序. exit()或return语句: ...
- UNIX高级环境编程(3)Files And Directories - stat函数,文件类型,和各种ID
在前面的两篇,我们了解了IO操作的一些基本操作函数,包括open.read和write. 在本篇我们来学习一下文件系统的其他特性和一个文件的属性,涉及的函数功能包括: 查看文件的所有属性: 改变文件所 ...
- UNIX高级环境编程(5)Files And Directories - 文件相关时间,目录文件相关操作
1 File Times 每个文件会维护三个时间字段,每个字段代表的时间都不同.如下表所示: 字段说明: st_mtim(the modification time)记录了文件内容最后一次被修改的时 ...
- UNIX高级环境编程(10)进程控制(Process Control)- 竞态条件,exec函数,解释器文件和system函数
本篇主要介绍一下几个内容: 竞态条件(race condition) exec系函数 解释器文件 1 竞态条件(Race Condition) 竞态条件:当多个进程共同操作一个数据,并且结果依赖 ...
- UNIX高级环境编程(9)进程控制(Process Control)- fork,vfork,僵尸进程,wait和waitpid
本章包含内容有: 创建新进程 程序执行(program execution) 进程终止(process termination) 进程的各种ID 1 进程标识符(Process Identifie ...
- UNIX高级环境编程(16)文件系统 < 雨后 >
来点绿色放松一下眼睛吧 :) 文件系统是对文件和目录的组织集合. 一 设备文件 设备文件和系统的某个设备相对应. 设备驱动程序 处理设备的所有IO请求. 提供了一致的API接口,对应于系统调用的ope ...
- UNIX高级环境编程(15)进程和内存分配 < 故宫角楼 >
故宫角楼是很多摄影爱好者常去的地方,夕阳余辉下的故宫角楼平静而安详. 首先,了解一下进程的基本概念,进程在内存中布局和内容. 此外,还需要知道运行时是如何为动态数据结构(如链表和二叉树)分配额外内 ...
随机推荐
- 12-mapReduce的简介和yarn搭建
Hadoop的核心组件之er: mapreduce 目前的计算框架 mapreduce spark storm flink(阿里) mapreduce的核心理念: 移动计算, 而不是移动数据(redu ...
- Oracle驱动classes12.jar 与ojdbc14.jar的区别
简单的说,如果使用jdk1.2和jdk1.3就使用classes12.jar:如果使用的jdk1.4和jdk1.5的,就选用ojdbc14.jar. 驱动包classes12.jar用于JDK 1.2 ...
- 微信小程序动态生成保存二维码
起源:最近小程序需要涉及到一些推广方面的功能,所以要写一个动态生成二维码用户进行下载分享,写完之后受益良多,特此来分享一下: 一.微信小程序动态生成保存二维码 wxml: <view class ...
- unity游戏热更新总结
1.利用反射来做Dll更新 这种方式只支持windows以及安卓这种支持JIT的平台,对于IOS就不适用了,IOS这种Full-AOT的平台不支持生成新的代码,因此这种热更方式很少用到. 2.利用 ...
- Node.js HTTP Server对象及GET、POST请求
上一博客学习了请求与响应,2次读2次写,但有一个问题就是客户端写入的时候怎么知道请求到达.所以HTTP Server对象出现了.它提供了实现HTTP服务器的基本框架.它可以监听端口的底层套接字和接收请 ...
- UIKit 框架之UITextView
// // ViewController.m // UItextView // // Created by City--Online on 15/5/22. // Copyright (c) 2015 ...
- UIKit 框架之Bar、Controller
UIKit框架中有各种Bar,UITabBar.UINavigationBar.UIToolbar.Bar对应的就有一些Item,tabBarItem.navigationItem.toolbarIt ...
- 阿里云centos6.5实践编译安装LNMP架构web环境
LNMP 代表的就是:Linux系统下Nginx+MySQL+PHP这种网站服务器架构. 本次测试需求: **实践centos6.5编译安装 LNMP生产环境 架构 web生产环境 使用 ngx_pa ...
- (二)this、call和apply
在javascript中,this关键字总让一些初学者迷惑,Function.prototype.call, Function.prototype.apply这两个方法广泛的运用.我们有必要理解这几个 ...
- Java使用Redisson分布式锁实现原理
本篇文章摘自:https://www.jb51.net/article/149353.htm 由于时间有限,暂未验证 仅先做记录.有大家注意下哈(会尽快抽时间进行验证) 1. 基本用法 添加依赖 &l ...