嵌入式Linux系统编程 — 3.4 access、chmod和 umask函数修改文件访问权限

news2024/11/15 9:19:19

目录

1 文件访问权限

1.1 文件权限基本概念

1.2 普通权限

1.3 特殊权限

2 目录权限

3 access函数检查文件权限

3.1 access函数简介

3.2 示例程序

3.3 chmod修改文件权限

3.4 fchmod函数

4 umask 函数

4.1 umask简介

4.2 示例程序


1 文件访问权限

1.1 文件权限基本概念

struct stat 结构体中的 st_mode 字段记录了文件的访问权限位。此时的文件并不仅仅指的是普通文件;所有文件类型文件(目录、设备文件)都有访问权限。

文件的权限可以分为两个大类,分别是普通权限特殊权限(也可称为附加权限)。普通权限包括对文件的读、写以及执行,而特殊权限则包括一些对文件的附加权限,譬如Set-User-ID、Set-Group-ID以及Sticky。

1.2 普通权限

每个文件都有 9 个普通的访问权限位,可将它们分为 3 类,如下表:

st_mode 权限表示宏含义
S_IRUSR
S_IWUSR
S_IXUSR
文件所有者读权限
文件所有者写权限
文件所有者执行权限
S_IRGRP
S_IWGRP
S_IXGRP
同组用户读权限
同组用户写权限
同组用户执行权限
S_IROTH
S_IWOTH
S_IXOTH
其它用户读权限
其它用户写权限
其它用户执行权限

可以 ls 命令或 stat 命令可以查看到文件的这 9 个访问权限,如下所示:

上面的红线框中, 描述了该文件的 9 个访问权限以及文件类型,例如"-rwxrwxrx":

最前面的一个字符表示该文件的类型,这个前面给大家介绍过, " - "表示该文件是一个普通文件。

  • r 表示具有读权限;
  • w 表示具有写权限;
  • x 表示具有执行权限;
  • -表示无此权限。

当进程每次对文件进行读、写、执行等操作时,内核就会对文件进行访问权限检查,以确定该进程对文件是否拥有相应的权限。而文件的权限检查就涉及到了文件的所有者(st_uid)、文件所属组(st_gid)以及其它用户,当然这里指的是从文件的角度来看;而对于进程来说,参与文件权限检查的是进程的有效用户、有效用户组以及进程的附属组用户。

如何判断权限,首先判断需要进行操作的文件的所有者:

  • 如果进程的有效用户 ID 等于文件所有者 ID(st_uid),意味着该进程以文件所有者的角色存在;
  • 如果进程的有效用户 ID 并不等于文件所有者 ID,意味着该进程并不是文件所有者身份;但是进程的有效用户组 ID 或进程的附属组 ID 之一等于文件的组 ID(st_gid),那么意味着该进程以文件所属组成员的角色存在,也就是文件所属组的同组用户成员。
  • 如果进程的有效用户 ID 不等于文件所有者 ID、并且进程的有效用户组 ID 或进程的所有附属组 ID均不等于文件的组 ID(st_gid) ,那么意味着该进程以其它用户的角色存在。
  • 如果进程的有效用户 ID 等于 0(root 用户),则无需进行权限检查,直接对该文件拥有最高权限。

1.3 特殊权限

st_mode 字段中除了记录文件的 9 个普通权限之外,还记录了文件的 3 个特殊权限,也就是图中所表示的 S 字段权限位。

S 字段三个 bit 位中,从高位到低位依次表示文件的 set-user-ID 位权限、 set-groupID 位权限以及 sticky 位权限,如下所示:

特殊权限含义
S_ISUIDset-user-ID 位权限
S_ISGIDset-group-ID 位权限
S_ISVTXSticky 位权限

这三种权限分别使用 S_ISUID、 S_ISGID 和 S_ISVTX 三个宏来表示:

S_ISUID 04000 set-user-ID bit
S_ISGID 02000 set-group-ID bit (see below)
S_ISVTX 01000 sticky bit (see below)

