在 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
期望接收指向 int
(int *
)的指针,但您传递给它的是一个整数文字(因此 Bad Address 错误)。
您不进行任何错误检查的事实也无济于事。
传递给FS_IOC_SETFLAGS
的正确标志是一个指针,该指针保存值EXT2_IMMUTABLE_FL
,它在ext2fs/ext2_fs.h
中定义(一些较旧/不同的Linux 发行版似乎在linux/ext2_fs.h
下有它),所以你会需要#include <ext2fs/etx2_fs.h>
。确保安装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() 设置不可变标志的主要内容,如果未能解决你的问题,请参考以下文章