第三章 《文件与目录》

                                                   目录

一 、 概述

二 、 stat 结构

三 、 文件类型

四 、 stat 之 st_uid / st_gid

五 、 stat 之 st_mode

六 、 stat 之 st_size

七 、 stat 之 st_atime / st_mtime / st_ctime

八 、 stat 之 st_nlink

九 、 stat 之 st_dev / st_rdev

十 、 stat 之 st_blksize / st_blocks

十一、其它

                                                  正文

一 、 概述

       本章围绕“文件和目录”这一主题 ,详细介绍了用于获取文件和目录信息的 stat 结构 ,包括stat结构体的各个属性以及获取 、修改属性的方法 ;另外 ,stat 结构与 i-node 的区别与联系 , 也要做一些分析 。

 

二 、stat 结构

 

 

2 . 获取 stat 结构

头文件 <sys/stat.h> 有以下函数定义:

 

 

特征:
a . 提供路径名 ,跟随符号链接(对与符号链接 ,buf 返回符号链接所指向的文件的信息);
b . 提供文件描述符 , 该文件须已经打开 ;
c . 提供路径名 ,不跟随符号链接 (对符号链接 ,buf 返回符号链接本身的信息);

 

三 、 文件类型 -- 七种

i     普通文件 (regular file)
ii     目录文件 (directory file)
iii    块特殊文件 (block special file 提供对设备带缓冲的访问) 
iv    字符特殊文件 (character special file 提供对设备不带缓冲的访问 ,一次一字符)
v     命名管道 (named pipe 或 FIFO)
vi    符号链接 (symbolic link)
vii   套接字 (socket) 

 

头文件 <sys/stat.h> 定义如下文件类型宏 ,参数为 stat->st_mode

i     #define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)

ii    #define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)

iii   #define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK)

iv   #define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR)

v    #define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFFIFO)

vi   #define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)

vii   #define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK)

 

另外三种 IPC 对象
POSIX.1 允许将 消息队列 、信号量 、共享存储对象 等表示为文件 ,如下这些宏可以确定相应类型 ,参数为 stat 对象 。
a . S_TYPEISMG()     /* IS MESSAGE */
b . S_TYPEISSEM()   /* IS SEMOPHORE */
c . S_TYPEISSHM()   /* IS SHARED MEMORY */
本书所述四种系统 FreeBSD 、Linux 、Mac OS X 、Solaris 都不将以上三种IPC对象表示为文件 。

 

四 、stat 之 st_uid / st_gid

1 . 新文件和新目录的所有权
i   新文件 -- 创建新文件同时决定所有权

    a)用户ID设置为进程的有效用户ID

    b)组ID可以是进程的有效组ID ,或者是所在目录的组ID
ii  新目录 -- 创建新目录同时决定所有权
    方式与决定新文件所有权的方式相同


2 . 修改文件和目录的所有权
i   函数声明 #include <unistd.h>
     a) int chown (const char * pathname , uid_t owner , gid_t group);
     b) int fchown (int filedes , uid_t owner , gid_t group);
     c) int lchown(const char * pathname  , uid_t owner  , gid_t group);


ii   函数功能及区别
     a)owner 或 group 等于 -1时 ,相应ID不做改变 。跟随符号链接 。
     b)owner 或 group 等于 -1时 ,相应ID不做改变 。
     c)owner 或 group 等于 -1时 ,相应ID不做改变 。不跟随符号链接 ,改变符号链接本身的属性 。


iii  谁有权限修改文件和目录的所有权
     头文件<unistd.h>下可选地定义了常量 _POSIX_CHOWN_RESTRICTED .
     如果定义了 _POSIX_CHOWN_RESTRICTED , 则
     a)用户 ID : 只有超级用户进程才能修改 。
     b)组 ID :超级用户可以修改 。对于非超级用户 ,在不更改UID的情况下 ,才仅仅能更改GID为进程所属的组 。
备注:这一部分的作用不懂!


iv  对文件SUID / SGID 位的影响
     当被非超级用户调用成功时 , 文件的SUID 和 SGID 位都被清零 。

 
5 . 如何检测 _POSIX_CHOWN_RESTRICTED
     #include <unistd.h>
     long pathconf(const char * pathname , int name);
     long fpathconf(int filedes , int name);

 

 