同样,以上数字使用的是八进制方式表示。 对应的 bit 位数字为 1,则表示设置了该权限、为 0 则表示并未设置该权限。三个权限位的作用如下:

  • 当进程对文件进行操作的时候、将进行权限检查,如果文件的 set-user-ID 位权限被设置,内核会将进程的有效 ID 设置为该文件的用户 ID(文件所有者 ID) ,意味着该进程直接获取了文件所有者的权限、以文件所有者的身份操作该文件。
  • 当进程对文件进行操作的时候、将进行权限检查,如果文件的 set-group-ID 位权限被设置,内核会将进程的有效用户组 ID 设置为该文件的用户组 ID(文件所属组 ID) ,意味着该进程直接获取了文件所属组成员的权限、以文件所属组成员的身份操作该文件。

Linux 系统下绝大部分的文件都没有设置 set-user-ID 位权限和 set-group-ID 位权限,所以通常情况下,进程的有效用户等于实际用户(有效用户 ID 等于实际用户 ID),有效组等于实际组(有效组 ID 等于实际组 ID)。

可以通过 st_mode 变量判断文件是否设置了 set-user-ID 位权限,代码如下:

if (st.st_mode & S_ISUID) {
    //设置了 set-user-ID 位权限
} else {
    //没有设置 set-user-ID 位权限
}

2 目录权限

删除文件、创建文件这些操作也是需要相应权限的,这些权限通过目录获取。目录(文件夹)在 Linux 系统下也是一种文件,拥有与普通文件相同的权限方案(S/U/G/O) ,只是这些权限的含义与文件权限不同。

  • 目录的读权限: 可列出(譬如:通过 ls 命令) 目录之下的内容(即目录下有哪些文件)。
  • 目录的写权限: 可以在目录下创建文件、删除文件。
  • 目录的执行权限: 可访问目录下的文件,譬如对目录下的文件进行读、写、执行等操作。

可以使用 ls 命令查看目录中的文件权限:

要想访问目录下的文件,譬如查看文件的 inode 节点、大小、权限等信息,还需要对目录拥有执行权限。反之,若拥有对目录的执行权限、而无读权限,只要知道目录内文件的名称,仍可对其进行访问,但不能列出目录下的内容(即目录下包含的其它文件的名称)。

要想在目录下创建文件或删除原有文件,需要同时拥有对该目录的执行和写权限。

由此可知,如果需要对文件进行读、写或执行等操作,不光是需要拥有该文件本身的读、写或执行权限,还需要拥有文件所在目录的执行权限。

3 access函数检查文件权限

3.1 access函数简介

文件的权限检查不仅讨论文件本身的权限,还需要涉及到文件所在目录的权限, 只有同时都满足了,才能通过操作系统的权限检查,进而才可以对文件进行相关操作;所以,程序当中对文件进行相关操作之前,需要先检查执行进程的用户是否对该文件拥有相应的权限。可以使用 access 系统调用检查文件权限,函数原型如下:

#include <unistd.h>

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

pathname: 需要进行权限检查的文件路径。
mode: 该参数除了可以单独使用之外,还可以通过按位或运算符" | "组合在一起,可以取以下值:

  • F_OK:检查文件是否存在
  • R_OK:检查是否拥有读权限
  • W_OK:检查是否拥有写权限
  • X_OK:检查是否拥有执行权限

返回值: 检查项通过则返回 0,表示拥有相应的权限并且文件存在;否则返回-1,如果多个检查项组合在一起,只要其中任何一项不通过都会返回-1。

3.2 示例程序

下面的程序是一个简单的命令行工具,其功能是检查用户指定的文件是否存在以及是否具备读、写和执行的权限。

