日常学习

linux-interface-14

June 14, 2019

文件系统

文件系统是对文件和目录的组织集合

设备

磁盘

文件系统

以下为ext2文件系统 为例

虚拟文件系统(VFS)

是一种内核特征,通过为文件系统操作创建抽象层来解决、屏蔽,各种不同文件系统的不同的实现细节

挂载 mount

虚拟内存文件系统(tmpfs):

linux支持驻留于内存的虚拟文件系统。tmpfs最为复杂。不仅使用RAM还能够利用交换空间。/proc/mount 即是此类文件系统的实现,可以创建tmpfs文件系统并挂载至/tmp来改善编译器类似的频繁使用/tmp目录的应用程序的性能。
内核还将此类文件系统用于:

文件属性

  1. int stat(const char * pathname, stuct stat * statbuf): 系统调用: 获取与文件有关的信息, 大部分都来自于i节点, struct stat结构如下:
    stuct stat {
    dev_t st_dev; 
    dev_t st_rdev; 
    ino_t st_ino;
    mode_t std_mode;
    n_link_t st_nlink;
    uid_t st_uid;
    g_id_t st_gid;
    time_t st_atime;
    time_t st_mtime;
    time_t st_ctime;
    }
    

    部分详细解释:

    • st_devo 字段标识文件所驻留的设备。st_ino 为文件的i节点,两者标识文件系统中的唯一文件。设备文件的i节点,st_rdev 字段包含了主、辅ID, 否则st_dev 包含信息
    • st_uid, st_gid 标识文件属主。 st_nlink 包含了指向文件的硬链接数目。 st_mode 标识了文件类型、已经权限。
    • st_atime, st_mtime, st_ctime, 为文件时间戳, 分别标识: 文件上次访问时间、修改时间、以及文件状态修改时间。
    • 系统调用: utime, utimes, utimensat 都可以修改文件的时间戳。
  2. 新建文件的属性: 新建文件时的属主取决于进程的有效用户ID。新建文件的组ID是多变的,下面为ext2下面新建文件组ID所遵循的规则:
文件系统mount选项 父目录设定set-group-id 标记 新建文件的组ID取值
-o grpid, -o bsdgroups 忽略 父目录组ID
-o nogrpid, -0 sysvgroups 进程的组id
-o nogrpid, -0 sysvgroups 父目录组id
  1. chown 系统调用, 可以更改文件属主, 权限为: 1)特权级别进程(root) 2)进程的有效用户ID与文件的用户ID匹配。当文件的属主或者属组发生了改变时,set-user-id, set-group-id 权限位会关闭。改变的是目录的时候, 其set-group-id并不会发生改变。目录的set-group-id 并不是为了创建 set-group-id程序,而是为了控制在该目录下创建文件的所有权。

  2. 目录下的权限

    • 权限:
    • 读权限: 可以ls 目录下的内容。
    • 写权限: 可在目录下创建、删除文件(删除文件对文件本身并不需要权限)
    • 可执行权限: 可访问目录中的文件,也成为search权限
  1. 文件系统的权限检查(包含目录、文件):只在初次调用时候,进行检查,open文件之后的fd,再次对其进行操作,是不再会进行检查的。规则如下:
    • 特权进程, 授予访问权限
    • 若进程的有效用户ID与文件的用户ID相同,内核会根据文件的属主权限,授予进程相同的权限。
    • 若进程的有效组ID或任意附属组ID与文件的组ID相匹配。根据文件的属组权限,授予进程
    • 若上面都不满足, 内核根绝other授予进程权限。
      内核检查会在最后时候,即是未能通过调用规则的检查的时候,才会检查检查是否属于特权进程。
      上述检查可能造就一些奇怪的现象(从代码逻辑上来看是正常的): 比如文件的owner没有rw权限,
      同样适用于目录的权限检查, 但是特权进程总是拥有可执行权限(即是总是可以搜索),特权进程总是拥有通过任何的权限检查
  2. Sticky位: 现在的UNIX的Sticky权限位 其限制删除的作用,为目录设定权限位时候, 表明非特权进程时,只有对目录有写权限+为文件或目录的属主时才能对目录下的文件进行 删除、重命名操作。(可以用于建立多个用户共享的目录文件, 每个用户管理自己的文件、访问其他人的文件。但是不能删除其他人的文件) 典型的应用在/tmp 目录下, chmod +t 可以添加 sticky位,

  3. 进程的文件创建掩码: umask 是一种进程属性, 当进程创建文件或者目录时候,该属性执行屏蔽那些权限位。进程的umask通常继承父shell, 所以可以在shell中使用umask来改变shell进程的umask,进而影响所有的shell子进程。umask(mode_t mask) 系统调用总是调用成功,并返回之前的umask数值。

  4. i节点扩展属性(EA)