补充内容一:

创建文件的两种方式

 

补充内容二:

--创建目录

--删除目录

--读取目录

--更改当前工作目录

--获得当前工作目录完整路径

 

补充内容三:

--sysconf

--pathconf

--fpathconf

 

 

五 、 stat 之 st_mode

1 . st_mode 是 mode_t 类型 ,即 unsigned int 类型 ,该字段只用该整数的 低16位

2 . st_mode 的0-8位是读写执行权限位 ,第9-11位是特殊的可执行权限位 ,第9位是粘住位 ,第10位是设置组ID位 ,第11位是设置用户ID位 ,第12-15位组合表示文件类型 。

3 . st_mode 的各位含义从如下的常量定义看出

4 . st_mode 的0-8位 。

a)分为三组 ,从低位到高位分别代表其它用户 、同组用户 、文件所有者这三类用户对该文件的读 、写 、执行权限 。(注意:是文件所有者 ,而不是进程的有效用户)

b)读权限决定了是否有权打开文件进行读操作 。对不同类型的文件而言 ,读操作的涵义不同 。例如 ,对于目录 ,就是读目录项 ;对于普通文件 ,就是读文件内容 ;(其它呢?)

c)写权限决定了是否有权打开文件进行写操作 。对不同类型的文件而言 ,写操作的涵义也不同 。例如 ,对于目录 ,就是写目录项 ;对于普通文件 ,就是写文件内容 ;(其它呢?)

d)执行权限

--对于可执行文件 ,则可以执行该文件 ;

--对于目录 ,则如果有写权限 ,就可以在该目录下删除 、增加文件 ;

--对于目录 ,则如果该目录使我们要访问的路径名中的一个部分的话 ,那么就可以通过该目录 ,即搜索该目录 ,寻找一个文件名 。(这样的话 ,需要有读权限配合吧 ,我觉得应该有 。)

--对于其它类型的文件 ,意义何在 ?

 

e)文件权限位限制open / exec 函数

--open 以 O_RDONLY 或 O_RDWR 打开文件 ,则须拥有文件的读权限

--open 以 O_WRONLY 或 O_RDWR 打开文件 ,则须拥有文件的写权限

--open 以 O_TRUNC  打开文件 ,则须拥有文件的写权限

--exec 族函数执行一个可执行文件 ,则须拥有文件的执行权限 ,其该文件必须为普通文件

 

5 . st_mode 之粘住位(第9位)

a) 历史功能 。设置文件一直保存在交换区 ,以使得当文件需要频繁被使用时减少换入换出 ,提高系统运行效率 。

b) Single Unix Specification 允许的功能。设置目录的粘住位 ,则用户要对该目录下的文件进行修改或删除需要具有以下特征:

    i   是文件的所有者

    ii  是目录的所有者

    iii 是超级用户

    粘住位的使用场合:/tmp 目录 。所有用户对/tmp目录都具有读写执行权限 ,因而任一个用户都可以删除修改该目录下所有文件 。通过设置/tmp目录的粘住位 ,可以防止用户删除其他用户的文件 。

 

6 . st_mode 之设置组ID位(第10位)设置用户ID位(第11位)

a)进程有用户id , 组id , 有效用户id , 有效组id 等属性 ,分别记为 uid 、gid 、euid 、egid 。内核主要根据euid 和 egid 测试进程对文件的访问权限 。(另外可能有附加组id属性)

b)进程访问一个可执行文件时 ,如果该文件并没有设置 suid 位 ,则令进程euid = 进程的uid , 进程egid = 进程gid ; 否则 ,令进程euid = 文件uid , 进程egid = 文件gid 。

c)进程每次打开 、创建或删除一个文件时 ,内核都会进行文件访问权限测试 。顺序如下: 
i     如果 euid == 0 , 超级用户 ,允许访问 ; 否则 ii
ii    如果 euid == uid , 则根据用户适当的访问权限位是否被设置决定是否允许访问 ; 否则 iii 
iii   如果 egid == gid 或者进程的附加组id之一等于 gid , 则根据组适当的访问权限位是否被设置决定是否允许访问 ; 否则 iv
iv  如果其他用户适当的访问权限位被设置 ,则允许访问 ,否则拒绝访问 