#include <stdio.h>
#include <unistd.h>
#include <errno.h>

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
        return 1;
    }

    const char *filename = argv[1];

    // 检查文件是否存在
    if (access(filename, F_OK) == -1) {
        perror("File does not exist");
        return 1;
    }

    // 检查文件的读权限
    if (access(filename, R_OK) == -1) {
        fprintf(stderr, "File is not readable.\n");
    }

    // 检查文件的写权限
    if (access(filename, W_OK) == -1) {
        fprintf(stderr, "File is not writable.\n");
    }

    // 检查文件的执行权限
    if (access(filename, X_OK) == -1) {
        fprintf(stderr, "File is not executable.\n");
    }

    // 如果没有错误发生,说明文件具有所有检查的权限
    if (errno == 0) {
        printf("File has all the required permissions.\n");
    }

    return 0;
}

程序首先检查命令行参数的数量是否正确,即程序名称后是否跟有一个文件名。如果参数数量不正确,程序将打印使用说明并退出。如果参数数量正确,程序将使用 access 函数来检查文件的存在性(F_OK)、读权限(R_OK)、写权限(W_OK)和执行权限(X_OK)。如果文件不存在或缺少相应的权限,程序将打印相应的错误信息。如果所有检查都通过,即没有触发任何错误,程序将打印一条消息表明文件具有所有检查的权限,然后正常退出。程序运行结果如下:

3.3 chmod修改文件权限

在 Linux 系统下,可以使用 chmod 命令修改文件权限,该命令内部实现方法其实是调用了 chmod 函数。chmod 函数是一个用于改变文件或目录权限的系统调用。它允许用户设置文件的读(r)、写(w)和执行(x)权限,以及特殊权限,如设置用户ID(setuid,简称suid)和粘滞位(sticky bit)。函数原型如下:

#include <sys/stat.h> 

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

path:指定要改变权限的文件或目录的路径。

mode:指定新的权限模式,与 open 函数的第三个参数一样。权限模式可以通过运算符" | "组合:

  • S_IRWXU:为文件所有者设置读、写和执行权限。
  • S_IRUSR:为文件所有者设置读权限。
  • S_IWUSR:为文件所有者设置写权限。
  • S_IXUSR:为文件所有者设置执行权限。
  • S_IRWXG:为文件所属组设置读、写和执行权限。
  • S_IRGRP:为文件所属组设置读权限。
  • S_IWGRP:为文件所属组设置写权限。
  • S_IXGRP:为文件所属组设置执行权限。
  • S_IRWXO:为其他用户设置读、写和执行权限。
  • S_IROTH:为其他用户设置读权限。
  • S_IWOTH:为其他用户设置写权限。
  • S_IXOTH:为其他用户设置执行权限。
  • S_ISUID:设置文件的setuid位。
  • S_ISGID:设置文件的setgid位。
  • S_ISVTX:设置文件的粘滞位。

下面是使用 chmod 函数的一个示例,其中 chmod 函数传入一个文件名参数,并尝试为该文件设置特定的权限模式。在这个例子中,我们将为文件设置文件所有者的读、写和执行权限。

#include <stdio.h>
#include <sys/stat.h>
#include <errno.h>

int main(int argc, char *argv[]) {
    // 检查是否传入了文件名作为参数
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
        return 1;
    }

    const char *filename = argv[1]; // 从参数中获取文件名
    mode_t mode = S_IRUSR | S_IWUSR | S_IXUSR; // 设置文件所有者的读、写和执行权限

    // 使用chmod函数改变文件权限
    if (chmod(filename, mode) == -1) {
        // 如果chmod函数调用失败,打印错误消息
        perror("Failed to change file mode");
        return 1;
    }

    // 如果权限改变成功,打印成功消息
    printf("File mode for '%s' changed successfully.\n", filename);
    return 0;
}

程序同样先检查是否传入了正确的参数数量,即程序名称后跟一个文件名。如果参数数量正确,程序将尝试使用 chmod 函数改变 argv[1] 指定的文件的权限。在这个例子中,设置的权限模式是 S_IRUSR | S_IWUSR | S_IXUSR,这意味着我们将为文件所有者设置读、写和执行权限。如果 chmod 调用失败,perror 将打印错误消息,程序返回状态码1。如果权限改变成功,程序将打印一条成功消息。程序运行结果如下:

