嵌入式学习和发展(六):Linux_Api文件属性

姓名:姜思维         学号:19020100333        学院:电子工程学院

转自:https://blog.csdn.net/weixin_41213648/article/details/88049688?spm=1001.2014.3001.5501

【嵌牛导读】Linux_Api文件属性

【嵌牛鼻子】Linux_Api      文件属性

【嵌牛提问】什么是Linux_Api的文件属性?

【嵌牛正文】

1、引言

上一章通过学习文件io,知道了如何打开文件、对文件进行读写数据等的操作,那么我们这一章将换一个角度,专门围绕文件属性进行相关的讨论。


2、文件类型

之前说过,在Linux下一切皆是文件。应用层看待底层机制时,一切皆以文件的眼光看待,但是底层的机制之间的毕竟是有所不同,根据这些不同,文件也被分为如下几种。

2.1、普通文件(regular file:符号-),其又分为如下两种

a)、文本文件(ascci二进制文件)

b)、二进制文件(纯二进制文件)

对linux内核而言,这两种文件并无区别,具体如何解释均有应用程序说了算。

2.2、目录文件(director file:符号d):一种特殊的文件,用以包含其它文件的文件名字和指向这些

文件对应i节点的节点编号,目录允许读,那么用户就可以读目录文件,但是只有内核可以写目录    文件。

2.3、字符特殊文件(character special file:符号c):对应字符设备。

2.4、块特殊文件(block special file:符号b):对应块设备(如磁盘等)。

2.5、FIFO(符号p):有名管道文件,用于进程间通信,是一种纯软机制。

2.6、套接口(socket:符号s):用于实现跨机进程间的网络通信,当然也可用于实现本地(本机)

    进程间的通信。

2.7、符号连接(symbolic link:符号l):用以指向另外一个文件,类似于windos界面下的快捷图标。

所有的这些文件中,普通文件数量最多,最常见,其次是目录文件。


3、获取文件属性的函数,stat、fstat、lstat

3.1、函数原型

#include

    #include

    #include

int stat(const char *path, struct stat *buf);

int fstat(int fd, struct stat *buf);

int lstat(const char *path, struct stat *buf);

3.2、函数功能

获取文件的属性信息,文件的属性信息存在了磁盘上该文件对应的inode(i结点)结点中,但

是open打开文件时又会从磁盘i节点中复制一份到内存v节点中i节点结构中,这三个函数还

是有一些区别的。

a)、stat:利用文件路径名从磁盘上的i节点中读取文件属性到buf结构体中。

b)、fstat:前提是文件已经打开,利用文件描述符直接从v结点中的i结点结构中读出文件的属

性,该函数直接从内存中读,但是stat直接从磁盘读取。

c)、lstat:如果操作的文件是符号连接文件时,直接读取的是符号连接文件的属性信息,而stat

和fstat读的是符号连接文件指向文件的属性。所以我们说stat和fstat是一个符号跟随的函    数,而lstat不是。有关符号跟随的问题后面将再次讲解。

3.4、参数说明

a)、const char *path:文件路径名。

b)、int fd:文件描述符。

c)、struct stat *buf:存放读出的文件属性信息。

3.4.1、struct stat *buf结构体说明

struct stat {

    dev_t    st_dev;      /* 块设备号 */

    ino_t    st_ino;        /* inode结点号 */

    mode_t    st_mode;    /* 文件类型和文件权限*/

    nlink_t  st_nlink;      /* 链接数 */

    uid_t    st_uid;        /* 文件所属用户ID*/

    gid_t    st_gid;        /* 文件所属组ID */

    dev_t    st_rdev;      /* 字符设备ID */

    off_t    st_size;        /* 文件大小 */

blksize_t st_blksize;    /* 系统每次按块Io操作时,块的大小(一般是512或1024) */

blkcnt_t  st_blocks;  /* 块的索引号 */

    time_t    st_atime;  /* 最后一次访问时间,如read*/

time_t    st_mtime;  /* 最后一次修改时间 */

time_t    st_ctime;  /* 最后一次状态修改的时间,如权限,所有者的修改 */

};

以上这些结构体成员专门用来存放文件的相关属性,囊括了我们讲的7种文件,但是每种类型的文件都不会全用上这些成员项。比如说块设备文件用了块设备ID st_dev后,就不会使用字符设备ID st_rdev,又如文件的大小只有普通文件和目录才有,其它文件是没有的。

3.4.2、st_uid、st_gid

st_uid:文件所属用户ID

st_gid:文件所属组ID

3.4.3、st_mode

st_mode其实是unsigned short(被typedef为了mode_t)的类型,包含了文件类型、设置位和文件权限。

3.4.4、ls -al看到的文件权限

7种文件决大部分属性是相同的,但是每种不同的文件都有自己独特的属性,这些属性都是stat函数从磁盘中读到了struct stat结构体中,ls -al的目的就是把struct stat结构体描述共有属性显示出来,我们下面以普通文件为例进行说明。

我们执行ls -al main

3.5、返回值

调用成功,返回0,失败返回-1,errno被设置。

3.6、测试用例

3.6.1、stat和lstat函数