目录和链接的实现结构

  1. 目录的实现结构:
    • i节点的类型标记为目录
    • 目录内容是经过特殊组织的文件表格(硬链接列表): 文件名和i-node编号。
    • 不能够read目录内容, 直接编辑目录内容是不允许的,不具有可移植性。需要使用相关的系统调用, open, link, mkdir, symlink ,unlink, rmdir etc
  2. 硬链接: 创建链接指向相同的i-node节点,成为硬链接。会改变 指向文件的i-node节点的 链接数。限制有: 1) 因为采用i-node编号,导致不能跨越文件系统。 2)不能为目录创建硬链接,会导致诸多的系统程序陷入混乱的链接环路中。
  3. 符号链接: 特殊的文件类型, 内容为另一个文件的名称(字符串)
    • ln -s 创建, 内容可以是绝对路径和相对路径,解释相对路径时,以链接本身的位置作为参照点。
    • 符号链接的地位不如硬链接,i节点并不体现符号链接数量。即便移除了文件,链接依然存在,成为空连接。甚至可以创建不存在文件的链接。
    • 因为符号链接的内容为字符串,所以可以跨越文件系统。一些系统调用中对符号链接进行解析,因为可能存在环路问题,所以规定了街引用的一个次数范围。_POSIX_SYMLOOP_MAX 常量规定了最大次数限制。(无论系统调用是否对符号链接进行解析,其总是对符号链接中的目录部分进行接引用,区分在于是否对文件接引用),
    • 权限为引用的文件的权限。
      dir
      dir

系统调用:

进程都有两个目录相关属性: 1)根目录, 2)工作目录。没想到过还可以修改根目录

  1. 进程的当前工作目录:定义了该进程解析相对路径的起点, 继承自父进程。
    • getcwd(char * wdbuf, size_t size): 拥有获取当前进程的工作目录, 当前工作目录的最大字符串长度应小于4096 的字节限制。超出将不再可靠。
    • chdir(const char *pathname): 更改调用进程的当前工作目录,可以为符号链接。
    • /proc/PID/cwd 为进程当前工作目录的映射
  2. 进程的根目录: 该目录是解释绝对路径的起点,继承自父进程。
    • chroot(const char *pathname): 系统调用改变进程的根目录,目的为监理一个chroot监禁区。将所有对绝对路径的解释设定为一个目录的起点。将应用程序设定为文件系统的特定区域。
    • 需要将 根目录的 .与.. 设定为自身, 防止根据相对路径,逃出监禁区
    • 并不是所有的程序都可以运行在监禁区,因为大多数程序与共享库之间采用动态链接的方式。因为需要复制一套标准的共享库来实现运行动态链接的程序(通过绑定挂载可以实现共享库共享)
    • 不能在监禁区中方式set-user-id-root 程序,方式获取root的权限
    • 需要chdir, 防止通过相对路径逃出监禁区
    • 如果进程之前持有监禁区之外的文件描述符,可以逃出监禁区
    • 可以通过套接字传递监禁区之外的文件描述符,所以需要避免
  3. 总结: chroot调用并不能真正的成为安全的控制机制,因为逃出监禁区的方式有很多,还不如实现更加现代的docker容器技术来避免。
    realpath(const char * pathname, char * resolved_path): 对路径解析为绝对路径。
    dirname, basename: 将路径名解析成, 目录和文件名两部分。