3.4 fchmod函数

该函数功能与 chmod 一样,参数略有不同。 fchmod()与 chmod()的区别在于使用了文件描述符来代替文件路径,就像是 fstat 与 stat 的区别。函数原型如下所示:

#include <sys/stat.h>

int fchmod(int fd, mode_t mode);

使用了文件描述符 fd 代替了文件路径 pathname,其它功能都是一样的。
 

4 umask 函数

4.1 umask简介

umask(用户文件创建掩码,User File Creation Mask)用于决定新创建的文件和目录的默认权限,umask 定义了文件系统创建文件或目录时默认应该屏蔽掉的权限位。

前面提到,文件和目录的权限可以用三位八进制数来表示,分别对应所有者(user)、所属组(group)和其他用户(others)的读(r)、写(w)和执行(x)权限。例如,一个文件的权限 755 表示:

  • 所有者有读、写和执行权限(7)
  • 所属组有读和执行权限(5)
  • 其他用户有读和执行权限(5)

umask 值定义了这些权限位中哪些应该被设置为“关闭”(即不允许)。umask 的值也是用八进制数表示的,但它的含义是相反的:umask 中的每个位表示应该从默认权限中减去相应的权限。例如,如果 umask 设置为 022

  • 对于文件,这将从默认权限 666(可读可写)中减去 022,结果为 644(可读可写,但不可执行)。
  • 对于目录,这将从默认权限 777(可读可写可执行)中减去 022,结果为 755(可读可写可执行,但组和其他用户不可写入)。

umask 的默认值通常是 022,这意味着新创建的文件默认没有执行权限,新创建的目录默认不允许组和其他用户写入。

在Linux系统中,umask(用户文件创建掩码)函数用于设置或获取当前进程的文件模式创建掩码。这个掩码决定了新创建的文件和目录的默认权限。函数原型:

#include <sys/stat.h> 

mode_t umask(mode_t mask);

mask:一个 mode_t 类型的值,用来设置新的umask值。这个值通常由 S_IRWXU(用户读、写、执行)、S_IRWXG(组读、写、执行)和 S_IRWXO(其他用户读、写、执行)的组合来表示,但是用它们的补码来表示。例如,如果你想设置文件默认没有写权限,目录默认没有写和执行权限,你可以使用 022 作为掩码值。

4.2 示例程序

代码演示了如何使用 umask 函数来获取和修改进程的文件创建掩码。

#include <stdio.h>
#include <sys/stat.h>

int main() {
    // 获取当前umask值
    mode_t current_umask = umask(0); // 设置umask为0,获取当前值

    // 打印当前umask值
    printf("Current umask: %04o\n", current_umask);

    // 设置新的umask值,这里设置为022,即组和其他用户默认没有写权限
    mode_t new_umask = umask(S_IRWXG | S_IRWXO);

    // 创建一个文件,将应用新的umask值
    FILE *file = fopen("testfile.txt", "w");
    if (file == NULL) {
        perror("Failed to open file");
        return 1;
    }
    fputs("Hello, World!\n", file);
    fclose(file);

    // 打印新的umask值
    printf("New umask: %04o\n", new_umask);

    // 可以恢复原来的umask值
    umask(current_umask);

    return 0;
}

程序首先通过调用 umask(0) 获取当前的umask值,并打印出来。然后,它将umask设置为 022,这意味着新创建的文件将默认不授予组和其他用户写权限。在这个umask值下,程序创建并写入一个名为 testfile.txt 的文件。之后,程序打印出新的umask值,并最终通过再次调用 umask 用之前的值恢复原始的umask设置。程序运行结果如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1808296.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

奇安信停服,国内还有什么可用的高防么?