事先在当前路径下touch一个file的普通文件,然后调用stat函数获取文件属性。


4、超级用户(root用户)和普通用户

a)、root用户对 / 下所有的文件有读写权限,而我们登陆的linux系统软件的内核代码和一些重

要文件也放在了root下,所以我们并不主张用root权限操作,这可能会修改或删除某些重要文

件导致系统崩溃。

b)、当普通用户登陆成功后,会自动进入家目录下的主目录下(/home/xxxx),    主目录路径记

录在了/etc/passwd中的用户信息中。以我的普通用户linux为例,ls /home/ 看下,结果如下:

linux  share  smb

家目录/home/存放的是主目录,每个主目录就是每个注册过的用户的合法操作目录,该普通用户在自己的主目录下拥有对所有文件操作的权利。当前普通用户是无法cd进入其它普通用户的主目录,目的是防止你修改或删除其它普通用户私有的文件(添加组ID除外)。

普通用户对/下的文件只有读权限,而没有写权限,目的是为了防止普通用户修改或删除超级用户的/目录下的文件。

在linux用户下试图进入share用户的主目录share是无法成功的,这就防止了linux这个普通用户修改或删除普通用户,同样cd smb也是不行的。但是允许普通用户cd /,只是对于普通用户来说只能读/下的文件,但是无法修改和删除/目录下文件。


5、进程与用户ID

5.1、与一个进程相关的用户ID

不管是执行vi、ls、pwd等命令,还是./a.out执行自己的可执行文件,目的都是为了运行程序,那么与一个进程相关的用户ID都有哪些呢?

1)、实际ID

•实际用户ID

•实际组ID   

2)、有效ID

•有效用户ID

•有效组ID   

3)、保存ID

•保存有效用户ID

•保存有效组ID

5.2、三种类型ID的区别

5.2.1、实际ID   

一般情况下,如果未设置添加组ID的话,用户自己跟自己一组,自己亲自担任组长,用户ID就是自己的组ID。

vi 程序运行时,当前用户是linux的话,进程的实际ID就是linux,ID是我们登录时从/etc/passwd中读出的。即便是我们现在将当前用户切换到了超级用户或其它的普通用户,但是之前运行进程的实际用户ID是不会发生改变的。

实际ID就是看,我们运行该程序的用户的实际用户ID和实际组ID,这个ID取自口令文件/etc/passwd。

5.2.2、有效ID   

1)、正常情况

有效组ID由exec函数(保存),正常在没有对可执行文件设置用户ID和设置组ID的情况下,exec保存的有效ID等于实际ID。

2)、特殊情况(设置了用户ID和设置组ID)

初始时exec函数保存有效ID等于实际ID

•如果对可执行文件设置了用户ID的话,exec函数保存的有效用户ID就被修改为可执行

文件的所属用户ID。

•如果对可执行文件设置了组ID的话,exec函数中保存的有效组ID被修改为可执行文件    的所属组ID。

•如果两个设置位都被设置了的话,exec函数保存的有效用户ID和有效组ID都会被修改。

3)、有效ID专门用于文件的权限检查。

5.2.3、保存ID

运行程序时,将exec函数中保存的有效ID保存一个副本。

5.2.4、设置ID   

设置文件的用户ID和设置文件的组ID,这在前面已经提到过,目的就是想让进程的实际ID和有效ID发生分离,因为正常情况下,有效ID就是实际ID的复制,但是设置了用户ID和组ID后,exec函数保存的有效用户ID和有效组ID就会被修改为文件的所属用户ID和所属组ID。

密码是用户信息的一部分,它存在了口令文件中,但是用户信息的重要性导致它是不可以被随意更改的,所以它是一个属于超级用户的文件,所以在一般情况下,普通用户是没有办法对其进行修改的,ls -al /etc/passwd,结果如下:

-rw-r--r--. 1 root root 2244 Jan 12  2012 /etc/passwd

看出这个文件的所属用户和所属组都是root,除root外的其它用户对该文件只能读,每个普通用户都有修改自己用户密码的权利,我们常在普通用户下用passwd这个命令进行修改,修改密码就涉及/etc/passwd文件的修改,这就需要用到设置位。

passwd这个命令是一个可执行文件,我们执行这个命令就是为了运行这个程序。实际上shell脚本的作用就是为了逻辑控制shell命令的执行,而每个shell命令其实就是一个可执行文件,每个命令运行起来就是一个程序。

执行命令ls /usr/bin/passwd  -al

-rwsr-xr-x. 1 root root 25980 Feb 22  2012 /usr/bin/passwd

这个命令(可执行文件),对于其它普通用户来说只能执行而不能读写,但是被做了用户设置位的设置,所以执行这个程序后,进程的有效用户ID被设置为了root,当它去改写/etc/passwd文件时,这个文件的所属用户ID等于进程的有效用户ID,passwd命令能修改/etc/passwd文件。


6、文件权限的检查

前面多多少少已经讲到了文件权限的检查,本小节将集中讨论文件权限的检查。

文件权限的检查实际上分为两部分:

•文件所属用户ID和所属组ID检查

•文件读写执行权限检查


