在 C 中使用 ioctl() 设置不可变标志

Posted

技术标签:

【中文标题】在 C 中使用 ioctl() 设置不可变标志【英文标题】:Setting Immutable Flag using ioctl() in C 【发布时间】:2015-09-08 19:30:05 【问题描述】:

我试图制作一个脚本来创建一个文件,然后将其设置为不可变的,类似于 linux 的chattr +i 命令。 脚本编译(使用 gcc),运行并创建文件。但是,文件本身不是不可变的,可以通过简单的rm -f 删除。我试图在调用chattr 的位置进行堆栈跟踪,并找到了一个名为ioctl 的函数。然后,我使用了我能收集到的少量信息,并想出了下面的内容。我从ext2_fs.h 缩小了范围,但它似乎不起作用。我显然忽略了一些事情。

更新之前的条目编译,但 在 ioctl() 函数上返回 -1错误地址perror() 一起显示。

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/fs.h>

int main()

    FILE *fp;
    char shovel[16] = "I have a shovel!";
    fp = fopen("/shovel.txt", "w+");
    fwrite(shovel, sizeof(shovel[0]), sizeof(shovel)/sizeof(shovel[0]), fp);
    ioctl(fileno(fp), FS_IOC_SETFLAGS, 0x00000010);
    fclose(fp);

任何帮助表示赞赏。

【问题讨论】:

您为什么认为ioctl 会成功?测试返回值呢? 您是否以 root 身份运行? 是的,以root身份运行,返回值仍然是-1 如果你必须先编译它才能运行它,那么它不是一个“脚本”。 您是否考虑过直接从命令行使用chattr 命令来验证您正在尝试执行的操作是否可以正常工作?并非每种文件系统类型都支持扩展属性。 【参考方案1】:

您使用了正确的 ioctl 命令,但您传递的参数错误。

ioctl_list(2) 的联机帮助页显示 FS_IOC_SETFLAGS 期望接收指向 intint *)的指针,但您传递给它的是一个整数文字(因此 Bad Address 错误)。

您不进行任何错误检查的事实也无济于事。

传递给FS_IOC_SETFLAGS 的正确标志是一个指针,该指针保存值EXT2_IMMUTABLE_FL,它在ext2fs/ext2_fs.h 中定义(一些较旧/不同的Linux 发行版似乎在linux/ext2_fs.h 下有它),所以你会需要#include &lt;ext2fs/etx2_fs.h&gt;。确保安装e2fslibs-dev(可能你也需要linux-headers)。

此代码正在运行:

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
#include <ext2fs/ext2_fs.h>

int main()

    FILE *fp;
    char shovel[16] = "I have a shovel!";

    if ((fp = fopen("shovel.txt", "w+")) == NULL) 
        perror("fopen(3) error");
        exit(EXIT_FAILURE);
    

    fwrite(shovel, sizeof(shovel[0]), sizeof(shovel)/sizeof(shovel[0]), fp);

    int val = EXT2_IMMUTABLE_FL;
    if (ioctl(fileno(fp), FS_IOC_SETFLAGS, &val) < 0)
        perror("ioctl(2) error");

    fclose(fp);

    return 0;

记得以 root 身份运行。

更新

正如Giuseppe Guerrini 在his answer 中所建议的那样,您可能希望使用FS_IMMUTABLE_FL,而无需包含ext2_fs.h

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/fs.h>

int main()

    FILE *fp;
    char shovel[16] = "I have a shovel!";

    if ((fp = fopen("shovel.txt", "w+")) == NULL) 
        perror("fopen(3) error");
        exit(EXIT_FAILURE);
    

    fwrite(shovel, sizeof(shovel[0]), sizeof(shovel)/sizeof(shovel[0]), fp);

    int val = FS_IMMUTABLE_FL;
    if (ioctl(fileno(fp), FS_IOC_SETFLAGS, &val) < 0)
        perror("ioctl(2) error");

    fclose(fp);

    return 0;

【讨论】:

欣赏代码库。示例中没有错误处理,因为它是我知道事先工作的更大代码库的一部分。我只是缩短了它,使它更容易理解我想要完成的事情,而不会用不相关的代码淹没这个主题,这会浪费人们的时间。到目前为止,它已集成并完美运行。非常感谢。【参考方案2】:

主要问题是ioctl 想要一个指向掩码的指针,而不是直接常量。您必须定义一个 int 变量,将掩码 (0x10) 存储在其中,并将其地址作为 ioctl 的第三个参数传递。

另外,我会添加一些提示:

其他更改属性的程序用于直接使用低级 I/O(打开、关闭...)。此外,该文件通常使用O_RDONLY 打开。 使用FS_IMMUTABLE_FL 代替原始常量。 首先获取当前的属性掩码 (FS_IOC_SETFLAGS) 并使用新标志对其进行掩码,这样服务就不会丢失其他设置。

【讨论】:

有趣。我的印象是EXT2_IMMUTABLE_FL 是正确的标志,但FS_IMMUTABLE_FL 似乎也是正确的,并且具有不需要包含ext2_fs.h 的良好副作用(如果你不这样做可能会很痛苦安装正确的软件包)。不错的答案! 成功了。我实际上将其追溯到 FS_IMMUTABLE_FL 并看到它被定义为 0x10。如果我更彻底地检查了联机帮助页,我会注意到这一点并且不会陷入这种困境。今晚终于可以不用挠头睡觉了。

以上是关于在 C 中使用 ioctl() 设置不可变标志的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Mac 插件中使用 ioctl() 设置 RTS?

文件IO详解(十七)---ioctl函数详解

C#/.NET 中不可重置的“标志”线程是不是安全?

IOCTL返回No such device

ioctl() 用于 C 中的套接字编程

在 AIX 上使用 ioctl 的网络接口的硬件地址