这里写自定义目录标题 背景DDOS怎么办&#xff1f;方案推荐总结 背景 继前段时间百度云加速通知免费服务&#xff0c;6月底奇安信也将停止服务&#xff0c;到时候国内将几乎不存在免费好用的高防CDN了&#xff1b;类似的事情还有阿里云和腾讯云的一年期免费SSl证书也都停止供应…

2024 AEE | 风丘科技将亮相日本爱知国际会展中心——共同创造!

2024年名古屋汽车工程博览会&#xff08;Automotive Engineering Exposition 2024 NAGOYA&#xff09;将于7月17-19日在日本爱知县国际展示场&#xff08;Aichi Sky Expo&#xff09;开展。本展会是专门为活跃在汽车行业的工程师和研究人员举办的汽车技术展览&#xff0c;汇聚了…

vmware-17虚拟机安装教程,安装linux centos系统

下载VMware 1.进入VMware官网&#xff1a;https://www.vmware.com/sg/products/workstation-pro.html 2.向下翻找到&#xff0c;如下界面并点击“现在安装” 因官网更新页面出现误差&#xff0c;现提供vmware17安装包网盘链接如下&#xff1a; 链接&#xff1a;https://pan.b…

细说NLP中的Embedding层

文章目录 前言一、为什么要引入Embedding层二、Embedding层是怎么发挥作用的&#xff1f;三、感受Embedding的强大四、为什么理解Embedding的底层原理&#xff1f;总结 前言 在构建高效的自然语言处理模型时&#xff0c;Embedding层是不可或缺的组成部分。它不仅可以帮助我们捕…

基于JSP的健身俱乐部会员管理系统的设计与实现

【免费】基于JSP的健身俱乐部会员管理系统.zip资源-CSDN文库https://download.csdn.net/download/JW_559/89416957 基于JSP的健身俱乐部会员管理系统的设计与实现 摘 要 目前我国虽然己经开发出了应用计算机操作的健身俱乐部管理系统&#xff0c;但管理软件&#xff0c;管理方…

Flutter 实现dispose探测控件

文章目录 前言一、什么是dispose探测控件&#xff1f;1、通常情况2、使用dispose探测控件 二、如何实现1、继承StatefulWidget2、定义dipose回调3、定义child4、重载Dispose方法5、build child 三、完整代码四、使用示例1、基本用法2、设置定义数据 总结 前言 开发flutter一般…

LabVIEW调用DLL时需注意的问题

在LabVIEW中调用DLL&#xff08;动态链接库&#xff09;是实现与外部代码集成的一种强大方式&#xff0c;但也存在一些常见的陷阱和复杂性。本文将从参数传递、数据类型匹配、内存管理、线程安全、调试和错误处理等多个角度详细介绍LabVIEW调用DLL时需要注意的问题&#xff0c;…

第1回 最开始的两行代码

当你按下开机键的那一刻,在主板上提前写死的固件程序BIOS会将硬盘启动区中的512(B)的数据,原封不动地复制到内存中的0x7c00这个位置,并跳转到那个位置: 下面我们针对每一步做详细介绍. 开机后初始化指向BIOS CPU中有一个PC寄存器,里面存储这将要执行的指令在内存中的地…

推测性解码:加速多模态大型语言模型的推理

大模型&#xff08;LLMs&#xff09;以其卓越的性能在多个应用场景中大放异彩。然而&#xff0c;随着应用的深入&#xff0c;这些模型的推理速度问题逐渐凸显。为了解决这一挑战&#xff0c;推测性解码&#xff08;Speculative Decoding, SPD&#xff09;技术应运而生。本文深入…

ROS geometry_msgs和sensor_msgs

ROS geometry_msgs和sensor_msgs geometry_msgs geometry_msgs 是 ROS&#xff08;Robot Operating System&#xff09;中用于几何计算和空间表示的一个核心消息包。它定义了一系列消息类型&#xff0c;用于表示位置、方向、速度等几何概念。以下是一些 geometry_msgs 中定义…

Tessy学习系列(三):单元测试——官方例程isValueInRange