7、新创建的的文件和目录的所有权

7.1、对于新创建的文件和目录所有权遵循如下规则

1)、新创建的文件(包括目录在内)所属用户ID设置为进程的有效用户ID,

2)、新文件的有效组ID按如下规则:

a)、如果该新文件所在目录的所属组ID未做设置,新文件的所属组ID为进程的有效组ID

b)、如果该新文件所在目录的所属组ID做了设置,新文件的所属组ID为该文件所在目录的所属组ID

7.2、举例

1)、例1

正常情况下vi或touch file,mkidr kk,试想file和kk的所属用户ID和所属组ID是多少呢?

2)、例2

a)、我们自己写一个程序,调用open函数创建一个新文件file,然后我们对生成a.out所属    用户改为root,并且做所属用户的设置位,新文件file的所属用户ID是什么

b)、将a.out所在目录的所属组ID修改为root,并作组设置位,那么这时新创建的文件file    的所属组ID又什么呢?

8、access函数

open函数打开已存在文件时会进行文件权限检查,用的是进程的有效用户ID和有效组ID,但是有的时候当可执行文件被做了设置为以后,往往进程的实际ID和有效ID不再相同,那么我们可能就像验证下实际ID对于文件是否具有相应的存取权限呢,access函数就能够满足这样的功能。


8.1函数原型和所需头文件

#include

int access(const char *pathname, int mode);

8.2、函数功能:按照实际ID对文件进行相应的存取权限测试

8.3、函数参数

•const char *pathname:文件路径名

•int mode:文件擦开放式说明,mode与如下选择

F_OK:测试文件是否存在

R_OK:测试文件是否允许读

W_OK:测试文件是否允许写

X_OK:测试文件是否允许执行

以上多个选项可用|组合。

8.4、函数返回值:成功返回0,失败返回-1,errno被设置

8.5、测试用例

1)、切换到root用户,然后touch file,ls file -al

-rw-r--r--. 1 root root 0 Apr 30 14:46 file

2)、回到普通用户,编译下面的程序,得到可执行文件a.out


9、umask函数

9.1函数原型和所需头文件

#include

#include

mode_t umask(mode_t mask);

9.2、函数功能:修改文件权限掩码

9.3、函数参数

•mode_t mask:新的文件权限掩码

9.4、函数返回值:此函数调用永远都会成功返回,返回的是修改前的文件权限掩码


10、chmod、fchmod函数

10.1函数原型和所需头文件

#include

int chmod(const char *path, mode_t mode);

int fchmod(int fd, mode_t mode);

10.2、函数功能:修改文件权限,一个是用文件路径操作,一个是用文件描述符操作

10.3、函数参数

•const char *path:文件路径

•int fd:文件描述符

10.4、函数返回值:调用成功返回0,失败返回-1,errno被设置

10.5、测试用例:略

10.6、注意

1)、我们常见的chmod命令就是这两函数的一个调用界面

2)、chmod命令、chmod函数和fchmod函数想要被合法执行需满足下面的条件

a)、超级用户,无条件执行

b)、如果在普通用户下,进程的有效用户ID等于文件所属用户ID

比如file文件,它的权限如下

-rw-rw-r--.  1 root  linux    0 Apr 30 21:33 file

当我们在普通用户执行chmod函数或chmod命令时,因为进程的有效用户ID是普通用户,而不是root,所以在在普通用户相面执行chmod命令或函数失败,报如下错误:

[    linux@bogon xiangtan_1404]$ chmod 777 file

报错chmod: changing permissions of `file': Operation not permitted,权限受限

3)、只有超级用户有权限调用chmod命令或函数设置或修改粘住位


11、粘住位

在早期的unix系统中,如果一个可执行文件的粘住位S_ISVTX做了设置(粘住位st_mode中,前面有提到),那么这个程序第一次执行结束后,这个程序的.text会被保存在交换区,目的是为了下一次执行改程序时,得到快速执行,但是实际上现代unix已经不再需要粘住位这个技术了,我们这里就不再讲解。

12、chown,fchown,lchown函数

12.1函数原型和所需头文件

#include

int chown(const char *path, uid_t owner, gid_t group);

int fchown(int fd, uid_t owner, gid_t group);

int lchown(const char *path, uid_t owner, gid_t group);


12.2、函数功能:修改文件的所属用户ID或所属组ID。

12.3、函数参数

•const char *path:文件路径

•int fd:文件描述符

•uid_t owner:指定所属用户ID

•gid_t group指定所属组ID

12.4、函数返回值:调用成功返回0,失败返回-1,errno被设置。

12.5、测试用例:略

12.6、注意

1)、我们的chown命令是这两个函数的调用界面。

2)、chown用文件路径操作,fchown功能和chown一样,但是它使用文件描述符操作。lchown也利用文件路径操作,不过它可以操作符号连接文件,chown和fchown却不能。

3)、只有超级用户才能执行chown命令或函数


————————————————

版权声明:本文为CSDN博主「JiandaoStudio」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/weixin_41213648/article/details/88049688

你可能感兴趣的:(嵌入式学习和发展(六):Linux_Api文件属性)