d) 通过对可执行文件设置SUID 、SGID位 ,使得对资源没有访问权限的进程可以获得更高的访问权限 。

e) SUID 举例 http://www.enet.com.cn/article/2007/1224/A20071224966521.shtml

f ) SGID 举例 http://www.enet.com.cn/article/2007/1224/A20071224966521.shtml

 

7 . 设置SUID / SGID 位 

 

 

 

六 、 stat 之 st_size

1 . 字节为单位的文件长度 ,只对普通文件 、目录文件 、链接文件有意义 。

2 . 普通文件:st_size可以为 0 ,此时将读到EOF

3 . 目录文件:st_size通常是16或512的倍数

4 . 符号链接:文件名中的实际字节数

5 . 文件空洞 ,设置偏移量超过文件尾端并写数据 ,导致产生空洞 。

--拷贝有空洞的文件 ,文件空洞自动填0 ,填0后调用ls指令 ,文件大小不变 。

--du 指令显示文件大小比ls 大 ,因为 ,文件系统使用了若干块存放指向实际数据块的指针 ,du 指令把这些块也计算了进来 。

  

七 、 stat 之 st_atime / st_mtime / st_ctime

1 . st_atime 指最后一次访问问价内容的时间 ;st_mtime 指最后一次修改文件内容的时间 ;st_ctime 指最后一次修改i-node的时间 。

2 . read 修改st_atime ; write 修改st_mtime ; chown / fchown / lchown / chmod / fchown 修改st_ctime 。

3 . ls -u file 显示st_atime(-u 表示 use ?) ; ls 默认显示 st_mtime ; ls -c file 显示st_ctime (c表示 change ?) 。

4 . 修改时间值

a)#include <utime.h>

   int utime(const char * pathname , const struct utimbuf * times);

   struct utimbuf{

         time_t   actime;     /* access time */

         time_t   modtime;  /* modification time */

   };

b)如果

c)

八 、 stat 之 st_nlink

九 、 stat 之 st_dev / st_rdev

十 、 stat 之 st_blksize / st_blocks

十一、其它

 


                                            补充内容一

创建文件的两种方式

1 . 方式一

头文件<fcntl.h> 中如下定义

            int creat(const char * pathname , modet_t mode);

它等价于

            open(pathname , O_WRONLY | O_CREAT | O_TRUNC , mode) ;

该函数根据路径pathname寻找文件 ,如果存在则只写打开(O_WRONLY) , 并截短为 0 (O_TRUNC) ; 如果不存在则创建 (O_CREAT) 。 但缺陷是创建了文件之后只能写 , 不能读 。

 

2 . 方式二

            open(pathname , O_RDWR | O_CREAT | O_TRUNC , mode);

根据路径 pathname 寻找文件 ,存在则为读写而打开 (O_RDWR) ,并将长度截短为 0 (O_TRUNC) ; 不存在则创建文件 (O_CREAT) 。如果想创建一个文件 ,对它先写然后读 , 那么如果使用 creat 函数则需要 , 先creat ,写完后close ,再open 进行读 。而方式二的优点就是能够同时实现以上功能 。

 

 


                                                       补充内容二

1 . 创建新目录
#include <sys/stat.h>
int mkdir(const char * pathname , mode_t mode);
该函数创建一个空目录 ,即页目录 ,其中只有两个目录项 , . 和 .. 目录项 。mode 指定新目录的读写执行权限 ,注意以下几点:

i   mkdir 之前先调用 cmask 函数 ,设置进程的文件模式创建屏蔽字为 0 , 同时保存原来的文件模式创建屏蔽字 ;之后才调用 mkdir 函数 , 最后 恢复原来的文件模式创建屏蔽字 。即:
     int pre = cmask(0);
     mkdir(pathname , my_mode);
     cmask(pre);//恢复
通过这种方式能够确保目录的访问模式被设置为 mode 。