一、工程创建 &#xff08;1&#xff09;新建工程 注意&#xff1a;工程名称以及路劲不能包含空格和中文 &#xff08;2&#xff09;新建测试集与单元测试模块 新建测试集 新建单元测试模块 设置测试模块为单元测试模块并选择GNU GCC编译器如果需要其他的编译器&#xff0c;…

SSM物流管理系统的设计与实现-计算机毕业设计源码44323

摘 要 科技进步的飞速发展引起人们日常生活的巨大变化&#xff0c;电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用。信息时代的到来已成为不可阻挡的时尚潮流&#xff0c;人类发展的历史正进入一个新时代。在现实运用中&#xff0c;应用软件的工作…

vue-router全部搞定(附源码)

源码下载链接&#xff08;先转存&#xff0c;后下载&#xff09;&#xff1a;https://pan.quark.cn/s/b0c6edd68c21 怎么用vue-cli搭建项目 我们固然可以用传统htmljs的方式来搭建vue项目&#xff0c;但是如果组件很多&#xff0c;就需要通过Vue.component的方式一个个去引入…

flask实现抽奖程序(一)

后端代码E:\LearningProject\lottery\app.py from flask import Flask, render_template import randomapp Flask(__name__)employees [赵一, 钱二, 孙三, 李四, 周五, 吴六, 郑七, 王八]app.route(/) def hello_world():return render_template(index.html, employeesemplo…

14.《C语言》——【牛客网BC116—BC123题目讲解】

亲爱的读者&#xff0c;大家好&#xff01;我是一名正在学习编程的高校生。在这个博客里&#xff0c;我将和大家一起探讨编程技巧、分享实用工具&#xff0c;并交流学习心得。希望通过我的博客&#xff0c;你能学到有用的知识&#xff0c;提高自己的技能&#xff0c;成为一名优…

【语音告警】Zabbix语音播报-报警媒介部分配置-语音报警灯|声光报警器|网络信号灯

阅读说明 本文为博灵语音通知终端与Zabbix报警媒介的配置&#xff0c;对接完成后可以实现Zabbix的声光语音告警&#xff0c;播报效果可以参考 Modbus-博灵语音通知终端与PLC联动告警介绍 对接前需配置好通知终端的IP地址&#xff0c;设备参数参见 其他完整的Zabbix语音播报报…

重邮计算机网络803-(2)物理层

一.物理层 1.介绍 物理层的主要任务描述为确定与传输媒体的接口的一些特性&#xff0c;即&#xff1a; ①机械特性 指明接口所用接线器的形状和尺寸、引线数目和排列、固定和锁定装置等等。 ②电气特性 指明在接口电缆的各条线上出现的电压的范围。 ③功能特性 指明某条线上…

Long-Context LLM综述

目录 前言1. Preliminary2. 方法分类3. 长度外推3.1 位置外推和插值位置外推插值 3.2 上下文窗口分割与滑动3.3 提示压缩 4. 注意力近似4.1 低秩分解4.2 稀疏注意力4.3 Softmax-free Attention 5. Attention-free Transformers5.1 状态空间模型(State Space Model, SSM)5.2 位置…

Redis实战——创建账户及连接数据库

一、创建一个新账户 要创建一个带有免费数据库的新账户&#xff0c;请按照以下步骤操作&#xff1a; 前往 Redis Cloud 的注册页面。有两种开始使用 Redis Cloud 的选项&#xff1a; 在表单中输入您的信息&#xff0c;然后选择“Get Started”&#xff08;开始使用&#xff…

PR如何让音频淡入淡出

PR如何让音频淡入淡出 方法一&#xff1a;效果控件关键帧方法二&#xff1a;音频轨道关键帧 以淡入为例&#xff0c;介绍如何设置淡入的两种方法&#xff0c;推荐使用第二种。淡出效果类似。 方法一&#xff1a;效果控件关键帧 选中音频&#xff0c;点击效果控件 在淡入结束的…