ii  至少开启目录的一个可执行权限位 。
目录tmp模式字为 -r--r--r-- 时 ,文件的所有者只能读该目录的信息 。在该目录的上级目录执行 ls tmp 是合法的 ;但由于没有写权限 ,cd tmp 是非法的 。
目录tmp模式字为 -rw-r--r-- 时 ,文件的所有者可以读目录信息 。在该目录的上级目录执行 cd tmp 是合法的 ;但是由于没有可执行权限 ,在tmp中创建文件是非法的 。
如果关闭所有可执行权限位 , 就使得遍历搜索目录树成为不可能 ,同时 ,由于写权限位缺少了可执行权限位的支持 ,无法在该目录下创建和删除文件 。
关于可执行权限位 ,可以参考本文的 stat->st_mode 部分 。

 

2 . 删除目录
i   函数定义
#include <unistd.h>
int rmdir(const char * pathname);
ii  功能
删除空目录(只包含 . 和 .. 目录项的目录 ,也称作 页目录)
如果调用rmdir使得目录的链接计数成为 0 ,则
a . 如果目录没有被其它进程打开 ,则释放目录所占用的空间 。包括 i-node 和 . 目录项 ..目录项 。
b . 如果目录已经被其它进程打开 ,则在 rmdir 返回之前 ,要删除最后一个链接及 . 目录项和 .. 目录项 。同时 ,在所有进程都不能在该目录下创建新文件 ,并且 ,直到最后一个进程关闭该目录 ,其空间才被释放 。(此处不理解!)


3 . 读取目录信息
头文件<dirent.h>里有如下函数 :
DIR * opendir(const char * pathname);
struct dirent * readdir(DIR * dp);
void rewinddir(DIR * dp);
int closedir(DIR * dp);
long telldir(DIR * dp);
void seekdir(DIR * dp , long loc);

DIR 结构
日后补充!!

dirent 结构
struct dirent{
       ino_t    d_ino;                               /* i-node 号 */
       char     d_name[NAME_MAX + 1];  /* 以 空字符结尾的文件名 */
};

用法
opendir 传入路径 ,获得 DIR 对象 ;
readdir  传入DIR对象 ,可以依次获得封装了目录项的 dirent 对象 ,最后返回 NULL;
由 dirent 对象可以获得 i 节点号 和 文件名 (注意:文件有多种类型 , 不一定是普通文件);
由文件名以及上级目录的绝对路径可以获得该文件的路径;
lstat 或者 stat 函数 ,传入文件的路径 ,可以获得文件的所有信息 ,包括 st_mode 等


4 . 更改进程当前工作目录
i   用户的起始目录
通常存储在口令文件(etc/passwd)中 ,是该用户登录项的第六个字段 ,是登录名的一个属性 。

ii   进程的当前目录
用户登录UNIX系统时 ,当前工作目录是用户的起始目录 ,当前工作目录是进程的属性 。

iii  chdir 函数
#include <unistd.h>
int chdir (const char * pathname)
注意:pathname 以/开始 ,则是绝对路径 ,否则是相对与当前目录的路径
         成功返回 0 ,出错返回 -1

iv  fchdir 函数
#include <unistd.h>
int fchdir(int filedes);
与chdir区别:参数类型不同

 

5 .  获得当前工作目录的完整绝对路径名
i    调用 lstat 函数 ,传入 "." ,获得当前工作目录的 stat 对象 ,由此只能获得 i-node 号 ,而得不到名字 。
ii   调用 opendir 函数 ,传入 ".." , 获得上级目录的 DIR 对象
iii  循环调用 readdir 函数 ,传入 ii 获得的 DIR 对象 ,得到 dirent 对象 ,直到dirp->d_ino == 工作目录的 i-node 号 ,于是获得 文件名 dirp->d_name
iv   用相同的方法逐层上移直到遇到根 ,从而获得绝对路径名
注意 :根目录的上级目录为根目录 ,可以作为终止条件 。
#include <unistd.h>
char * getcwd(char * buf , size_t size);
参数:
buf 需要有足够的空间容纳绝对路径名以及一个 null 终止字符
调用 getcwd 之前要先分配足够的空间 ,可以使用 malloc 函数 ,buf 不能为 NULL
size 为 buf 缓冲区的长度 
返回值:
失败 NULL , 成功 返回指向当前工作目录的字符串指针。(有意义吗!!?)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值